diff options
688 files changed, 9049 insertions, 5142 deletions
diff --git a/Android.bp b/Android.bp index 7c9cdcfb9826..381e0466efbd 100644 --- a/Android.bp +++ b/Android.bp @@ -326,6 +326,7 @@ java_defaults { "av-types-aidl-java", "tv_tuner_resource_manager_aidl_interface-java", "soundtrigger_middleware-aidl-java", + "modules-utils-preconditions", "modules-utils-os", "framework-permission-aidl-java", ], @@ -409,6 +410,7 @@ filegroup { srcs: [ // TODO: remove these annotations as soon as we can use andoid.support.annotations.* ":framework-annotations", + ":modules-utils-preconditions-srcs", "core/java/android/net/DhcpResults.java", "core/java/android/util/IndentingPrintWriter.java", "core/java/android/util/LocalLog.java", @@ -416,7 +418,6 @@ filegroup { "core/java/com/android/internal/util/IndentingPrintWriter.java", "core/java/com/android/internal/util/IState.java", "core/java/com/android/internal/util/MessageUtils.java", - "core/java/com/android/internal/util/Preconditions.java", "core/java/com/android/internal/util/RingBufferIndices.java", "core/java/com/android/internal/util/State.java", "core/java/com/android/internal/util/StateMachine.java", @@ -446,11 +447,11 @@ java_library { sdk_version: "module_current", min_sdk_version: "30", srcs: [ + ":modules-utils-preconditions-srcs", "core/java/android/os/HandlerExecutor.java", "core/java/com/android/internal/util/AsyncChannel.java", "core/java/com/android/internal/util/AsyncService.java", "core/java/com/android/internal/util/Protocol.java", - "core/java/com/android/internal/util/Preconditions.java", "telephony/java/android/telephony/Annotation.java", ":net-utils-framework-wifi-common-srcs", ], diff --git a/apex/appsearch/Android.bp b/apex/appsearch/Android.bp index 87f65a922004..c592d1305cd1 100644 --- a/apex/appsearch/Android.bp +++ b/apex/appsearch/Android.bp @@ -24,8 +24,8 @@ package { apex { name: "com.android.appsearch", manifest: "apex_manifest.json", + bootclasspath_fragments: ["com.android.appsearch-bootclasspath-fragment"], java_libs: [ - "framework-appsearch", "service-appsearch", ], key: "com.android.appsearch.key", @@ -45,3 +45,10 @@ android_app_certificate { // com.android.appsearch.pk8 (the private key) certificate: "com.android.appsearch", } + +// Encapsulate the contributions made by the com.android.appsearch to the bootclasspath. +bootclasspath_fragment { + name: "com.android.appsearch-bootclasspath-fragment", + contents: ["framework-appsearch"], + apex_available: ["com.android.appsearch"], +} diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp index f92f44bc853f..b8fce4f10cd0 100644 --- a/apex/appsearch/framework/Android.bp +++ b/apex/appsearch/framework/Android.bp @@ -52,14 +52,18 @@ filegroup { java_sdk_library { name: "framework-appsearch", srcs: [":framework-appsearch-sources"], - sdk_version: "core_platform", // TODO(b/146218515) should be module_current - impl_only_libs: ["framework-minus-apex"], // TODO(b/146218515) should be removed + sdk_version: "module_current", + static_libs: [ + // This list must be kept in sync with jarjar.txt + "modules-utils-preconditions", + ], libs: ["unsupportedappusage"], // TODO(b/181887768) should be removed defaults: ["framework-module-defaults"], permitted_packages: ["android.app.appsearch"], aidl: { include_dirs: ["frameworks/base/core/java"], // TODO(b/146218515) should be removed }, + jarjar_rules: "jarjar-rules.txt", apex_available: ["com.android.appsearch"], unsafe_ignore_missing_latest_api: true, // TODO(b/146218515) should be removed } diff --git a/apex/appsearch/framework/jarjar-rules.txt b/apex/appsearch/framework/jarjar-rules.txt new file mode 100644 index 000000000000..50c3ee4125a8 --- /dev/null +++ b/apex/appsearch/framework/jarjar-rules.txt @@ -0,0 +1,6 @@ +# Rename all com.android.internal.util classes to prevent class name collisions +# between this module and the other versions of the utility classes linked into +# the framework. + +# These must be kept in sync with the sources of framework-utils-appsearch +rule com.android.internal.util.Preconditions* android.app.appsearch.internal.util.Preconditions@1 diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index bc3c79fea9e4..59b940a460d3 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -205,7 +205,7 @@ public class AppSearchManager { AppSearchSession.createSearchSession( searchContext, mService, - mContext.getUserId(), + mContext.getUser().getIdentifier(), getPackageName(), executor, callback); @@ -228,7 +228,7 @@ public class AppSearchManager { Objects.requireNonNull(executor); Objects.requireNonNull(callback); GlobalSearchSession.createGlobalSearchSession( - mService, mContext.getUserId(), getPackageName(), executor, callback); + mService, mContext.getUser().getIdentifier(), getPackageName(), executor, callback); } /** Returns the package name that should be used for uid verification. */ 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 f0b04fccd406..272e12db0124 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java @@ -19,6 +19,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.util.ArrayMap; +import java.util.Collections; import java.util.Map; import java.util.Objects; @@ -48,9 +49,9 @@ public final class AppSearchBatchResult<KeyType, ValueType> { @NonNull Map<KeyType, ValueType> successes, @NonNull Map<KeyType, AppSearchResult<ValueType>> failures, @NonNull Map<KeyType, AppSearchResult<ValueType>> all) { - mSuccesses = successes; - mFailures = failures; - mAll = all; + mSuccesses = Objects.requireNonNull(successes); + mFailures = Objects.requireNonNull(failures); + mAll = Objects.requireNonNull(all); } /** Returns {@code true} if this {@link AppSearchBatchResult} has no failures. */ @@ -70,7 +71,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> { */ @NonNull public Map<KeyType, ValueType> getSuccesses() { - return mSuccesses; + return Collections.unmodifiableMap(mSuccesses); } /** @@ -81,7 +82,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> { */ @NonNull public Map<KeyType, AppSearchResult<ValueType>> getFailures() { - return mFailures; + return Collections.unmodifiableMap(mFailures); } /** @@ -92,7 +93,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> { */ @NonNull public Map<KeyType, AppSearchResult<ValueType>> getAll() { - return mAll; + return Collections.unmodifiableMap(mAll); } /** @@ -128,20 +129,37 @@ public final class AppSearchBatchResult<KeyType, ValueType> { * Associates the {@code key} with the provided successful return value. * * <p>Any previous mapping for a key, whether success or failure, is deleted. + * + * <p>This is a convenience function which is equivalent to {@code setResult(key, + * AppSearchResult.newSuccessfulResult(value))}. + * + * @param key The key to associate the result with; usually corresponds to some identifier + * from the input like an ID or name. + * @param value An optional value to associate with the successful result of the operation + * being performed. */ @SuppressWarnings("MissingGetterMatchingBuilder") // See getSuccesses @NonNull public Builder<KeyType, ValueType> setSuccess( - @NonNull KeyType key, @Nullable ValueType result) { + @NonNull KeyType key, @Nullable ValueType value) { Objects.requireNonNull(key); resetIfBuilt(); - return setResult(key, AppSearchResult.newSuccessfulResult(result)); + return setResult(key, AppSearchResult.newSuccessfulResult(value)); } /** * Associates the {@code key} with the provided failure code and error message. * * <p>Any previous mapping for a key, whether success or failure, is deleted. + * + * <p>This is a convenience function which is equivalent to {@code setResult(key, + * AppSearchResult.newFailedResult(resultCode, errorMessage))}. + * + * @param key The key to associate the result with; usually corresponds to some identifier + * from the input like an ID or name. + * @param resultCode One of the constants documented in {@link + * AppSearchResult#getResultCode}. + * @param errorMessage An optional string describing the reason or nature of the failure. */ @SuppressWarnings("MissingGetterMatchingBuilder") // See getFailures @NonNull @@ -158,6 +176,10 @@ public final class AppSearchBatchResult<KeyType, ValueType> { * Associates the {@code key} with the provided {@code result}. * * <p>Any previous mapping for a key, whether success or failure, is deleted. + * + * @param key The key to associate the result with; usually corresponds to some identifier + * from the input like an ID or name. + * @param result The result to associate with the key. */ @SuppressWarnings("MissingGetterMatchingBuilder") // See getAll @NonNull @@ -183,8 +205,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> { @NonNull public AppSearchBatchResult<KeyType, ValueType> build() { mBuilt = true; - return new AppSearchBatchResult<>( - new ArrayMap<>(mSuccesses), new ArrayMap<>(mFailures), new ArrayMap<>(mAll)); + return new AppSearchBatchResult<>(mSuccesses, mFailures, mAll); } private void resetIfBuilt() { 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 30c98b0c8866..c57cf2e68993 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java @@ -175,14 +175,24 @@ public final class AppSearchResult<ValueType> { return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage; } - /** Creates a new successful {@link AppSearchResult}. */ + /** + * Creates a new successful {@link AppSearchResult}. + * + * @param value An optional value to associate with the successful result of the operation being + * performed. + */ @NonNull public static <ValueType> AppSearchResult<ValueType> newSuccessfulResult( @Nullable ValueType value) { return new AppSearchResult<>(RESULT_OK, value, /*errorMessage=*/ null); } - /** Creates a new failed {@link AppSearchResult}. */ + /** + * Creates a new failed {@link AppSearchResult}. + * + * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}. + * @param errorMessage An optional string describing the reason or nature of the failure. + */ @NonNull public static <ValueType> AppSearchResult<ValueType> newFailedResult( @ResultCode int resultCode, @Nullable String errorMessage) { diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java index e324b9f81d9c..558899e493ae 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java @@ -164,8 +164,7 @@ public final class GetByDocumentIdRequest { @NonNull public GetByDocumentIdRequest build() { mBuilt = true; - return new GetByDocumentIdRequest( - mNamespace, new ArraySet<>(mIds), new ArrayMap<>(mProjectionTypePropertyPaths)); + return new GetByDocumentIdRequest(mNamespace, mIds, mProjectionTypePropertyPaths); } private void resetIfBuilt() { diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java index b6575c2030ff..26bdf031a5b0 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java @@ -93,14 +93,25 @@ public final class ReportSystemUsageRequest { private final String mDocumentId; private Long mUsageTimestampMillis; - /** Creates a {@link ReportSystemUsageRequest.Builder} instance. */ + /** + * Creates a {@link ReportSystemUsageRequest.Builder} instance. + * + * @param packageName The package name of the app which owns the document that was used + * (e.g. from {@link SearchResult#getPackageName}). + * @param databaseName The database in which the document that was used resides (e.g. from + * {@link SearchResult#getDatabaseName}). + * @param namespace The namespace of the document that was used (e.g. from {@link + * GenericDocument#getNamespace}. + * @param documentId The ID of document that was used (e.g. from {@link + * GenericDocument#getId}. + */ public Builder( @NonNull String packageName, - @NonNull String database, + @NonNull String databaseName, @NonNull String namespace, @NonNull String documentId) { mPackageName = Objects.requireNonNull(packageName); - mDatabase = Objects.requireNonNull(database); + mDatabase = Objects.requireNonNull(databaseName); mNamespace = Objects.requireNonNull(namespace); mDocumentId = Objects.requireNonNull(documentId); } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java index 59fa6e0743f6..c388bdebb00d 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java @@ -71,7 +71,14 @@ public final class ReportUsageRequest { private String mDocumentId; private Long mUsageTimestampMillis; - /** Creates a {@link ReportUsageRequest.Builder} instance. */ + /** + * Creates a new {@link ReportUsageRequest.Builder} instance. + * + * @param namespace The namespace of the document that was used (e.g. from {@link + * GenericDocument#getNamespace}. + * @param documentId The ID of document that was used (e.g. from {@link + * GenericDocument#getId}. + */ public Builder(@NonNull String namespace, @NonNull String documentId) { mNamespace = Objects.requireNonNull(namespace); mDocumentId = Objects.requireNonNull(documentId); diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java index 2c75b71f3606..3e5a2ca246e0 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java @@ -375,5 +375,19 @@ public class SetSchemaResponse { mBundle.getInt(RESULT_CODE_FIELD), mBundle.getString(ERROR_MESSAGE_FIELD, /*defaultValue=*/ "")); } + + @NonNull + @Override + public String toString() { + return "MigrationFailure { schemaType: " + + getSchemaType() + + ", namespace: " + + getNamespace() + + ", documentId: " + + getDocumentId() + + ", appSearchResult: " + + getAppSearchResult().toString() + + "}"; + } } } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/LogUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/LogUtil.java new file mode 100644 index 000000000000..f2cc3b969a60 --- /dev/null +++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/LogUtil.java @@ -0,0 +1,104 @@ +/* + * 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; +import android.annotation.Nullable; +import android.util.Log; + +import java.util.Objects; + +/** + * Utilities for logging to logcat. + * + * @hide + */ +public final class LogUtil { + /** + * The {@link #piiTrace} logs are intended for sensitive data that can't be enabled in + * production, so they are build-gated by this constant. + * + * <p> + * + * <ul> + * <li>0: no tracing. + * <li>1: fast tracing (statuses/counts only) + * <li>2: full tracing (complete messages) + * </ul> + */ + private static final int PII_TRACE_LEVEL = 0; + + private final String mTag; + + public LogUtil(@NonNull String tag) { + mTag = Objects.requireNonNull(tag); + } + + /** Returns whether piiTrace() is enabled (PII_TRACE_LEVEL > 0). */ + public boolean isPiiTraceEnabled() { + return PII_TRACE_LEVEL > 0; + } + + /** + * If icing lib interaction tracing is enabled via {@link #PII_TRACE_LEVEL}, logs the provided + * message to logcat. + * + * <p>If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately. + */ + public void piiTrace(@NonNull String message) { + piiTrace(message, /*fastTraceObj=*/ null, /*fullTraceObj=*/ null); + } + + /** + * If icing lib interaction tracing is enabled via {@link #PII_TRACE_LEVEL}, logs the provided + * message and object to logcat. + * + * <p>If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately. + * + * <p>Otherwise, {@code traceObj} is logged if it is non-null. + */ + public void piiTrace(@NonNull String message, @Nullable Object traceObj) { + piiTrace(message, /*fastTraceObj=*/ traceObj, /*fullTraceObj=*/ null); + } + + /** + * If icing lib interaction tracing is enabled via {@link #PII_TRACE_LEVEL}, logs the provided + * message and objects to logcat. + * + * <p>If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately. + * + * <p>If {@link #PII_TRACE_LEVEL} is 1, {@code fastTraceObj} is logged if it is non-null. + * + * <p>If {@link #PII_TRACE_LEVEL} is 2, {@code fullTraceObj} is logged if it is non-null, else + * {@code fastTraceObj} is logged if it is non-null.. + */ + public void piiTrace( + @NonNull String message, @Nullable Object fastTraceObj, @Nullable Object fullTraceObj) { + if (PII_TRACE_LEVEL == 0) { + return; + } + StringBuilder builder = new StringBuilder("(trace) ").append(message); + if (PII_TRACE_LEVEL == 1 && fastTraceObj != null) { + builder.append(": ").append(fastTraceObj); + } else if (PII_TRACE_LEVEL == 2 && fullTraceObj != null) { + builder.append(": ").append(fullTraceObj); + } else if (PII_TRACE_LEVEL == 2 && fastTraceObj != null) { + builder.append(": ").append(fastTraceObj); + } + Log.i(mTag, builder.toString()); + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java index 0f643c513888..b7e21591a86c 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java @@ -36,6 +36,10 @@ import android.util.ArraySet; import android.util.Log; import com.android.server.appsearch.external.localstorage.util.PrefixUtil; +import com.android.server.appsearch.visibilitystore.NotPlatformSurfaceableMap; +import com.android.server.appsearch.visibilitystore.PackageAccessibleDocument; +import com.android.server.appsearch.visibilitystore.PackageAccessibleMap; +import com.android.server.appsearch.visibilitystore.VisibilityDocument; import com.google.android.icing.proto.PersistType; @@ -74,74 +78,10 @@ public class VisibilityStore { /** No-op user id that won't have any visibility settings. */ public static final int NO_OP_USER_ID = -1; - /** Schema type for documents that hold AppSearch's metadata, e.g. visibility settings */ - private static final String VISIBILITY_TYPE = "VisibilityType"; - /** Version for the visibility schema */ private static final int SCHEMA_VERSION = 0; /** - * Property that holds the list of platform-hidden schemas, as part of the visibility settings. - */ - private static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable"; - - /** Property that holds nested documents of package accessible schemas. */ - private static final String PACKAGE_ACCESSIBLE_PROPERTY = "packageAccessible"; - - /** Schema type for nested documents that hold package accessible information. */ - private static final String PACKAGE_ACCESSIBLE_TYPE = "PackageAccessibleType"; - - /** Property that holds the package name that can access a schema. */ - private static final String PACKAGE_NAME_PROPERTY = "packageName"; - - /** Property that holds the SHA 256 certificate of the app that can access a schema. */ - private static final String SHA_256_CERT_PROPERTY = "sha256Cert"; - - /** Property that holds the prefixed schema type that is accessible by some package. */ - private static final String ACCESSIBLE_SCHEMA_PROPERTY = "accessibleSchema"; - - /** Schema for the VisibilityStore's documents. */ - private static final AppSearchSchema VISIBILITY_SCHEMA = - new AppSearchSchema.Builder(VISIBILITY_TYPE) - .addProperty( - new AppSearchSchema.StringPropertyConfig.Builder( - NOT_PLATFORM_SURFACEABLE_PROPERTY) - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) - .build()) - .addProperty( - new AppSearchSchema.DocumentPropertyConfig.Builder( - PACKAGE_ACCESSIBLE_PROPERTY, PACKAGE_ACCESSIBLE_TYPE) - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) - .build()) - .build(); - - /** - * Schema for package accessible documents, these will be nested in a top-level visibility - * document. - */ - private static final AppSearchSchema PACKAGE_ACCESSIBLE_SCHEMA = - new AppSearchSchema.Builder(PACKAGE_ACCESSIBLE_TYPE) - .addProperty( - new AppSearchSchema.StringPropertyConfig.Builder(PACKAGE_NAME_PROPERTY) - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .build()) - .addProperty( - new AppSearchSchema.BytesPropertyConfig.Builder(SHA_256_CERT_PROPERTY) - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .build()) - .addProperty( - new AppSearchSchema.StringPropertyConfig.Builder( - ACCESSIBLE_SCHEMA_PROPERTY) - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .build()) - .build(); - - /** * These cannot have any of the special characters used by AppSearchImpl (e.g. {@code * AppSearchImpl#PACKAGE_DELIMITER} or {@code AppSearchImpl#DATABASE_DELIMITER}. */ @@ -177,24 +117,12 @@ public class VisibilityStore { // platform-surfaceable content. private int mGlobalQuerierUid; - /** - * Maps prefixes to the set of schemas that are platform-hidden within that prefix. All schemas - * in the map are prefixed. - * - * <p>Although the prefix key isn't used for lookup, it's helpful in ensuring that all previous - * visibility settings for a prefix are completely overridden by new visibility settings. - */ - private final Map<String, Set<String>> mNotPlatformSurfaceableMap = new ArrayMap<>(); + /** Stores the schemas that are platform-hidden. All values are prefixed. */ + private final NotPlatformSurfaceableMap mNotPlatformSurfaceableMap = + new NotPlatformSurfaceableMap(); - /** - * Maps prefixes to a an internal map. The internal map maps prefixed schemas to the set of - * PackageIdentifiers that have access to that schema. - * - * <p>Although the prefix key isn't used for lookup, it's helpful in ensuring that all previous - * visibility settings for a prefix are completely overridden by new visibility settings. - */ - private final Map<String, Map<String, Set<PackageIdentifier>>> mPackageAccessibleMap = - new ArrayMap<>(); + /** Stores the schemas that are package accessible. All values are prefixed. */ + private final PackageAccessibleMap mPackageAccessibleMap = new PackageAccessibleMap(); /** * Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()} @@ -228,9 +156,9 @@ public class VisibilityStore { boolean hasVisibilityType = false; boolean hasPackageAccessibleType = false; for (AppSearchSchema schema : getSchemaResponse.getSchemas()) { - if (schema.getSchemaType().equals(VISIBILITY_TYPE)) { + if (schema.getSchemaType().equals(VisibilityDocument.SCHEMA_TYPE)) { hasVisibilityType = true; - } else if (schema.getSchemaType().equals(PACKAGE_ACCESSIBLE_TYPE)) { + } else if (schema.getSchemaType().equals(PackageAccessibleDocument.SCHEMA_TYPE)) { hasPackageAccessibleType = true; } @@ -244,7 +172,7 @@ public class VisibilityStore { mAppSearchImpl.setSchema( PACKAGE_NAME, DATABASE_NAME, - Arrays.asList(VISIBILITY_SCHEMA, PACKAGE_ACCESSIBLE_SCHEMA), + Arrays.asList(VisibilityDocument.SCHEMA, PackageAccessibleDocument.SCHEMA), /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -261,40 +189,34 @@ public class VisibilityStore { try { // Note: We use the other clients' prefixed names as ids - GenericDocument document = + VisibilityDocument visibilityDocument = new VisibilityDocument( mAppSearchImpl.getDocument( PACKAGE_NAME, DATABASE_NAME, NAMESPACE, /*id=*/ addIdPrefix(prefix), - /*typePropertyPaths=*/ Collections.emptyMap()); + /*typePropertyPaths=*/ Collections.emptyMap())); // Update platform visibility settings String[] notPlatformSurfaceableSchemas = - document.getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY); + visibilityDocument.getNotPlatformSurfaceableSchemas(); if (notPlatformSurfaceableSchemas != null) { - mNotPlatformSurfaceableMap.put( - prefix, new ArraySet<>(Arrays.asList(notPlatformSurfaceableSchemas))); + mNotPlatformSurfaceableMap.setNotPlatformSurfaceable( + prefix, + new ArraySet<>(notPlatformSurfaceableSchemas)); } // Update 3p package visibility settings Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>(); GenericDocument[] packageAccessibleDocuments = - document.getPropertyDocumentArray(PACKAGE_ACCESSIBLE_PROPERTY); + visibilityDocument.getPackageAccessibleSchemas(); if (packageAccessibleDocuments != null) { for (int i = 0; i < packageAccessibleDocuments.length; i++) { - String packageName = - packageAccessibleDocuments[i].getPropertyString( - PACKAGE_NAME_PROPERTY); - byte[] sha256Cert = - packageAccessibleDocuments[i].getPropertyBytes( - SHA_256_CERT_PROPERTY); + PackageAccessibleDocument packageAccessibleDocument = + new PackageAccessibleDocument(packageAccessibleDocuments[i]); PackageIdentifier packageIdentifier = - new PackageIdentifier(packageName, sha256Cert); - - String prefixedSchema = - packageAccessibleDocuments[i].getPropertyString( - ACCESSIBLE_SCHEMA_PROPERTY); + packageAccessibleDocument.getPackageIdentifier(); + String prefixedSchema = packageAccessibleDocument.getAccessibleSchemaType(); Set<PackageIdentifier> packageIdentifiers = schemaToPackageIdentifierMap.get(prefixedSchema); if (packageIdentifiers == null) { @@ -304,7 +226,7 @@ public class VisibilityStore { schemaToPackageIdentifierMap.put(prefixedSchema, packageIdentifiers); } } - mPackageAccessibleMap.put(prefix, schemaToPackageIdentifierMap); + mPackageAccessibleMap.setPackageAccessible(prefix, schemaToPackageIdentifierMap); } catch (AppSearchException e) { if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) { // TODO(b/172068212): This indicates some desync error. We were expecting a @@ -339,38 +261,30 @@ public class VisibilityStore { Objects.requireNonNull(schemasPackageAccessible); // Persist the document - GenericDocument.Builder<?> visibilityDocument = - new GenericDocument.Builder<>( - NAMESPACE, /*id=*/ addIdPrefix(prefix), VISIBILITY_TYPE); + VisibilityDocument.Builder visibilityDocument = + new VisibilityDocument.Builder(NAMESPACE, /*id=*/ addIdPrefix(prefix)); if (!schemasNotPlatformSurfaceable.isEmpty()) { - visibilityDocument.setPropertyString( - NOT_PLATFORM_SURFACEABLE_PROPERTY, + visibilityDocument.setSchemasNotPlatformSurfaceable( schemasNotPlatformSurfaceable.toArray(new String[0])); } Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>(); - List<GenericDocument> packageAccessibleDocuments = new ArrayList<>(); + List<PackageAccessibleDocument> packageAccessibleDocuments = new ArrayList<>(); for (Map.Entry<String, List<PackageIdentifier>> entry : schemasPackageAccessible.entrySet()) { for (int i = 0; i < entry.getValue().size(); i++) { - GenericDocument packageAccessibleDocument = new GenericDocument.Builder<>( - NAMESPACE, /*id=*/ "", PACKAGE_ACCESSIBLE_TYPE) - .setPropertyString( - PACKAGE_NAME_PROPERTY, - entry.getValue().get(i).getPackageName()) - .setPropertyBytes( - SHA_256_CERT_PROPERTY, - entry.getValue().get(i).getSha256Certificate()) - .setPropertyString(ACCESSIBLE_SCHEMA_PROPERTY, entry.getKey()) - .build(); + PackageAccessibleDocument packageAccessibleDocument = + new PackageAccessibleDocument.Builder(NAMESPACE, /*id=*/ "") + .setAccessibleSchemaType(entry.getKey()) + .setPackageIdentifier(entry.getValue().get(i)) + .build(); packageAccessibleDocuments.add(packageAccessibleDocument); } schemaToPackageIdentifierMap.put(entry.getKey(), new ArraySet<>(entry.getValue())); } if (!packageAccessibleDocuments.isEmpty()) { - visibilityDocument.setPropertyDocument( - PACKAGE_ACCESSIBLE_PROPERTY, - packageAccessibleDocuments.toArray(new GenericDocument[0])); + visibilityDocument.setPackageAccessibleSchemas( + packageAccessibleDocuments.toArray(new PackageAccessibleDocument[0])); } mAppSearchImpl.putDocument( @@ -379,8 +293,8 @@ public class VisibilityStore { mAppSearchImpl.persistToDisk(PersistType.Code.LITE); // Update derived data structures. - mNotPlatformSurfaceableMap.put(prefix, schemasNotPlatformSurfaceable); - mPackageAccessibleMap.put(prefix, schemaToPackageIdentifierMap); + mNotPlatformSurfaceableMap.setNotPlatformSurfaceable(prefix, schemasNotPlatformSurfaceable); + mPackageAccessibleMap.setPackageAccessible(prefix, schemaToPackageIdentifierMap); } /** Checks whether {@code prefixedSchema} can be searched over by the {@code callerUid}. */ @@ -389,10 +303,14 @@ public class VisibilityStore { Objects.requireNonNull(prefix); Objects.requireNonNull(prefixedSchema); + if (prefix.equals(VISIBILITY_STORE_PREFIX)) { + return false; // VisibilityStore schemas are for internal bookkeeping. + } + // We compare appIds here rather than direct uids because the package's uid may change based // on the user that's running. if (UserHandle.isSameApp(mGlobalQuerierUid, callerUid) - && isSchemaPlatformSurfaceable(prefix, prefixedSchema)) { + && mNotPlatformSurfaceableMap.isSchemaPlatformSurfaceable(prefix, prefixedSchema)) { return true; } @@ -401,29 +319,6 @@ public class VisibilityStore { } /** - * Returns whether the caller has platform query privileges, and if so, that the schema is - * surfaceable on the platform. - */ - private boolean isSchemaPlatformSurfaceable( - @NonNull String prefix, @NonNull String prefixedSchema) { - if (prefix.equals(VISIBILITY_STORE_PREFIX)) { - // VisibilityStore schemas are for internal bookkeeping. - return false; - } - - Set<String> notPlatformSurfaceableSchemas = mNotPlatformSurfaceableMap.get(prefix); - if (notPlatformSurfaceableSchemas == null) { - // No schemas were opted out of being platform-surfaced. So by default, it can be - // surfaced. - return true; - } - - // Some schemas were opted out of being platform-surfaced. As long as this schema - // isn't one of those opt-outs, it's surfaceable. - return !notPlatformSurfaceableSchemas.contains(prefixedSchema); - } - - /** * Returns whether the schema is accessible by the {@code callerUid}. Checks that the callerUid * has one of the allowed PackageIdentifier's package. And if so, that the package also has the * matching certificate. @@ -434,20 +329,8 @@ public class VisibilityStore { */ private boolean isSchemaPackageAccessible( @NonNull String prefix, @NonNull String prefixedSchema, int callerUid) { - Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = - mPackageAccessibleMap.get(prefix); - if (schemaToPackageIdentifierMap == null) { - // No schemas under this prefix have granted package access, return early. - return false; - } - Set<PackageIdentifier> packageIdentifiers = - schemaToPackageIdentifierMap.get(prefixedSchema); - if (packageIdentifiers == null) { - // No package identifiers were granted access for this schema, return early. - return false; - } - + mPackageAccessibleMap.getAccessiblePackages(prefix, prefixedSchema); for (PackageIdentifier packageIdentifier : packageIdentifiers) { // Check that the caller uid matches this allowlisted PackageIdentifier. // TODO(b/169883602): Consider caching the UIDs of packages. Looking this up in the @@ -467,7 +350,6 @@ public class VisibilityStore { return true; } } - // If we can't verify the schema is package accessible, default to no access. return false; } 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 e5e20e733264..1cb0cc51de34 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 @@ -28,6 +28,7 @@ import static com.android.server.appsearch.external.localstorage.util.PrefixUtil import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.WorkerThread; +import android.app.appsearch.AppSearchResult; import android.app.appsearch.AppSearchSchema; import android.app.appsearch.GenericDocument; import android.app.appsearch.GetByDocumentIdRequest; @@ -38,6 +39,7 @@ import android.app.appsearch.SearchSpec; import android.app.appsearch.SetSchemaResponse; import android.app.appsearch.StorageInfo; import android.app.appsearch.exceptions.AppSearchException; +import android.app.appsearch.util.LogUtil; import android.content.Context; import android.os.Bundle; import android.os.SystemClock; @@ -147,8 +149,11 @@ public final class AppSearchImpl implements Closeable { private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock(); + private final LogUtil mLogUtil = new LogUtil(TAG); + @GuardedBy("mReadWriteLock") - private final IcingSearchEngine mIcingSearchEngineLocked; + @VisibleForTesting + final IcingSearchEngine mIcingSearchEngineLocked; @GuardedBy("mReadWriteLock") private final VisibilityStore mVisibilityStoreLocked; @@ -216,7 +221,7 @@ public final class AppSearchImpl implements Closeable { appSearchImpl.initializeVisibilityStore(); long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime(); - if (logger != null && initStatsBuilder != null) { + if (logger != null) { initStatsBuilder .setTotalLatencyMillis( (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)) @@ -247,66 +252,91 @@ public final class AppSearchImpl implements Closeable { IcingSearchEngineOptions.newBuilder() .setBaseDir(icingDir.getAbsolutePath()) .build(); + mLogUtil.piiTrace("Constructing IcingSearchEngine, request", options); mIcingSearchEngineLocked = new IcingSearchEngine(options); + mLogUtil.piiTrace( + "Constructing IcingSearchEngine, response", + Objects.hashCode(mIcingSearchEngineLocked)); + mVisibilityStoreLocked = new VisibilityStore(this, context, userId, globalQuerierPackage); - InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize(); - - if (initStatsBuilder != null) { - initStatsBuilder - .setStatusCode( - statusProtoToAppSearchException(initializeResultProto.getStatus()) - .getResultCode()) - // TODO(b/173532925) how to get DeSyncs value - .setHasDeSync(false); - AppSearchLoggerHelper.copyNativeStats( - initializeResultProto.getInitializeStats(), initStatsBuilder); - } - long prepareSchemaAndNamespacesLatencyStartMillis = SystemClock.elapsedRealtime(); - SchemaProto schemaProto; - GetAllNamespacesResultProto getAllNamespacesResultProto = null; + // The core initialization procedure. If any part of this fails, we bail into + // resetLocked(), deleting all data (but hopefully allowing AppSearchImpl to come up). try { + mLogUtil.piiTrace("icingSearchEngine.initialize, request"); + InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize(); + mLogUtil.piiTrace( + "icingSearchEngine.initialize, response", + initializeResultProto.getStatus(), + initializeResultProto); + + if (initStatsBuilder != null) { + initStatsBuilder + .setStatusCode( + statusProtoToResultCode(initializeResultProto.getStatus())) + // TODO(b/173532925) how to get DeSyncs value + .setHasDeSync(false); + AppSearchLoggerHelper.copyNativeStats( + initializeResultProto.getInitializeStats(), initStatsBuilder); + } + checkSuccess(initializeResultProto.getStatus()); - schemaProto = getSchemaProtoLocked(); - getAllNamespacesResultProto = mIcingSearchEngineLocked.getAllNamespaces(); - checkSuccess(getAllNamespacesResultProto.getStatus()); - } catch (AppSearchException e) { - Log.w(TAG, "Error initializing, resetting IcingSearchEngine.", e); - if (initStatsBuilder != null && getAllNamespacesResultProto != null) { + + long prepareSchemaAndNamespacesLatencyStartMillis = SystemClock.elapsedRealtime(); + SchemaProto schemaProto = getSchemaProtoLocked(); + + mLogUtil.piiTrace("init:getAllNamespaces, request"); + GetAllNamespacesResultProto getAllNamespacesResultProto = + mIcingSearchEngineLocked.getAllNamespaces(); + mLogUtil.piiTrace( + "init:getAllNamespaces, response", + getAllNamespacesResultProto.getNamespacesCount(), + getAllNamespacesResultProto); + + if (initStatsBuilder != null) { initStatsBuilder .setStatusCode( - statusProtoToAppSearchException( - getAllNamespacesResultProto.getStatus()) - .getResultCode()) + statusProtoToResultCode( + getAllNamespacesResultProto.getStatus())) .setPrepareSchemaAndNamespacesLatencyMillis( (int) (SystemClock.elapsedRealtime() - prepareSchemaAndNamespacesLatencyStartMillis)); } - // Some error. Reset and see if it fixes it. - resetLocked(); - return; - } - // Populate schema map - for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) { - String prefixedSchemaType = schema.getSchemaType(); - addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType), schema); - } + checkSuccess(getAllNamespacesResultProto.getStatus()); - // Populate namespace map - for (String prefixedNamespace : getAllNamespacesResultProto.getNamespacesList()) { - addToMap(mNamespaceMapLocked, getPrefix(prefixedNamespace), prefixedNamespace); - } + // Populate schema map + for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) { + String prefixedSchemaType = schema.getSchemaType(); + addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType), schema); + } - // logging prepare_schema_and_namespaces latency - if (initStatsBuilder != null) { - initStatsBuilder.setPrepareSchemaAndNamespacesLatencyMillis( - (int) - (SystemClock.elapsedRealtime() - - prepareSchemaAndNamespacesLatencyStartMillis)); + // Populate namespace map + for (String prefixedNamespace : getAllNamespacesResultProto.getNamespacesList()) { + addToMap(mNamespaceMapLocked, getPrefix(prefixedNamespace), prefixedNamespace); + } + + // logging prepare_schema_and_namespaces latency + if (initStatsBuilder != null) { + initStatsBuilder.setPrepareSchemaAndNamespacesLatencyMillis( + (int) + (SystemClock.elapsedRealtime() + - prepareSchemaAndNamespacesLatencyStartMillis)); + } + + mLogUtil.piiTrace("Init completed successfully"); + + } catch (AppSearchException e) { + // Some error. Reset and see if it fixes it. + Log.e(TAG, "Error initializing, resetting IcingSearchEngine.", e); + if (initStatsBuilder != null) { + initStatsBuilder.setStatusCode(e.getResultCode()); + } + resetLocked(initStatsBuilder); } + } finally { mReadWriteLock.writeLock().unlock(); } @@ -321,8 +351,9 @@ public final class AppSearchImpl implements Closeable { mReadWriteLock.writeLock().lock(); try { throwIfClosedLocked(); - + mLogUtil.piiTrace("Initializing VisibilityStore, request"); mVisibilityStoreLocked.initialize(); + mLogUtil.piiTrace("Initializing VisibilityStore, response"); } finally { mReadWriteLock.writeLock().unlock(); } @@ -348,9 +379,10 @@ public final class AppSearchImpl implements Closeable { if (mClosedLocked) { return; } - persistToDisk(PersistType.Code.FULL); + mLogUtil.piiTrace("icingSearchEngine.close, request"); mIcingSearchEngineLocked.close(); + mLogUtil.piiTrace("icingSearchEngine.close, response"); mClosedLocked = true; } catch (AppSearchException e) { Log.w(TAG, "Error when closing AppSearchImpl.", e); @@ -410,9 +442,12 @@ public final class AppSearchImpl implements Closeable { rewriteSchema(prefix, existingSchemaBuilder, newSchemaBuilder.build()); // Apply schema + SchemaProto finalSchema = existingSchemaBuilder.build(); + mLogUtil.piiTrace("setSchema, request", finalSchema.getTypesCount(), finalSchema); SetSchemaResultProto setSchemaResultProto = - mIcingSearchEngineLocked.setSchema( - existingSchemaBuilder.build(), forceOverride); + mIcingSearchEngineLocked.setSchema(finalSchema, forceOverride); + mLogUtil.piiTrace( + "setSchema, response", setSchemaResultProto.getStatus(), setSchemaResultProto); // Determine whether it succeeded. try { @@ -544,11 +579,16 @@ public final class AppSearchImpl implements Closeable { mReadWriteLock.readLock().lock(); try { throwIfClosedLocked(); + mLogUtil.piiTrace("getAllNamespaces, request"); // We can't just use mNamespaceMap here because we have no way to prune namespaces from // mNamespaceMap when they have no more documents (e.g. after setting schema to empty or // using deleteByQuery). GetAllNamespacesResultProto getAllNamespacesResultProto = mIcingSearchEngineLocked.getAllNamespaces(); + mLogUtil.piiTrace( + "getAllNamespaces, response", + getAllNamespacesResultProto.getNamespacesCount(), + getAllNamespacesResultProto); checkSuccess(getAllNamespacesResultProto.getStatus()); String prefix = createPrefix(packageName, databaseName); List<String> results = new ArrayList<>(); @@ -601,17 +641,18 @@ public final class AppSearchImpl implements Closeable { String prefix = createPrefix(packageName, databaseName); addPrefixToDocument(documentBuilder, prefix); long rewriteDocumentTypeEndTimeMillis = SystemClock.elapsedRealtime(); + DocumentProto finalDocument = documentBuilder.build(); + mLogUtil.piiTrace("putDocument, request", finalDocument.getUri(), finalDocument); PutResultProto putResultProto = mIcingSearchEngineLocked.put(documentBuilder.build()); + mLogUtil.piiTrace("putDocument, response", putResultProto.getStatus(), putResultProto); addToMap(mNamespaceMapLocked, prefix, documentBuilder.getNamespace()); // Logging stats - if (logger != null && pStatsBuilder != null) { + if (pStatsBuilder != null) { pStatsBuilder .getGeneralStatsBuilder() - .setStatusCode( - statusProtoToAppSearchException(putResultProto.getStatus()) - .getResultCode()); + .setStatusCode(statusProtoToResultCode(putResultProto.getStatus())); pStatsBuilder .setGenerateDocumentProtoLatencyMillis( (int) @@ -629,7 +670,7 @@ public final class AppSearchImpl implements Closeable { } finally { mReadWriteLock.writeLock().unlock(); - if (logger != null && pStatsBuilder != null) { + if (logger != null) { long totalEndTimeMillis = SystemClock.elapsedRealtime(); pStatsBuilder .getGeneralStatsBuilder() @@ -685,8 +726,14 @@ public final class AppSearchImpl implements Closeable { .addAllTypePropertyMasks(prefixedPropertyMasks) .build(); + String finalNamespace = createPrefix(packageName, databaseName) + namespace; + if (mLogUtil.isPiiTraceEnabled()) { + mLogUtil.piiTrace( + "getDocument, request", finalNamespace + ", " + id + "," + getResultSpec); + } GetResultProto getResultProto = - mIcingSearchEngineLocked.get(prefix + namespace, id, getResultSpec); + mIcingSearchEngineLocked.get(finalNamespace, id, getResultSpec); + mLogUtil.piiTrace("getDocument, response", getResultProto.getStatus(), getResultProto); checkSuccess(getResultProto.getStatus()); // The schema type map cannot be null at this point. It could only be null if no @@ -754,7 +801,7 @@ public final class AppSearchImpl implements Closeable { sStatsBuilder); } finally { mReadWriteLock.readLock().unlock(); - if (logger != null && sStatsBuilder != null) { + if (logger != null) { sStatsBuilder.setTotalLatencyMillis( (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)); logger.logStats(sStatsBuilder.build()); @@ -832,9 +879,10 @@ public final class AppSearchImpl implements Closeable { } } else { // Client didn't specify certain schemas to search over, check all schemas - Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix).keySet(); + Map<String, SchemaTypeConfigProto> prefixedSchemas = + mSchemaMapLocked.get(prefix); if (prefixedSchemas != null) { - for (String prefixedSchema : prefixedSchemas) { + for (String prefixedSchema : prefixedSchemas.keySet()) { if (packageName.equals(callerPackageName) || mVisibilityStoreLocked.isSchemaSearchableByCaller( prefix, prefixedSchema, callerUid)) { @@ -854,7 +902,7 @@ public final class AppSearchImpl implements Closeable { } finally { mReadWriteLock.readLock().unlock(); - if (logger != null && sStatsBuilder != null) { + if (logger != null) { sStatsBuilder.setTotalLatencyMillis( (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)); logger.logStats(sStatsBuilder.build()); @@ -938,18 +986,25 @@ public final class AppSearchImpl implements Closeable { rewriteResultSpecForPrefixesLocked(resultSpecBuilder, prefixes, allowedPrefixedSchemas); ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec); + SearchSpecProto finalSearchSpec = searchSpecBuilder.build(); + ResultSpecProto finalResultSpec = resultSpecBuilder.build(); long rewriteSearchSpecLatencyEndMillis = SystemClock.elapsedRealtime(); + if (mLogUtil.isPiiTraceEnabled()) { + mLogUtil.piiTrace( + "search, request", + finalSearchSpec.getQuery(), + finalSearchSpec + ", " + scoringSpec + ", " + finalResultSpec); + } SearchResultProto searchResultProto = - mIcingSearchEngineLocked.search( - searchSpecBuilder.build(), scoringSpec, resultSpecBuilder.build()); + mIcingSearchEngineLocked.search(finalSearchSpec, scoringSpec, finalResultSpec); + mLogUtil.piiTrace( + "search, response", searchResultProto.getResultsCount(), searchResultProto); if (sStatsBuilder != null) { sStatsBuilder - .setStatusCode( - statusProtoToAppSearchException(searchResultProto.getStatus()) - .getResultCode()) + .setStatusCode(statusProtoToResultCode(searchResultProto.getStatus())) .setRewriteSearchSpecLatencyMillis( (int) (rewriteSearchSpecLatencyEndMillis @@ -985,8 +1040,13 @@ public final class AppSearchImpl implements Closeable { try { throwIfClosedLocked(); + mLogUtil.piiTrace("getNextPage, request", nextPageToken); SearchResultProto searchResultProto = mIcingSearchEngineLocked.getNextPage(nextPageToken); + mLogUtil.piiTrace( + "getNextPage, response", + searchResultProto.getResultsCount(), + searchResultProto); checkSuccess(searchResultProto.getStatus()); return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked); } finally { @@ -1007,6 +1067,7 @@ public final class AppSearchImpl implements Closeable { try { throwIfClosedLocked(); + mLogUtil.piiTrace("invalidateNextPageToken, request", nextPageToken); mIcingSearchEngineLocked.invalidateNextPageToken(nextPageToken); } finally { mReadWriteLock.readLock().unlock(); @@ -1039,7 +1100,9 @@ public final class AppSearchImpl implements Closeable { .setUsageType(usageType) .build(); + mLogUtil.piiTrace("reportUsage, request", report.getDocumentUri(), report); ReportUsageResultProto result = mIcingSearchEngineLocked.reportUsage(report); + mLogUtil.piiTrace("reportUsage, response", result.getStatus(), result); checkSuccess(result.getStatus()); } finally { mReadWriteLock.writeLock().unlock(); @@ -1068,9 +1131,13 @@ public final class AppSearchImpl implements Closeable { throwIfClosedLocked(); String prefixedNamespace = createPrefix(packageName, databaseName) + namespace; + if (mLogUtil.isPiiTraceEnabled()) { + mLogUtil.piiTrace("removeById, request", prefixedNamespace + ", " + id); + } DeleteResultProto deleteResultProto = mIcingSearchEngineLocked.delete(prefixedNamespace, id); - + mLogUtil.piiTrace( + "removeById, response", deleteResultProto.getStatus(), deleteResultProto); checkSuccess(deleteResultProto.getStatus()); } finally { mReadWriteLock.writeLock().unlock(); @@ -1121,8 +1188,12 @@ public final class AppSearchImpl implements Closeable { searchSpecBuilder, Collections.singleton(prefix), allowedPrefixedSchemas)) { return; } + SearchSpecProto finalSearchSpec = searchSpecBuilder.build(); + mLogUtil.piiTrace("removeByQuery, request", finalSearchSpec); DeleteByQueryResultProto deleteResultProto = - mIcingSearchEngineLocked.deleteByQuery(searchSpecBuilder.build()); + mIcingSearchEngineLocked.deleteByQuery(finalSearchSpec); + mLogUtil.piiTrace( + "removeByQuery, response", deleteResultProto.getStatus(), deleteResultProto); // It seems that the caller wants to get success if the data matching the query is // not in the DB because it was not there or was successfully deleted. @@ -1202,7 +1273,10 @@ public final class AppSearchImpl implements Closeable { @NonNull private StorageInfo getStorageInfoForNamespacesLocked(@NonNull Set<String> prefixedNamespaces) throws AppSearchException { + mLogUtil.piiTrace("getStorageInfo, request"); StorageInfoResultProto storageInfoResult = mIcingSearchEngineLocked.getStorageInfo(); + mLogUtil.piiTrace( + "getStorageInfo, response", storageInfoResult.getStatus(), storageInfoResult); checkSuccess(storageInfoResult.getStatus()); if (!storageInfoResult.hasStorageInfo() || !storageInfoResult.getStorageInfo().hasDocumentStorageInfo()) { @@ -1277,8 +1351,13 @@ public final class AppSearchImpl implements Closeable { try { throwIfClosedLocked(); + mLogUtil.piiTrace("persistToDisk, request", persistType); PersistToDiskResultProto persistToDiskResultProto = mIcingSearchEngineLocked.persistToDisk(persistType); + mLogUtil.piiTrace( + "persistToDisk, response", + persistToDiskResultProto.getStatus(), + persistToDiskResultProto); checkSuccess(persistToDiskResultProto.getStatus()); } finally { mReadWriteLock.writeLock().unlock(); @@ -1305,12 +1384,21 @@ public final class AppSearchImpl implements Closeable { newSchemaBuilder.addTypes(existingSchema.getTypes(i)); } } + SchemaProto finalSchema = newSchemaBuilder.build(); // Apply schema, set force override to true to remove all schemas and documents under // that package. + mLogUtil.piiTrace( + "clearPackageData.setSchema, request", + finalSchema.getTypesCount(), + finalSchema); SetSchemaResultProto setSchemaResultProto = mIcingSearchEngineLocked.setSchema( - newSchemaBuilder.build(), /*ignoreErrorsAndDeleteDocuments=*/ true); + finalSchema, /*ignoreErrorsAndDeleteDocuments=*/ true); + mLogUtil.piiTrace( + "clearPackageData.setSchema, response", + setSchemaResultProto.getStatus(), + setSchemaResultProto); // Determine whether it succeeded. checkSuccess(setSchemaResultProto.getStatus()); @@ -1330,12 +1418,24 @@ public final class AppSearchImpl implements Closeable { * @throws AppSearchException on IcingSearchEngine error. */ @GuardedBy("mReadWriteLock") - private void resetLocked() throws AppSearchException { + private void resetLocked(@Nullable InitializeStats.Builder initStatsBuilder) + throws AppSearchException { + mLogUtil.piiTrace("icingSearchEngine.reset, request"); ResetResultProto resetResultProto = mIcingSearchEngineLocked.reset(); + mLogUtil.piiTrace( + "icingSearchEngine.reset, response", + resetResultProto.getStatus(), + resetResultProto); mOptimizeIntervalCountLocked = 0; mSchemaMapLocked.clear(); mNamespaceMapLocked.clear(); + if (initStatsBuilder != null) { + initStatsBuilder + .setHasReset(true) + .setResetStatusCode(statusProtoToResultCode(resetResultProto.getStatus())); + } + // Must be called after everything else since VisibilityStore may repopulate // IcingSearchEngine with an initial schema. mVisibilityStoreLocked.handleReset(); @@ -1472,15 +1572,17 @@ public final class AppSearchImpl implements Closeable { // Empty namespaces on the search spec means to query over all namespaces. Set<String> existingNamespaces = mNamespaceMapLocked.get(prefix); - if (namespaceFilters.isEmpty()) { - // Include all namespaces - searchSpecBuilder.addAllNamespaceFilters(existingNamespaces); - } else { - // Prefix the given namespaces. - for (int i = 0; i < namespaceFilters.size(); i++) { - String prefixedNamespace = prefix + namespaceFilters.get(i); - if (existingNamespaces.contains(prefixedNamespace)) { - searchSpecBuilder.addNamespaceFilters(prefixedNamespace); + if (existingNamespaces != null) { + if (namespaceFilters.isEmpty()) { + // Include all namespaces + searchSpecBuilder.addAllNamespaceFilters(existingNamespaces); + } else { + // Prefix the given namespaces. + for (int i = 0; i < namespaceFilters.size(); i++) { + String prefixedNamespace = prefix + namespaceFilters.get(i); + if (existingNamespaces.contains(prefixedNamespace)) { + searchSpecBuilder.addNamespaceFilters(prefixedNamespace); + } } } } @@ -1581,6 +1683,9 @@ public final class AppSearchImpl implements Closeable { Map<String, List<String>> packageAndNamespaceToNamespaces = new ArrayMap<>(); for (String prefix : existingPrefixes) { Set<String> prefixedNamespaces = mNamespaceMapLocked.get(prefix); + if (prefixedNamespaces == null) { + continue; + } String packageName = getPackageName(prefix); // Create a new prefix without the database name. This will allow us to group namespaces // that have the same name and package but a different database name together. @@ -1636,6 +1741,9 @@ public final class AppSearchImpl implements Closeable { Map<String, List<String>> packageToNamespacesMap = new ArrayMap<>(); for (String prefix : existingPrefixes) { Set<String> prefixedNamespaces = mNamespaceMapLocked.get(prefix); + if (prefixedNamespaces == null) { + continue; + } String packageName = getPackageName(prefix); List<String> packageNamespaceList = packageToNamespacesMap.get(packageName); if (packageNamespaceList == null) { @@ -1677,6 +1785,9 @@ public final class AppSearchImpl implements Closeable { Map<String, List<String>> namespaceToPrefixedNamespaces = new ArrayMap<>(); for (String prefix : existingPrefixes) { Set<String> prefixedNamespaces = mNamespaceMapLocked.get(prefix); + if (prefixedNamespaces == null) { + continue; + } for (String prefixedNamespace : prefixedNamespaces) { String namespace; try { @@ -1707,7 +1818,9 @@ public final class AppSearchImpl implements Closeable { @VisibleForTesting @GuardedBy("mReadWriteLock") SchemaProto getSchemaProtoLocked() throws AppSearchException { + mLogUtil.piiTrace("getSchema, request"); GetSchemaResultProto schemaProto = mIcingSearchEngineLocked.getSchema(); + mLogUtil.piiTrace("getSchema, response", schemaProto.getStatus(), schemaProto); // TODO(b/161935693) check GetSchemaResultProto is success or not. Call reset() if it's not. // TODO(b/161935693) only allow GetSchemaResultProto NOT_FOUND on first run checkCodeOneOf(schemaProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND); @@ -1781,7 +1894,9 @@ public final class AppSearchImpl implements Closeable { return; } - throw statusProtoToAppSearchException(statusProto); + throw new AppSearchException( + ResultCodeToProtoConverter.toResultCode(statusProto.getCode()), + statusProto.getMessage()); } /** @@ -1849,7 +1964,10 @@ public final class AppSearchImpl implements Closeable { public void optimize() throws AppSearchException { mReadWriteLock.writeLock().lock(); try { + mLogUtil.piiTrace("optimize, request"); OptimizeResultProto optimizeResultProto = mIcingSearchEngineLocked.optimize(); + mLogUtil.piiTrace( + "optimize, response", optimizeResultProto.getStatus(), optimizeResultProto); checkSuccess(optimizeResultProto.getStatus()); } finally { mReadWriteLock.writeLock().unlock(); @@ -1887,7 +2005,10 @@ public final class AppSearchImpl implements Closeable { @GuardedBy("mReadWriteLock") @VisibleForTesting GetOptimizeInfoResultProto getOptimizeInfoResultLocked() { - return mIcingSearchEngineLocked.getOptimizeInfo(); + mLogUtil.piiTrace("getOptimizeInfo, request"); + GetOptimizeInfoResultProto result = mIcingSearchEngineLocked.getOptimizeInfo(); + mLogUtil.piiTrace("getOptimizeInfo, response", result.getStatus(), result); + return result; } @GuardedBy("mReadWriteLock") @@ -1898,16 +2019,16 @@ public final class AppSearchImpl implements Closeable { } /** - * Converts an erroneous status code to an AppSearchException. Callers should ensure that the - * status code is not OK or WARNING_DATA_LOSS. + * Converts an erroneous status code from the Icing status enums to the AppSearchResult enums. * - * @param statusProto StatusProto with error code and message to translate into - * AppSearchException. - * @return AppSearchException with the parallel error code. + * <p>Callers should ensure that the status code is not OK or WARNING_DATA_LOSS. + * + * @param statusProto StatusProto with error code to translate into an {@link AppSearchResult} + * code. + * @return {@link AppSearchResult} error code */ - private static AppSearchException statusProtoToAppSearchException(StatusProto statusProto) { - return new AppSearchException( - ResultCodeToProtoConverter.toResultCode(statusProto.getCode()), - statusProto.getMessage()); + private static @AppSearchResult.ResultCode int statusProtoToResultCode( + @NonNull StatusProto statusProto) { + return ResultCodeToProtoConverter.toResultCode(statusProto.getCode()); } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java index 5364a0caa1aa..72befa795d9f 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java @@ -106,6 +106,10 @@ public final class InitializeStats { private final int mNativeNumDocuments; /** Returns number of schema types currently in the schema store. */ private final int mNativeNumSchemaTypes; + /** Whether we had to reset the index, losing all data, during initialization. */ + private final boolean mHasReset; + /** If we had to reset, contains the status code of the reset operation. */ + @AppSearchResult.ResultCode private final int mResetStatusCode; /** Returns the status of the initialization. */ @AppSearchResult.ResultCode @@ -214,6 +218,21 @@ public final class InitializeStats { return mNativeNumSchemaTypes; } + /** Returns whether we had to reset the index, losing all data, as part of initialization. */ + public boolean hasReset() { + return mHasReset; + } + + /** + * Returns the status of the reset, if one was performed according to {@link #hasReset}. + * + * <p>If no value has been set, the default value is {@link AppSearchResult#RESULT_OK}. + */ + @AppSearchResult.ResultCode + public int getResetStatusCode() { + return mResetStatusCode; + } + InitializeStats(@NonNull Builder builder) { Objects.requireNonNull(builder); mStatusCode = builder.mStatusCode; @@ -232,11 +251,14 @@ public final class InitializeStats { mNativeDocumentStoreDataStatus = builder.mNativeDocumentStoreDataStatus; mNativeNumDocuments = builder.mNativeNumDocuments; mNativeNumSchemaTypes = builder.mNativeNumSchemaTypes; + mHasReset = builder.mHasReset; + mResetStatusCode = builder.mResetStatusCode; } /** Builder for {@link InitializeStats}. */ public static class Builder { @AppSearchResult.ResultCode int mStatusCode; + int mTotalLatencyMillis; boolean mHasDeSync; int mPrepareSchemaAndNamespacesLatencyMillis; @@ -251,6 +273,8 @@ public final class InitializeStats { @DocumentStoreDataStatus int mNativeDocumentStoreDataStatus; int mNativeNumDocuments; int mNativeNumSchemaTypes; + boolean mHasReset; + @AppSearchResult.ResultCode int mResetStatusCode; /** Sets the status of the initialization. */ @NonNull @@ -392,6 +416,20 @@ public final class InitializeStats { return this; } + /** Sets whether we had to reset the index, losing all data, as part of initialization. */ + @NonNull + public Builder setHasReset(boolean hasReset) { + mHasReset = hasReset; + return this; + } + + /** Sets the status of the reset, if one was performed according to {@link #setHasReset}. */ + @NonNull + public Builder setResetStatusCode(@AppSearchResult.ResultCode int resetStatusCode) { + mResetStatusCode = resetStatusCode; + return this; + } + /** * Constructs a new {@link InitializeStats} from the contents of this {@link * InitializeStats.Builder} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java index 9ae9f1852849..80cfe89ffc1c 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java @@ -112,8 +112,11 @@ public class PrefixUtil { return prefixedString.substring(delimiterIndex + 1); } throw new AppSearchException( - AppSearchResult.RESULT_UNKNOWN_ERROR, - "The prefixed value doesn't contains a valid database name."); + AppSearchResult.RESULT_INTERNAL_ERROR, + "The prefixed value \"" + + prefixedString + + "\" doesn't contain a valid " + + "database name"); } /** @@ -128,8 +131,11 @@ public class PrefixUtil { int databaseDelimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER); if (databaseDelimiterIndex == -1) { throw new AppSearchException( - AppSearchResult.RESULT_UNKNOWN_ERROR, - "The databaseName prefixed value doesn't contain a valid database name."); + AppSearchResult.RESULT_INTERNAL_ERROR, + "The prefixed value \"" + + prefixedString + + "\" doesn't contain a valid " + + "database name"); } // Add 1 to include the char size of the DATABASE_DELIMITER diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java new file mode 100644 index 000000000000..5afdda280c35 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.appsearch.visibilitystore; + +import android.annotation.NonNull; +import android.util.ArrayMap; + +import java.util.Map; +import java.util.Set; + +/** + * Stores information about what types are hidden from platform surfaces through the + * {@link android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} API. + * + * This object is not thread safe. + * @hide + */ +public class NotPlatformSurfaceableMap { + /** + * Maps prefixes to the set of prefixed schemas that are platform-hidden within that prefix. + */ + private final Map<String, Set<String>> mMap = new ArrayMap<>(); + + /** + * Sets the prefixed schemas that are opted out of platform surfacing for the prefix. + * + * <p>Any existing mappings for this prefix are overwritten. + */ + public void setNotPlatformSurfaceable(@NonNull String prefix, @NonNull Set<String> schemas) { + mMap.put(prefix, schemas); + } + + /** + * Returns whether the given prefixed schema is platform surfaceable (has not opted out) in the + * given prefix. + */ + public boolean isSchemaPlatformSurfaceable(@NonNull String prefix, @NonNull String schemaType) { + Set<String> schemaTypes = mMap.get(prefix); + if (schemaTypes == null) { + // No opt-outs for this prefix + return true; + } + // Some schemas were opted out of being platform-surfaced. As long as this schema + // isn't one of those opt-outs, it's surfaceable. + return !schemaTypes.contains(schemaType); + } + + /** Discards all data in the map. */ + public void clear() { + mMap.clear(); + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java new file mode 100644 index 000000000000..5601ef93490d --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.appsearch.visibilitystore; + +import android.annotation.NonNull; +import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.GenericDocument; +import android.app.appsearch.PackageIdentifier; + +import androidx.annotation.Nullable; + +/** + * Holds configuration about a package+cert that can access a schema. + * + * @see android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage + * @hide + */ +public class PackageAccessibleDocument extends GenericDocument { + /** Schema type for nested documents that hold package accessible information. */ + public static final String SCHEMA_TYPE = "PackageAccessibleType"; + + /** Property that holds the package name that can access a schema. */ + private static final String PACKAGE_NAME_PROPERTY = "packageName"; + + /** Property that holds the SHA 256 certificate of the app that can access a schema. */ + private static final String SHA_256_CERT_PROPERTY = "sha256Cert"; + + /** Property that holds the prefixed schema type that is accessible by some package. */ + private static final String ACCESSIBLE_SCHEMA_PROPERTY = "accessibleSchema"; + + /** + * Schema for package accessible documents, these will be nested in a top-level + * {@link VisibilityDocument}. + * + * <p>NOTE: If you update this, also update + * {@link com.android.server.appsearch.external.localstorage.VisibilityStore#SCHEMA_VERSION} + */ + public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE) + .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(PACKAGE_NAME_PROPERTY) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .build()) + .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(SHA_256_CERT_PROPERTY) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .build()) + .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( + ACCESSIBLE_SCHEMA_PROPERTY) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .build()) + .build(); + + public PackageAccessibleDocument(@NonNull GenericDocument genericDocument) { + super(genericDocument); + } + + @Nullable + public String getAccessibleSchemaType() { + return getPropertyString(ACCESSIBLE_SCHEMA_PROPERTY); + } + + /** Gets which package is able to access {@link #getAccessibleSchemaType} */ + @NonNull + public PackageIdentifier getPackageIdentifier() { + String packageName = getPropertyString(PACKAGE_NAME_PROPERTY); + byte[] sha256Cert = getPropertyBytes(SHA_256_CERT_PROPERTY); + 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) { + super(namespace, id, SCHEMA_TYPE); + } + + /** Sets which prefixed schema type is accessible by the package */ + @NonNull + public Builder setAccessibleSchemaType(@NonNull String schemaType) { + return setPropertyString(ACCESSIBLE_SCHEMA_PROPERTY, schemaType); + } + + /** Sets which package is able to access the {@link #setAccessibleSchemaType}. */ + @NonNull + public Builder setPackageIdentifier(@NonNull PackageIdentifier packageIdentifier) { + return setPropertyString(PACKAGE_NAME_PROPERTY, packageIdentifier.getPackageName()) + .setPropertyBytes(SHA_256_CERT_PROPERTY, + packageIdentifier.getSha256Certificate()); + } + + @Override + @NonNull + public PackageAccessibleDocument build() { + return new PackageAccessibleDocument(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/PackageAccessibleMap.java new file mode 100644 index 000000000000..e90e8bf3f118 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.appsearch.visibilitystore; + +import android.annotation.NonNull; +import android.app.appsearch.PackageIdentifier; +import android.util.ArrayMap; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * Stores information about what types are accessible to which packages through the + * {@link android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} API. + * + * This object is not thread safe. + * @hide + */ +public class PackageAccessibleMap { + /** + * Maps prefixes to prefixed schema types to PackageIdentifiers that have access to that schema. + */ + private final Map<String, Map<String, Set<PackageIdentifier>>> mMap = new ArrayMap<>(); + + /** + * Sets the prefixed schemas that have package visibility in the given prefix. + * + * <p>Any existing mappings for this prefix are overwritten. + */ + public void setPackageAccessible( + @NonNull String prefix, + @NonNull Map<String, Set<PackageIdentifier>> schemaToPackageIdentifier) { + mMap.put(prefix, schemaToPackageIdentifier); + } + + /** + * Returns the set of all {@link android.app.appsearch.PackageIdentifier}s which can access the + * given schema type. + * + * <p>If no such settings exist, returns the empty set. + */ + @NonNull + public Set<PackageIdentifier> getAccessiblePackages( + @NonNull String prefix, @NonNull String schemaType) { + Map<String, Set<PackageIdentifier>> schemaTypeToVisibility = mMap.get(prefix); + if (schemaTypeToVisibility == null) { + return Collections.emptySet(); + } + Set<PackageIdentifier> accessiblePackages = schemaTypeToVisibility.get(schemaType); + if (accessiblePackages == null) { + return Collections.emptySet(); + } + return accessiblePackages; + } + + /** Discards all data in the map. */ + public void clear() { + mMap.clear(); + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java new file mode 100644 index 000000000000..327ce854fc5b --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.appsearch.visibilitystore; + +import android.annotation.NonNull; +import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.GenericDocument; + +import androidx.annotation.Nullable; + +/** + * Holds the visibility settings that apply to a package's databases. + * @hide + */ +public class VisibilityDocument extends GenericDocument { + /** Schema type for documents that hold AppSearch's metadata, e.g. visibility settings */ + public static final String SCHEMA_TYPE = "VisibilityType"; + + /** + * Property that holds the list of platform-hidden schemas, as part of the visibility settings. + */ + private static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable"; + + /** Property that holds nested documents of package accessible schemas. */ + private static final String PACKAGE_ACCESSIBLE_PROPERTY = "packageAccessible"; + + /** + * Schema for the VisibilityStore's documents. + * + * <p>NOTE: If you update this, also update + * {@link com.android.server.appsearch.external.localstorage.VisibilityStore#SCHEMA_VERSION} + */ + public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE) + .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( + NOT_PLATFORM_SURFACEABLE_PROPERTY) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .build()) + .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder( + PACKAGE_ACCESSIBLE_PROPERTY, PackageAccessibleDocument.SCHEMA_TYPE) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .build()) + .build(); + + public VisibilityDocument(@NonNull GenericDocument genericDocument) { + super(genericDocument); + } + + @Nullable + public String[] getNotPlatformSurfaceableSchemas() { + return getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY); + } + + @Nullable + public GenericDocument[] getPackageAccessibleSchemas() { + return getPropertyDocumentArray(PACKAGE_ACCESSIBLE_PROPERTY); + } + + /** Builder for {@link VisibilityDocument}. */ + public static class Builder extends GenericDocument.Builder<VisibilityDocument.Builder> { + public Builder(@NonNull String namespace, @NonNull String id) { + super(namespace, id, SCHEMA_TYPE); + } + + /** Sets which prefixed schemas have opted out of platform surfacing. */ + @NonNull + public Builder setSchemasNotPlatformSurfaceable( + @NonNull String[] notPlatformSurfaceableSchemas) { + return setPropertyString( + NOT_PLATFORM_SURFACEABLE_PROPERTY, notPlatformSurfaceableSchemas); + } + + /** Sets which prefixed schemas have configured package access. */ + @NonNull + public Builder setPackageAccessibleSchemas( + @NonNull PackageAccessibleDocument[] packageAccessibleSchemas) { + return setPropertyDocument(PACKAGE_ACCESSIBLE_PROPERTY, packageAccessibleSchemas); + } + } +} diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index 85d85aa5aa31..9723de6c5fce 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -I0216abecc41d020f16ed8947a9f37b710afd331e +a83c33a5a394141fea1d065ce0fab513a62d4bcf 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 a70d9b5facc3..d6ce3ebc267f 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 @@ -30,6 +30,9 @@ import java.util.Set; * <p>An {@link AppSearchSessionShim} instance provides access to database operations such as * setting a schema, adding documents, and searching. * + * <p>Instances of this interface are usually obtained from a storage implementation, e.g. {@code + * AppSearchManager.createSearchSession()} or {@code PlatformStorage.createSearchSession()}. + * * <p>All implementations of this interface must be thread safe. * * @see GlobalSearchSessionShim diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index b096537821ca..8b824e8a9a72 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -152,6 +152,8 @@ public class AlarmManager { * this broadcast will be sent. Applications can reschedule all the necessary alarms when * receiving it. * + * <p>This broadcast will <em>not</em> be sent when the user revokes the permission. + * * <p><em>Note:</em> * Applications are still required to check {@link #canScheduleExactAlarms()} * before using the above APIs after receiving this broadcast, diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 452be3008a32..96cbed75622f 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -2629,8 +2629,7 @@ public class JobSchedulerService extends com.android.server.SystemService } } catch (NameNotFoundException e) { throw new IllegalArgumentException( - "Tried to schedule job for non-existent package: " - + service.getPackageName()); + "Tried to schedule job for non-existent component: " + service); } } diff --git a/api/Android.bp b/api/Android.bp index 5b733883063a..b85dc4613981 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -24,12 +24,17 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +metalava_cmd = "$(location metalava)" +// Silence reflection warnings. See b/168689341 +metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED " +metalava_cmd += " --no-banner --format=v2 " + genrule { name: "current-api-xml", tools: ["metalava"], srcs: [":frameworks-base-api-current.txt"], out: ["current.api"], - cmd: "$(location metalava) --no-banner -convert2xmlnostrip $(in) $(out)", + cmd: metalava_cmd + "-convert2xmlnostrip $(in) $(out)", visibility: ["//visibility:public"], } @@ -56,7 +61,7 @@ genrule { ], out: ["current.txt"], tools: ["metalava"], - cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", + cmd: metalava_cmd + "$(in) --api $(out)", dists: [ { targets: ["droidcore"], @@ -81,7 +86,7 @@ genrule { ], out: ["stdout.txt"], tools: ["metalava"], - cmd: "$(location metalava) --no-banner --format=v2 " + + cmd: metalava_cmd + "--check-compatibility:api:released $(location :android.api.public.latest) " + "--baseline:compatibility:released $(location :android-incompatibilities.api.public.latest) " + "$(location :frameworks-base-api-current.txt) " + @@ -138,7 +143,7 @@ genrule { ], out: ["removed.txt"], tools: ["metalava"], - cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", + cmd: metalava_cmd + "$(in) --api $(out)", dists: [ { targets: ["droidcore"], @@ -173,7 +178,7 @@ genrule { ], out: ["system-current.txt"], tools: ["metalava"], - cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", + cmd: metalava_cmd + "$(in) --api $(out)", dists: [ { targets: ["droidcore"], @@ -199,7 +204,7 @@ genrule { ], out: ["stdout.txt"], tools: ["metalava"], - cmd: "$(location metalava) --no-banner --format=v2 " + + cmd: metalava_cmd + "--check-compatibility:api:released $(location :android.api.system.latest) " + "--check-compatibility:base $(location :frameworks-base-api-current.txt) " + "--baseline:compatibility:released $(location :android-incompatibilities.api.system.latest) " + @@ -227,7 +232,7 @@ genrule { ], out: ["system-removed.txt"], tools: ["metalava"], - cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", + cmd: metalava_cmd + "$(in) --api $(out)", dists: [ { targets: ["droidcore"], @@ -263,7 +268,7 @@ genrule { ], out: ["module-lib-current.txt"], tools: ["metalava"], - cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", + cmd: metalava_cmd + "$(in) --api $(out)", dists: [ { targets: ["droidcore"], @@ -288,7 +293,7 @@ genrule { ], out: ["stdout.txt"], tools: ["metalava"], - cmd: "$(location metalava) --no-banner --format=v2 " + + cmd: metalava_cmd + "--check-compatibility:api:released $(location :android.api.module-lib.latest) " + // Note: having "public" be the base of module-lib is not perfect -- it should // ideally be a merged public+system), but this will help when migrating from @@ -319,7 +324,7 @@ genrule { ], out: ["module-lib-removed.txt"], tools: ["metalava"], - cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", + cmd: metalava_cmd + "$(in) --api $(out)", dists: [ { targets: ["droidcore"], @@ -360,7 +365,7 @@ genrule { ], out: ["system-server-current.txt"], tools: ["metalava"], - cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", + cmd: metalava_cmd + "$(in) --api $(out)", dists: [ { targets: ["droidcore"], @@ -384,7 +389,7 @@ genrule { ], out: ["system-server-removed.txt"], tools: ["metalava"], - cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", + cmd: metalava_cmd + "$(in) --api $(out)", dists: [ { targets: ["droidcore"], diff --git a/core/api/current.txt b/core/api/current.txt index 98c2d40aa952..6fd17157ee86 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -141,6 +141,7 @@ package android { field public static final String REORDER_TASKS = "android.permission.REORDER_TASKS"; field public static final String REQUEST_COMPANION_PROFILE_WATCH = "android.permission.REQUEST_COMPANION_PROFILE_WATCH"; field public static final String REQUEST_COMPANION_RUN_IN_BACKGROUND = "android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND"; + field public static final String REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND = "android.permission.REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND"; field public static final String REQUEST_COMPANION_USE_DATA_IN_BACKGROUND = "android.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND"; field public static final String REQUEST_DELETE_PACKAGES = "android.permission.REQUEST_DELETE_PACKAGES"; field public static final String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; @@ -245,6 +246,7 @@ package android { public static final class R.attr { ctor public R.attr(); + field public static final int __removed3; field public static final int absListViewStyle = 16842858; // 0x101006a field public static final int accessibilityEventTypes = 16843648; // 0x1010380 field public static final int accessibilityFeedbackType = 16843650; // 0x1010382 @@ -3172,6 +3174,7 @@ package android.accessibilityservice { field public static final int FLAG_ENABLE_ACCESSIBILITY_VOLUME = 128; // 0x80 field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2 field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10 + field public static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 8192; // 0x2000 field public static final int FLAG_REQUEST_ACCESSIBILITY_BUTTON = 256; // 0x100 field @Deprecated public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8 field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20 @@ -52887,8 +52890,8 @@ package android.view.translation { public static final class TranslationRequest.Builder { ctor public TranslationRequest.Builder(); - method @NonNull public android.view.translation.TranslationRequest.Builder addTranslationRequestValue(@NonNull android.view.translation.TranslationRequestValue); - method @NonNull public android.view.translation.TranslationRequest.Builder addViewTranslationRequest(@NonNull android.view.translation.ViewTranslationRequest); + method @Deprecated @NonNull public android.view.translation.TranslationRequest.Builder addTranslationRequestValue(@NonNull android.view.translation.TranslationRequestValue); + method @Deprecated @NonNull public android.view.translation.TranslationRequest.Builder addViewTranslationRequest(@NonNull android.view.translation.ViewTranslationRequest); method @NonNull public android.view.translation.TranslationRequest build(); method @NonNull public android.view.translation.TranslationRequest.Builder setFlags(int); method @NonNull public android.view.translation.TranslationRequest.Builder setTranslationRequestValues(@NonNull java.util.List<android.view.translation.TranslationRequestValue>); @@ -52898,7 +52901,7 @@ package android.view.translation { public final class TranslationRequestValue implements android.os.Parcelable { method public int describeContents(); method @NonNull public static android.view.translation.TranslationRequestValue forText(@NonNull CharSequence); - method @NonNull public CharSequence getText(); + method @Nullable public CharSequence getText(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationRequestValue> CREATOR; } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 62ffac7bddf2..1a773557fc69 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -10405,19 +10405,20 @@ package android.service.translation { method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method public void onConnected(); method public void onCreateTranslationSession(@NonNull android.view.translation.TranslationContext, int, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @Deprecated public abstract void onCreateTranslationSession(@NonNull android.view.translation.TranslationContext, int); + method @Deprecated public void onCreateTranslationSession(@NonNull android.view.translation.TranslationContext, int); method public void onDisconnected(); method public abstract void onFinishTranslationSession(int); method public abstract void onTranslationCapabilitiesRequest(int, int, @NonNull java.util.function.Consumer<java.util.Set<android.view.translation.TranslationCapability>>); - method public abstract void onTranslationRequest(@NonNull android.view.translation.TranslationRequest, int, @Nullable android.os.CancellationSignal, @NonNull android.service.translation.TranslationService.OnTranslationResultCallback); + method @Deprecated public void onTranslationRequest(@NonNull android.view.translation.TranslationRequest, int, @Nullable android.os.CancellationSignal, @NonNull android.service.translation.TranslationService.OnTranslationResultCallback); + method public void onTranslationRequest(@NonNull android.view.translation.TranslationRequest, int, @Nullable android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.view.translation.TranslationResponse>); method public final void updateTranslationCapability(@NonNull android.view.translation.TranslationCapability); field public static final String SERVICE_INTERFACE = "android.service.translation.TranslationService"; field public static final String SERVICE_META_DATA = "android.translation_service"; } - public static interface TranslationService.OnTranslationResultCallback { + @Deprecated public static interface TranslationService.OnTranslationResultCallback { method @Deprecated public void onError(); - method public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse); + method @Deprecated public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse); } } @@ -10468,7 +10469,7 @@ package android.service.voice { method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int); method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int); method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(); - method @Nullable public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle); + method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle); method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition(); method public final void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory); field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1 @@ -10493,7 +10494,7 @@ package android.service.voice { method public abstract void onAvailabilityChanged(int); method public void onHotwordDetectionServiceInitialized(int); method public void onHotwordDetectionServiceRestarted(); - method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult); + method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult); } public static class AlwaysOnHotwordDetector.EventPayload { @@ -10539,6 +10540,7 @@ package android.service.voice { public abstract class HotwordDetectionService extends android.app.Service { ctor public HotwordDetectionService(); + method public static int getMaxCustomInitializationStatus(); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method @Deprecated public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, long, @NonNull android.service.voice.HotwordDetectionService.Callback); method public void onDetect(@NonNull android.service.voice.AlwaysOnHotwordDetector.EventPayload, long, @NonNull android.service.voice.HotwordDetectionService.Callback); @@ -10546,16 +10548,14 @@ package android.service.voice { method public void onDetect(@NonNull android.service.voice.HotwordDetectionService.Callback); method public void onDetect(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle, @NonNull android.service.voice.HotwordDetectionService.Callback); method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, long, @Nullable java.util.function.IntConsumer); - field public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_1 = 1; // 0x1 - field public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_2 = 2; // 0x2 field public static final int INITIALIZATION_STATUS_SUCCESS = 0; // 0x0 field public static final int INITIALIZATION_STATUS_UNKNOWN = 100; // 0x64 field public static final String SERVICE_INTERFACE = "android.service.voice.HotwordDetectionService"; } public static final class HotwordDetectionService.Callback { - method public void onDetected(@Nullable android.service.voice.HotwordDetectedResult); - method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult); + method public void onDetected(@NonNull android.service.voice.HotwordDetectedResult); + method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult); } public interface HotwordDetector { @@ -10576,7 +10576,7 @@ package android.service.voice { method public void onHotwordDetectionServiceRestarted(); method public void onRecognitionPaused(); method public void onRecognitionResumed(); - method public void onRejected(@Nullable android.service.voice.HotwordRejectedResult); + method public void onRejected(@NonNull android.service.voice.HotwordRejectedResult); } public final class HotwordRejectedResult implements android.os.Parcelable { diff --git a/core/java/Android.bp b/core/java/Android.bp index 793d0ef164fa..6c001f305ce7 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -169,6 +169,7 @@ filegroup { filegroup { name: "framework-jobscheduler-shared-srcs", srcs: [ + ":modules-utils-preconditions-srcs", "com/android/internal/util/ArrayUtils.java", "com/android/internal/util/BitUtils.java", "com/android/internal/util/CollectionUtils.java", @@ -178,7 +179,6 @@ filegroup { "com/android/internal/util/FastXmlSerializer.java", "com/android/internal/util/FunctionalUtils.java", "com/android/internal/util/ParseUtils.java", - "com/android/internal/util/Preconditions.java", "com/android/internal/util/RingBufferIndices.java", "com/android/internal/util/StatLogger.java", "com/android/internal/util/XmlUtils.java", @@ -189,9 +189,9 @@ filegroup { filegroup { name: "framework-permission-s-shared-srcs", srcs: [ + ":modules-utils-preconditions-srcs", "com/android/internal/infra/AndroidFuture.java", "com/android/internal/infra/ServiceConnector.java", - "com/android/internal/util/Preconditions.java", "com/android/internal/infra/AndroidFuture.aidl", "com/android/internal/infra/IAndroidFuture.aidl", "android/os/HandlerExecutor.java", @@ -323,6 +323,7 @@ aidl_interface { filegroup { name: "framework-telephony-common-shared-srcs", srcs: [ + ":modules-utils-preconditions-srcs", "android/os/RegistrantList.java", "android/os/Registrant.java", "android/util/IndentingPrintWriter.java", @@ -336,7 +337,6 @@ filegroup { "com/android/internal/util/HexDump.java", "com/android/internal/util/IState.java", "com/android/internal/util/IndentingPrintWriter.java", - "com/android/internal/util/Preconditions.java", "com/android/internal/util/State.java", "com/android/internal/util/StateMachine.java", "com/android/internal/util/UserIcons.java", @@ -348,10 +348,10 @@ filegroup { filegroup { name: "framework-cellbroadcast-shared-srcs", srcs: [ + ":modules-utils-preconditions-srcs", "android/os/HandlerExecutor.java", "android/util/LocalLog.java", "com/android/internal/util/IState.java", - "com/android/internal/util/Preconditions.java", "com/android/internal/util/State.java", "com/android/internal/util/StateMachine.java", ], @@ -360,10 +360,10 @@ filegroup { filegroup { name: "framework-ims-common-shared-srcs", srcs: [ + ":modules-utils-preconditions-srcs", "android/os/RegistrantList.java", "android/os/Registrant.java", "com/android/internal/os/SomeArgs.java", - "com/android/internal/util/Preconditions.java", ], } diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index 01ea0267729c..04c784ea1c17 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -373,7 +373,6 @@ public class AccessibilityServiceInfo implements Parcelable { * #FLAG_REQUEST_MULTI_FINGER_GESTURES} is disabled this flag has no effect. * * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE - * @hide */ public static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x0002000; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 198fa651695c..295943da1262 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -136,6 +136,7 @@ import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; import android.view.translation.TranslationSpec; import android.view.translation.UiTranslationController; +import android.view.translation.UiTranslationSpec; import android.widget.AdapterView; import android.widget.Toast; import android.widget.Toolbar; @@ -8815,11 +8816,13 @@ public class Activity extends ContextThemeWrapper * @hide */ public void updateUiTranslationState(int state, TranslationSpec sourceSpec, - TranslationSpec targetSpec, List<AutofillId> viewIds) { + TranslationSpec targetSpec, List<AutofillId> viewIds, + UiTranslationSpec uiTranslationSpec) { if (mUiTranslationController == null) { mUiTranslationController = new UiTranslationController(this, getApplicationContext()); } - mUiTranslationController.updateUiTranslationState(state, sourceSpec, targetSpec, viewIds); + mUiTranslationController.updateUiTranslationState( + state, sourceSpec, targetSpec, viewIds, uiTranslationSpec); } class HostCallbacks extends FragmentHostCallback<Activity> { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index a6677674a679..3ebf545248c7 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -178,6 +178,7 @@ import android.view.autofill.AutofillId; import android.view.contentcapture.IContentCaptureManager; import android.view.contentcapture.IContentCaptureOptionsCallback; import android.view.translation.TranslationSpec; +import android.view.translation.UiTranslationSpec; import android.webkit.WebView; import android.window.SizeConfigurationBuckets; import android.window.SplashScreen; @@ -1843,13 +1844,15 @@ public final class ActivityThread extends ClientTransactionHandler @Override public void updateUiTranslationState(IBinder activityToken, int state, - TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds) { + TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds, + UiTranslationSpec uiTranslationSpec) { SomeArgs args = SomeArgs.obtain(); args.arg1 = activityToken; args.arg2 = state; args.arg3 = sourceSpec; args.arg4 = targetSpec; args.arg5 = viewIds; + args.arg6 = uiTranslationSpec; sendMessage(H.UPDATE_UI_TRANSLATION_STATE, args); } } @@ -2212,7 +2215,7 @@ public final class ActivityThread extends ClientTransactionHandler final SomeArgs args = (SomeArgs) msg.obj; updateUiTranslationState((IBinder) args.arg1, (int) args.arg2, (TranslationSpec) args.arg3, (TranslationSpec) args.arg4, - (List<AutofillId>) args.arg5); + (List<AutofillId>) args.arg5, (UiTranslationSpec) args.arg6); break; case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK: handleSetContentCaptureOptionsCallback((String) msg.obj); @@ -4194,13 +4197,15 @@ public final class ActivityThread extends ClientTransactionHandler } private void updateUiTranslationState(IBinder activityToken, int state, - TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds) { + TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds, + UiTranslationSpec uiTranslationSpec) { final ActivityClientRecord r = mActivities.get(activityToken); if (r == null) { Log.w(TAG, "updateUiTranslationState(): no activity for " + activityToken); return; } - r.activity.updateUiTranslationState(state, sourceSpec, targetSpec, viewIds); + r.activity.updateUiTranslationState( + state, sourceSpec, targetSpec, viewIds, uiTranslationSpec); } private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>(); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 918309e8e735..4555c1725a56 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -46,6 +46,7 @@ import android.os.RemoteCallback; import android.os.SharedMemory; import android.view.autofill.AutofillId; import android.view.translation.TranslationSpec; +import android.view.translation.UiTranslationSpec; import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.ReferrerIntent; @@ -160,5 +161,6 @@ oneway interface IApplicationThread { IUiAutomationConnection instrumentationUiConnection, in ApplicationInfo targetInfo); void updateUiTranslationState(IBinder activityToken, int state, in TranslationSpec sourceSpec, - in TranslationSpec targetSpec, in List<AutofillId> viewIds); + in TranslationSpec targetSpec, in List<AutofillId> viewIds, + in UiTranslationSpec uiTranslationSpec); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index c6847aac0cea..fc2c6ac7301e 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -174,6 +174,7 @@ public class Notification implements Parcelable */ public static final @ServiceNotificationPolicy int FOREGROUND_SERVICE_DEFERRED = 2; + @ServiceNotificationPolicy private int mFgsDeferBehavior; /** @@ -4614,9 +4615,9 @@ public class Notification implements Parcelable * foreground service. By default, the system can choose to defer * visibility of the notification for a short time after the service is * started. Pass - * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE BEHAVIOR_IMMEDIATE_DISPLAY} + * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE FOREGROUND_SERVICE_IMMEDIATE} * to this method in order to guarantee that visibility is never deferred. Pass - * {@link Notification#FOREGROUND_SERVICE_DEFERRED BEHAVIOR_DEFERRED_DISPLAY} + * {@link Notification#FOREGROUND_SERVICE_DEFERRED FOREGROUND_SERVICE_DEFERRED} * to request that visibility is deferred whenever possible. * * <p class="note">Note that deferred visibility is not guaranteed. There @@ -4624,13 +4625,13 @@ public class Notification implements Parcelable * service's associated Notification immediately even when the app has used * this method to explicitly request deferred display.</p> * @param behavior One of - * {@link Notification#FOREGROUND_SERVICE_DEFAULT BEHAVIOR_DEFAULT}, - * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE BEHAVIOR_IMMEDIATE_DISPLAY}, - * or {@link Notification#FOREGROUND_SERVICE_DEFERRED BEHAVIOR_DEFERRED_DISPLAY} + * {@link Notification#FOREGROUND_SERVICE_DEFAULT FOREGROUND_SERVICE_DEFAULT}, + * {@link Notification#FOREGROUND_SERVICE_IMMEDIATE FOREGROUND_SERVICE_IMMEDIATE}, + * or {@link Notification#FOREGROUND_SERVICE_DEFERRED FOREGROUND_SERVICE_DEFERRED} * @return */ @NonNull - public Builder setForegroundServiceBehavior(int behavior) { + public Builder setForegroundServiceBehavior(@ServiceNotificationPolicy int behavior) { mN.mFgsDeferBehavior = behavior; return this; } @@ -5309,8 +5310,7 @@ public class Notification implements Parcelable // the change's state in NotificationManagerService were very complex. These behavior // changes are entirely visual, and should otherwise be undetectable by apps. @SuppressWarnings("AndroidFrameworkCompatChange") - private void calculateLargeIconDimens(boolean largeIconShown, - @NonNull StandardTemplateParams p, + private void calculateRightIconDimens(Icon rightIcon, boolean isPromotedPicture, @NonNull TemplateBindResult result) { final Resources resources = mContext.getResources(); final float density = resources.getDisplayMetrics().density; @@ -5323,9 +5323,9 @@ public class Notification implements Parcelable final float viewHeightDp = resources.getDimension( R.dimen.notification_right_icon_size) / density; float viewWidthDp = viewHeightDp; // icons are 1:1 by default - if (largeIconShown && (p.mPromotePicture + if (rightIcon != null && (isPromotedPicture || mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S)) { - Drawable drawable = mN.mLargeIcon.loadDrawable(mContext); + Drawable drawable = rightIcon.loadDrawable(mContext); if (drawable != null) { int iconWidth = drawable.getIntrinsicWidth(); int iconHeight = drawable.getIntrinsicHeight(); @@ -5337,8 +5337,8 @@ public class Notification implements Parcelable } } final float extraMarginEndDpIfVisible = viewWidthDp + iconMarginDp; - result.setRightIconState(largeIconShown, viewWidthDp, - extraMarginEndDpIfVisible, expanderSizeDp); + result.setRightIconState(rightIcon != null /* visible */, viewWidthDp, + viewHeightDp, extraMarginEndDpIfVisible, expanderSizeDp); } /** @@ -5349,19 +5349,45 @@ public class Notification implements Parcelable if (mN.mLargeIcon == null && mN.largeIcon != null) { mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon); } - boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon; - calculateLargeIconDimens(showLargeIcon, p, result); - if (showLargeIcon) { + + // Determine the left and right icons + Icon leftIcon = p.mHideLeftIcon ? null : mN.mLargeIcon; + Icon rightIcon = p.mHideRightIcon ? null + : (p.mPromotedPicture != null ? p.mPromotedPicture : mN.mLargeIcon); + + // Apply the left icon (without duplicating the bitmap) + if (leftIcon != rightIcon || leftIcon == null) { + // If the leftIcon is explicitly hidden or different from the rightIcon, then set it + // explicitly and make sure it won't take the right_icon drawable. + contentView.setImageViewIcon(R.id.left_icon, leftIcon); + contentView.setIntTag(R.id.left_icon, R.id.tag_uses_right_icon_drawable, 0); + } else { + // If the leftIcon equals the rightIcon, just set the flag to use the right_icon + // drawable. This avoids the view having two copies of the same bitmap. + contentView.setIntTag(R.id.left_icon, R.id.tag_uses_right_icon_drawable, 1); + } + + // Always calculate dimens to populate `result` for the GONE case + boolean isPromotedPicture = p.mPromotedPicture != null; + calculateRightIconDimens(rightIcon, isPromotedPicture, result); + + // Bind the right icon + if (rightIcon != null) { contentView.setViewLayoutWidth(R.id.right_icon, result.mRightIconWidthDp, TypedValue.COMPLEX_UNIT_DIP); + contentView.setViewLayoutHeight(R.id.right_icon, + result.mRightIconHeightDp, TypedValue.COMPLEX_UNIT_DIP); contentView.setViewVisibility(R.id.right_icon, View.VISIBLE); - contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon); - processLargeLegacyIcon(mN.mLargeIcon, contentView, p); + contentView.setImageViewIcon(R.id.right_icon, rightIcon); + contentView.setIntTag(R.id.right_icon, R.id.tag_keep_when_showing_left_icon, + isPromotedPicture ? 1 : 0); + processLargeLegacyIcon(rightIcon, contentView, p); } else { // The "reset" doesn't clear the drawable, so we do it here. This clear is // important because the presence of a drawable in this view (regardless of the // visibility) is used by NotificationGroupingUtil to set the visibility. contentView.setImageViewIcon(R.id.right_icon, null); + contentView.setIntTag(R.id.right_icon, R.id.tag_keep_when_showing_left_icon, 0); } } @@ -6048,11 +6074,13 @@ public class Notification implements Parcelable .viewType(StandardTemplateParams.VIEW_TYPE_MINIMIZED) .highlightExpander(false) .fillTextsFrom(this); - if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) { + if (!useRegularSubtext || TextUtils.isEmpty(p.summaryText)) { p.summaryText(createSummaryText()); } RemoteViews header = makeNotificationHeader(p); header.setBoolean(R.id.notification_header, "setAcceptAllTouches", true); + // The low priority header has no app name and shows the text + header.setBoolean(R.id.notification_header, "styleTextAsTitle", true); return header; } @@ -7397,25 +7425,11 @@ public class Notification implements Parcelable return super.makeContentView(increasedHeight); } - Icon oldLargeIcon = mBuilder.mN.mLargeIcon; - mBuilder.mN.mLargeIcon = mPictureIcon; - // The legacy largeIcon might not allow us to clear the image, as it's taken in - // replacement if the other one is null. Because we're restoring these legacy icons - // for old listeners, this is in general non-null. - Bitmap largeIconLegacy = mBuilder.mN.largeIcon; - mBuilder.mN.largeIcon = null; - StandardTemplateParams p = mBuilder.mParams.reset() .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL) .fillTextsFrom(mBuilder) - .promotePicture(true); - RemoteViews contentView = getStandardView(mBuilder.getBaseLayoutResource(), - p, null /* result */); - - mBuilder.mN.mLargeIcon = oldLargeIcon; - mBuilder.mN.largeIcon = largeIconLegacy; - - return contentView; + .promotedPicture(mPictureIcon); + return getStandardView(mBuilder.getBaseLayoutResource(), p, null /* result */); } /** @@ -7427,25 +7441,11 @@ public class Notification implements Parcelable return super.makeHeadsUpContentView(increasedHeight); } - Icon oldLargeIcon = mBuilder.mN.mLargeIcon; - mBuilder.mN.mLargeIcon = mPictureIcon; - // The legacy largeIcon might not allow us to clear the image, as it's taken in - // replacement if the other one is null. Because we're restoring these legacy icons - // for old listeners, this is in general non-null. - Bitmap largeIconLegacy = mBuilder.mN.largeIcon; - mBuilder.mN.largeIcon = null; - StandardTemplateParams p = mBuilder.mParams.reset() .viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP) .fillTextsFrom(mBuilder) - .promotePicture(true); - RemoteViews contentView = getStandardView(mBuilder.getHeadsUpBaseLayoutResource(), - p, null /* result */); - - mBuilder.mN.mLargeIcon = oldLargeIcon; - mBuilder.mN.largeIcon = largeIconLegacy; - - return contentView; + .promotedPicture(mPictureIcon); + return getStandardView(mBuilder.getHeadsUpBaseLayoutResource(), p, null /* result */); } /** @@ -7540,14 +7540,21 @@ public class Notification implements Parcelable mShowBigPictureWhenCollapsed = extras.getBoolean(EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED); + mPictureIcon = getPictureIcon(extras); + } + + /** @hide */ + @Nullable + public static Icon getPictureIcon(@Nullable Bundle extras) { + if (extras == null) return null; // When this style adds a picture, we only add one of the keys. If both were added, // it would most likely be a legacy app trying to override the picture in some way. // Because of that case it's better to give precedence to the legacy field. Bitmap bitmapPicture = extras.getParcelable(EXTRA_PICTURE); if (bitmapPicture != null) { - mPictureIcon = Icon.createWithBitmap(bitmapPicture); + return Icon.createWithBitmap(bitmapPicture); } else { - mPictureIcon = extras.getParcelable(EXTRA_PICTURE_ICON); + return extras.getParcelable(EXTRA_PICTURE_ICON); } } @@ -8323,7 +8330,8 @@ public class Notification implements Parcelable .hideProgress(true) .title(isHeaderless ? conversationTitle : null) .text(null) - .hideLargeIcon(hideRightIcons || isOneToOne) + .hideLeftIcon(isOneToOne) + .hideRightIcon(hideRightIcons || isOneToOne) .headerTextSecondary(isHeaderless ? null : conversationTitle); RemoteViews contentView = mBuilder.applyStandardTemplateWithActions( isConversationLayout @@ -9121,9 +9129,10 @@ public class Notification implements Parcelable StandardTemplateParams p = mBuilder.mParams.reset() .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL) - .hideTime(numActionsToShow > 1) // hide if actions wider than a large icon - .hideSubText(numActionsToShow > 1) // hide if actions wider than a large icon - .hideLargeIcon(numActionsToShow > 0) // large icon or actions; not both + .hideTime(numActionsToShow > 1) // hide if actions wider than a right icon + .hideSubText(numActionsToShow > 1) // hide if actions wider than a right icon + .hideLeftIcon(false) // allow large icon on left when grouped + .hideRightIcon(numActionsToShow > 0) // right icon or actions; not both .hideProgress(true) .fillTextsFrom(mBuilder); TemplateBindResult result = new TemplateBindResult(); @@ -9527,7 +9536,8 @@ public class Notification implements Parcelable .viewType(viewType) .callStyleActions(true) .allowTextWithProgress(true) - .hideLargeIcon(true) + .hideLeftIcon(true) + .hideRightIcon(true) .hideAppName(isCollapsed) .titleViewId(R.id.conversation_text) .title(title) @@ -12111,6 +12121,7 @@ public class Notification implements Parcelable private static class TemplateBindResult { boolean mRightIconVisible; float mRightIconWidthDp; + float mRightIconHeightDp; /** * The margin end that needs to be added to the heading so that it won't overlap @@ -12135,10 +12146,11 @@ public class Notification implements Parcelable */ public final MarginSet mTitleMarginSet = new MarginSet(); - public void setRightIconState(boolean visible, float widthDp, + public void setRightIconState(boolean visible, float widthDp, float heightDp, float marginEndDpIfVisible, float expanderSizeDp) { mRightIconVisible = visible; mRightIconWidthDp = widthDp; + mRightIconHeightDp = heightDp; mHeadingExtraMarginSet.setValues(0, marginEndDpIfVisible); mHeadingFullMarginSet.setValues(expanderSizeDp, marginEndDpIfVisible + expanderSizeDp); mTitleMarginSet.setValues(0, marginEndDpIfVisible + expanderSizeDp); @@ -12237,7 +12249,9 @@ public class Notification implements Parcelable boolean mHideActions; boolean mHideProgress; boolean mHideSnoozeButton; - boolean mPromotePicture; + boolean mHideLeftIcon; + boolean mHideRightIcon; + Icon mPromotedPicture; boolean mCallStyleActions; boolean mAllowTextWithProgress; int mTitleViewId; @@ -12247,7 +12261,6 @@ public class Notification implements Parcelable CharSequence headerTextSecondary; CharSequence summaryText; int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES; - boolean hideLargeIcon; boolean allowColorization = true; boolean mHighlightExpander = false; @@ -12261,7 +12274,9 @@ public class Notification implements Parcelable mHideActions = false; mHideProgress = false; mHideSnoozeButton = false; - mPromotePicture = false; + mHideLeftIcon = false; + mHideRightIcon = false; + mPromotedPicture = null; mCallStyleActions = false; mAllowTextWithProgress = false; mTitleViewId = R.id.title; @@ -12271,7 +12286,6 @@ public class Notification implements Parcelable summaryText = null; headerTextSecondary = null; maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES; - hideLargeIcon = false; allowColorization = true; mHighlightExpander = false; return this; @@ -12336,8 +12350,8 @@ public class Notification implements Parcelable return this; } - final StandardTemplateParams promotePicture(boolean promotePicture) { - this.mPromotePicture = promotePicture; + final StandardTemplateParams promotedPicture(Icon promotedPicture) { + this.mPromotedPicture = promotedPicture; return this; } @@ -12371,8 +12385,14 @@ public class Notification implements Parcelable return this; } - final StandardTemplateParams hideLargeIcon(boolean hideLargeIcon) { - this.hideLargeIcon = hideLargeIcon; + + final StandardTemplateParams hideLeftIcon(boolean hideLeftIcon) { + this.mHideLeftIcon = hideLeftIcon; + return this; + } + + final StandardTemplateParams hideRightIcon(boolean hideRightIcon) { + this.mHideRightIcon = hideRightIcon; return this; } @@ -12409,7 +12429,8 @@ public class Notification implements Parcelable // Minimally decorated custom views do not show certain pieces of chrome that have // always been shown when using DecoratedCustomViewStyle. boolean hideOtherFields = decorationType <= DECORATION_MINIMAL; - hideLargeIcon(hideOtherFields); + hideLeftIcon(false); // The left icon decoration is better than showing nothing. + hideRightIcon(hideOtherFields); hideProgress(hideOtherFields); hideActions(hideOtherFields); return this; diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 152de4496cac..2ed44ec648fb 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -78,6 +78,10 @@ public class AppWidgetHostView extends FrameLayout { static final int VIEW_MODE_ERROR = 2; static final int VIEW_MODE_DEFAULT = 3; + // Set of valid colors resources. + private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_neutral1_0; + private static final int LAST_RESOURCE_COLOR_ID = android.R.color.system_accent3_1000; + // When we're inflating the initialLayout for a AppWidget, we only allow // views that are allowed in RemoteViews. private static final LayoutInflater.Filter INFLATER_FILTER = @@ -97,6 +101,7 @@ public class AppWidgetHostView extends FrameLayout { private boolean mOnLightBackground; private SizeF mCurrentSize = null; private RemoteViews.ColorResources mColorResources = null; + private SparseIntArray mColorMapping = null; // Stores the last remote views last inflated. private RemoteViews mLastInflatedRemoteViews = null; private long mLastInflatedRemoteViewsId = -1; @@ -888,12 +893,29 @@ public class AppWidgetHostView extends FrameLayout { * {@link android.R.color#system_neutral1_500}. */ public void setColorResources(@NonNull SparseIntArray colorMapping) { - mColorResources = RemoteViews.ColorResources.create(mContext, colorMapping); + if (mColorMapping != null && isSameColorMapping(mColorMapping, colorMapping)) { + return; + } + mColorMapping = colorMapping.clone(); + mColorResources = RemoteViews.ColorResources.create(mContext, mColorMapping); mLayoutId = -1; mViewMode = VIEW_MODE_NOINIT; reapplyLastRemoteViews(); } + /** Check if, in the current context, the two color mappings are equivalent. */ + private boolean isSameColorMapping(SparseIntArray oldColors, SparseIntArray newColors) { + if (oldColors.size() != newColors.size()) { + return false; + } + for (int i = 0; i < oldColors.size(); i++) { + if (oldColors.valueAt(i) != newColors.get(oldColors.keyAt(i))) { + return false; + } + } + return true; + } + /** * Reset the dynamically overloaded resources, reverting to the default values for * all the colors. @@ -904,6 +926,7 @@ public class AppWidgetHostView extends FrameLayout { public void resetColorResources() { if (mColorResources != null) { mColorResources = null; + mColorMapping = null; mLayoutId = -1; mViewMode = VIEW_MODE_NOINIT; reapplyLastRemoteViews(); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index a1d419e82174..edf0e5753441 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4775,8 +4775,7 @@ public abstract class PackageManager { * @param flags Additional option flags to modify the data returned. * @return A {@link ServiceInfo} object containing information about the * service. - * @throws NameNotFoundException if a package with the given name cannot be - * found on the system. + * @throws NameNotFoundException if the component cannot be found on the system. */ @NonNull public abstract ServiceInfo getServiceInfo(@NonNull ComponentName component, diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING index aa26520ba0ca..1eb4504bf591 100644 --- a/core/java/android/content/pm/TEST_MAPPING +++ b/core/java/android/content/pm/TEST_MAPPING @@ -39,9 +39,6 @@ "include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest" } ] - }, - { - "name": "CtsPackageManagerBootTestCases" } ], "postsubmit": [ @@ -52,6 +49,9 @@ "include-filter": "android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert" } ] + }, + { + "name": "CtsPackageManagerBootTestCases" } ] } diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 2d58520a942e..dce3fefff285 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -190,6 +190,8 @@ public abstract class DisplayManagerInternal { * has a preference. * @param requestedModeId The preferred mode id for the top-most visible window that has a * preference. + * @param requestedMaxRefreshRate The preferred highest refresh rate for the top-most visible + * window that has a preference. * @param requestedMinimalPostProcessing The preferred minimal post processing setting for the * display. This is true when there is at least one visible window that wants minimal post * processng on. @@ -197,8 +199,8 @@ public abstract class DisplayManagerInternal { * prior to call to performTraversalInTransactionFromWindowManager. */ public abstract void setDisplayProperties(int displayId, boolean hasContent, - float requestedRefreshRate, int requestedModeId, boolean requestedMinimalPostProcessing, - boolean inTraversal); + float requestedRefreshRate, int requestedModeId, float requestedMaxRefreshRate, + boolean requestedMinimalPostProcessing, boolean inTraversal); /** * Applies an offset to the contents of a display, for example to avoid burn-in. diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java index d41c0b4fbdb3..caab15251f58 100644 --- a/core/java/android/net/vcn/VcnConfig.java +++ b/core/java/android/net/vcn/VcnConfig.java @@ -52,12 +52,17 @@ public final class VcnConfig implements Parcelable { private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs"; @NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs; + private static final String IS_TEST_MODE_PROFILE_KEY = "mIsTestModeProfile"; + private final boolean mIsTestModeProfile; + private VcnConfig( @NonNull String packageName, - @NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs) { + @NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs, + boolean isTestModeProfile) { mPackageName = packageName; mGatewayConnectionConfigs = Collections.unmodifiableSet(new ArraySet<>(gatewayConnectionConfigs)); + mIsTestModeProfile = isTestModeProfile; validate(); } @@ -77,6 +82,7 @@ public final class VcnConfig implements Parcelable { new ArraySet<>( PersistableBundleUtils.toList( gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new)); + mIsTestModeProfile = in.getBoolean(IS_TEST_MODE_PROFILE_KEY); validate(); } @@ -104,6 +110,15 @@ public final class VcnConfig implements Parcelable { } /** + * Returns whether or not this VcnConfig is restricted to test networks. + * + * @hide + */ + public boolean isTestModeProfile() { + return mIsTestModeProfile; + } + + /** * Serializes this object to a PersistableBundle. * * @hide @@ -119,13 +134,14 @@ public final class VcnConfig implements Parcelable { new ArrayList<>(mGatewayConnectionConfigs), VcnGatewayConnectionConfig::toPersistableBundle); result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle); + result.putBoolean(IS_TEST_MODE_PROFILE_KEY, mIsTestModeProfile); return result; } @Override public int hashCode() { - return Objects.hash(mPackageName, mGatewayConnectionConfigs); + return Objects.hash(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile); } @Override @@ -136,7 +152,8 @@ public final class VcnConfig implements Parcelable { final VcnConfig rhs = (VcnConfig) other; return mPackageName.equals(rhs.mPackageName) - && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs); + && mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs) + && mIsTestModeProfile == rhs.mIsTestModeProfile; } // Parcelable methods @@ -172,6 +189,8 @@ public final class VcnConfig implements Parcelable { @NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>(); + private boolean mIsTestModeProfile = false; + public Builder(@NonNull Context context) { Objects.requireNonNull(context, "context was null"); @@ -207,13 +226,29 @@ public final class VcnConfig implements Parcelable { } /** + * Restricts this VcnConfig to matching with test networks (only). + * + * <p>This method is for testing only, and must not be used by apps. Calling {@link + * VcnManager#setVcnConfig(ParcelUuid, VcnConfig)} with a VcnConfig where test-network usage + * is enabled will require the MANAGE_TEST_NETWORKS permission. + * + * @return this {@link Builder} instance, for chaining + * @hide + */ + @NonNull + public Builder setIsTestModeProfile() { + mIsTestModeProfile = true; + return this; + } + + /** * Builds and validates the VcnConfig. * * @return an immutable VcnConfig instance */ @NonNull public VcnConfig build() { - return new VcnConfig(mPackageName, mGatewayConnectionConfigs); + return new VcnConfig(mPackageName, mGatewayConnectionConfigs, mIsTestModeProfile); } } } diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index f02346b6331b..7eea0b1129cf 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -15,6 +15,8 @@ */ package android.net.vcn; +import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; + import static com.android.internal.annotations.VisibleForTesting.Visibility; import android.annotation.IntDef; @@ -438,6 +440,8 @@ public final class VcnGatewayConnectionConfig { * distinguish between VcnGatewayConnectionConfigs configured on a single {@link * VcnConfig}. This will be used as the identifier in VcnStatusCallback invocations. * @param tunnelConnectionParams the IKE tunnel connection configuration + * @throws IllegalArgumentException if the provided IkeTunnelConnectionParams is not + * configured to support MOBIKE * @see IkeTunnelConnectionParams * @see VcnManager.VcnStatusCallback#onGatewayConnectionError */ @@ -446,6 +450,10 @@ public final class VcnGatewayConnectionConfig { @NonNull IkeTunnelConnectionParams tunnelConnectionParams) { Objects.requireNonNull(gatewayConnectionName, "gatewayConnectionName was null"); Objects.requireNonNull(tunnelConnectionParams, "tunnelConnectionParams was null"); + if (!tunnelConnectionParams.getIkeSessionParams().hasIkeOption(IKE_OPTION_MOBIKE)) { + throw new IllegalArgumentException( + "MOBIKE must be configured for the provided IkeSessionParams"); + } mGatewayConnectionName = gatewayConnectionName; mTunnelConnectionParams = tunnelConnectionParams; diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java index 0e9ccf144c2e..1f1818420b56 100644 --- a/core/java/android/net/vcn/VcnTransportInfo.java +++ b/core/java/android/net/vcn/VcnTransportInfo.java @@ -16,23 +16,17 @@ package android.net.vcn; -import static android.net.NetworkCapabilities.REDACT_ALL; -import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; +import static android.net.NetworkCapabilities.REDACT_NONE; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; -import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; - import android.annotation.NonNull; import android.annotation.Nullable; -import android.net.NetworkCapabilities; import android.net.TransportInfo; import android.net.wifi.WifiInfo; import android.os.Parcel; import android.os.Parcelable; import android.telephony.SubscriptionManager; -import com.android.internal.annotations.VisibleForTesting; - import java.util.Objects; /** @@ -55,32 +49,17 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { @Nullable private final WifiInfo mWifiInfo; private final int mSubId; - /** - * The redaction scheme to use when parcelling. - * - * <p>The TransportInfo/NetworkCapabilities redaction mechanisms rely on redaction being - * performed at parcelling time. This means that the redaction scheme must be stored for later - * use. - * - * <p>Since the redaction scheme itself is not parcelled, this field is listed as a transient. - * - * <p>Defaults to REDACT_ALL when constructed using public constructors, or creating from - * parcels. - */ - private final transient long mRedactions; - public VcnTransportInfo(@NonNull WifiInfo wifiInfo) { - this(wifiInfo, INVALID_SUBSCRIPTION_ID, REDACT_ALL); + this(wifiInfo, INVALID_SUBSCRIPTION_ID); } public VcnTransportInfo(int subId) { - this(null /* wifiInfo */, subId, REDACT_ALL); + this(null /* wifiInfo */, subId); } - private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId, long redactions) { + private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId) { mWifiInfo = wifiInfo; mSubId = subId; - mRedactions = redactions; } /** @@ -102,25 +81,14 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { * SubscriptionManager#INVALID_SUBSCRIPTION_ID}. * * @return the Subscription ID if a cellular underlying Network is present, else {@link - * android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID}. + * android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID}. */ public int getSubId() { return mSubId; } - /** - * Gets the redaction scheme - * - * @hide - */ - @VisibleForTesting(visibility = PRIVATE) - public long getRedaction() { - return mRedactions; - } - @Override public int hashCode() { - // mRedactions not hashed, as it is a transient, for control of parcelling return Objects.hash(mWifiInfo, mSubId); } @@ -128,8 +96,6 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { public boolean equals(Object o) { if (!(o instanceof VcnTransportInfo)) return false; final VcnTransportInfo that = (VcnTransportInfo) o; - - // mRedactions not compared, as it is a transient, for control of parcelling return Objects.equals(mWifiInfo, that.mWifiInfo) && mSubId == that.mSubId; } @@ -143,31 +109,19 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { @NonNull public TransportInfo makeCopy(long redactions) { return new VcnTransportInfo( - mWifiInfo == null ? null : mWifiInfo.makeCopy(redactions), mSubId, redactions); + (mWifiInfo == null) ? null : mWifiInfo.makeCopy(redactions), mSubId); } @Override public long getApplicableRedactions() { - long redactions = REDACT_FOR_NETWORK_SETTINGS; - - // Add additional wifi redactions if necessary - if (mWifiInfo != null) { - redactions |= mWifiInfo.getApplicableRedactions(); - } - - return redactions; - } - - private boolean shouldParcelNetworkSettingsFields() { - return (mRedactions & NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS) == 0; + return (mWifiInfo == null) ? REDACT_NONE : mWifiInfo.getApplicableRedactions(); } /** {@inheritDoc} */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(shouldParcelNetworkSettingsFields() ? mSubId : INVALID_SUBSCRIPTION_ID); - dest.writeParcelable( - shouldParcelNetworkSettingsFields() ? (Parcelable) mWifiInfo : null, flags); + dest.writeInt(mSubId); + dest.writeParcelable(mWifiInfo, flags); } @Override @@ -181,17 +135,7 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { public VcnTransportInfo createFromParcel(Parcel in) { final int subId = in.readInt(); final WifiInfo wifiInfo = in.readParcelable(null); - - // If all fields are their null values, return null TransportInfo to avoid - // leaking information about this being a VCN Network (instead of macro - // cellular, etc) - if (wifiInfo == null && subId == INVALID_SUBSCRIPTION_ID) { - return null; - } - - // Prevent further forwarding by redacting everything in future parcels from - // this VcnTransportInfo - return new VcnTransportInfo(wifiInfo, subId, REDACT_ALL); + return new VcnTransportInfo(wifiInfo, subId); } public VcnTransportInfo[] newArray(int size) { diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 32256677c7f1..8f366636ed9f 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -75,8 +75,10 @@ public final class BatteryUsageStats implements Parcelable { public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT = 2; private final int mDischargePercentage; - private final long mStatsStartTimestampMs; private final double mBatteryCapacityMah; + private final long mStatsStartTimestampMs; + private final long mStatsEndTimestampMs; + private final long mStatsDurationMs; private final double mDischargedPowerLowerBound; private final double mDischargedPowerUpperBound; private final long mBatteryTimeRemainingMs; @@ -90,6 +92,12 @@ public final class BatteryUsageStats implements Parcelable { private BatteryUsageStats(@NonNull Builder builder) { mStatsStartTimestampMs = builder.mStatsStartTimestampMs; + mStatsEndTimestampMs = builder.mStatsEndTimestampMs; + if (builder.mStatsDurationMs != -1) { + mStatsDurationMs = builder.mStatsDurationMs; + } else { + mStatsDurationMs = mStatsEndTimestampMs - mStatsStartTimestampMs; + } mBatteryCapacityMah = builder.mBatteryCapacityMah; mDischargePercentage = builder.mDischargePercentage; mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah; @@ -141,6 +149,24 @@ public final class BatteryUsageStats implements Parcelable { } /** + * Timestamp (as returned by System.currentTimeMillis()) of when the stats snapshot was taken, + * in milliseconds. + */ + public long getStatsEndTimestamp() { + return mStatsEndTimestampMs; + } + + /** + * Returns the duration of the stats session captured by this BatteryUsageStats. + * In rare cases, statsDuration != statsEndTimestamp - statsStartTimestamp. This may + * happen when BatteryUsageStats represents an accumulation of data across multiple + * non-contiguous sessions. + */ + public long getStatsDuration() { + return mStatsDurationMs; + } + + /** * Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully * charged), in mAh */ @@ -229,6 +255,8 @@ public final class BatteryUsageStats implements Parcelable { private BatteryUsageStats(@NonNull Parcel source) { mStatsStartTimestampMs = source.readLong(); + mStatsEndTimestampMs = source.readLong(); + mStatsDurationMs = source.readLong(); mBatteryCapacityMah = source.readDouble(); mDischargePercentage = source.readInt(); mDischargedPowerLowerBound = source.readDouble(); @@ -287,6 +315,8 @@ public final class BatteryUsageStats implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeLong(mStatsStartTimestampMs); + dest.writeLong(mStatsEndTimestampMs); + dest.writeLong(mStatsDurationMs); dest.writeDouble(mBatteryCapacityMah); dest.writeInt(mDischargePercentage); dest.writeDouble(mDischargedPowerLowerBound); @@ -441,6 +471,8 @@ public final class BatteryUsageStats implements Parcelable { private final String[] mCustomPowerComponentNames; private final boolean mIncludePowerModels; private long mStatsStartTimestampMs; + private long mStatsEndTimestampMs; + private long mStatsDurationMs = -1; private double mBatteryCapacityMah; private int mDischargePercentage; private double mDischargedPowerLowerBoundMah; @@ -494,6 +526,23 @@ public final class BatteryUsageStats implements Parcelable { } /** + * Sets the timestamp of when the battery stats snapshot was taken, in milliseconds. + */ + public Builder setStatsEndTimestamp(long statsEndTimestampMs) { + mStatsEndTimestampMs = statsEndTimestampMs; + return this; + } + + /** + * Sets the duration of the stats session. The default value of this field is + * statsEndTimestamp - statsStartTimestamp. + */ + public Builder setStatsDuration(long statsDurationMs) { + mStatsDurationMs = statsDurationMs; + return this; + } + + /** * Sets the battery discharge amount since BatteryStats reset as percentage of the full * charge. */ diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index b8ad068dc74c..326012d7142a 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -2026,6 +2026,9 @@ public class UserManager { case USER_TYPE_SYSTEM_HEADLESS: return FrameworkStatsLog .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__SYSTEM_HEADLESS; + case USER_TYPE_PROFILE_CLONE: + return FrameworkStatsLog + .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__PROFILE_CLONE; default: return FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN; } diff --git a/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java b/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java index f5851551d5c4..4f881b836cb3 100644 --- a/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java +++ b/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java @@ -17,7 +17,6 @@ package android.service.translation; import android.annotation.NonNull; -import android.annotation.Nullable; import android.os.DeadObjectException; import android.os.RemoteException; import android.util.Log; @@ -25,6 +24,7 @@ import android.view.translation.TranslationResponse; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; /** * Callback to receive the {@link TranslationResponse} on successful translation. @@ -32,13 +32,13 @@ import java.util.concurrent.atomic.AtomicBoolean; * @hide */ final class OnTranslationResultCallbackWrapper implements - TranslationService.OnTranslationResultCallback { + Consumer<TranslationResponse> { private static final String TAG = "OnTranslationResultCallback"; private final @NonNull ITranslationCallback mCallback; - private AtomicBoolean mCalled; + private final AtomicBoolean mCalled; /** * @hide @@ -49,7 +49,7 @@ final class OnTranslationResultCallbackWrapper implements } @Override - public void onTranslationSuccess(@Nullable TranslationResponse response) { + public void accept(TranslationResponse response) { assertNotCalled(); if (mCalled.getAndSet(response.isFinalResponse())) { throw new IllegalStateException("Already called with complete response"); @@ -66,15 +66,6 @@ final class OnTranslationResultCallbackWrapper implements } } - /** - * @deprecated use {@link #onTranslationSuccess} with error response instead. - */ - @Override - @Deprecated - public void onError() { - // no-op. - } - private void assertNotCalled() { if (mCalled.get()) { throw new IllegalStateException("Already called"); diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java index e7234ccc5dd2..24ebe454f728 100644 --- a/core/java/android/service/translation/TranslationService.java +++ b/core/java/android/service/translation/TranslationService.java @@ -125,7 +125,9 @@ public abstract class TranslationService extends Service { /** * Interface definition for a callback to be invoked when the translation is compleled. + * @deprecated use a {@link Consumer} instead. */ + @Deprecated public interface OnTranslationResultCallback { /** * Notifies the Android System that a translation request @@ -162,12 +164,12 @@ public abstract class TranslationService extends Service { public void onTranslationRequest(TranslationRequest request, int sessionId, ICancellationSignal transport, ITranslationCallback callback) throws RemoteException { - final OnTranslationResultCallback translationResultCallback = + final Consumer<TranslationResponse> consumer = new OnTranslationResultCallbackWrapper(callback); mHandler.sendMessage(obtainMessage(TranslationService::onTranslationRequest, TranslationService.this, request, sessionId, CancellationSignal.fromTransport(transport), - translationResultCallback)); + consumer)); } @Override @@ -242,8 +244,10 @@ public abstract class TranslationService extends Service { * instead. */ @Deprecated - public abstract void onCreateTranslationSession(@NonNull TranslationContext translationContext, - int sessionId); + public void onCreateTranslationSession(@NonNull TranslationContext translationContext, + int sessionId) { + // no-op + } /** * TODO: fill in javadoc. @@ -259,10 +263,45 @@ public abstract class TranslationService extends Service { * @param sessionId * @param callback * @param cancellationSignal + * @deprecated use + * {@link #onTranslationRequest(TranslationRequest, int, CancellationSignal, Consumer)} instead. + */ + @Deprecated + public void onTranslationRequest(@NonNull TranslationRequest request, int sessionId, + @Nullable CancellationSignal cancellationSignal, + @NonNull OnTranslationResultCallback callback) { + // no-op + } + + /** + * Called to the service with a {@link TranslationRequest} to be translated. + * + * <p>The service must call {@code callback.accept()} with the {@link TranslationResponse}. If + * {@link TranslationRequest#FLAG_PARTIAL_RESPONSES} was set, the service may call + * {@code callback.accept()} multiple times with partial responses.</p> + * + * @param request + * @param sessionId + * @param callback + * @param cancellationSignal */ - public abstract void onTranslationRequest(@NonNull TranslationRequest request, int sessionId, + //TODO: make abstract once aiai transitions. + public void onTranslationRequest(@NonNull TranslationRequest request, int sessionId, @Nullable CancellationSignal cancellationSignal, - @NonNull OnTranslationResultCallback callback); + @NonNull Consumer<TranslationResponse> callback) { + onTranslationRequest(request, sessionId, cancellationSignal, + new OnTranslationResultCallback() { + @Override + public void onTranslationSuccess(@NonNull TranslationResponse response) { + callback.accept(response); + } + + @Override + public void onError() { + // null-op + } + }); + } /** * TODO: fill in javadoc diff --git a/core/java/android/service/voice/AbstractHotwordDetector.java b/core/java/android/service/voice/AbstractHotwordDetector.java index 48967482fbdf..54ccf309a58e 100644 --- a/core/java/android/service/voice/AbstractHotwordDetector.java +++ b/core/java/android/service/voice/AbstractHotwordDetector.java @@ -55,10 +55,8 @@ abstract class AbstractHotwordDetector implements HotwordDetector { /** * Detect hotword from an externally supplied stream of data. * - * @return a writeable file descriptor that clients can start writing data in the given format. - * In order to stop detection, clients can close the given stream. + * @return true if the request to start recognition succeeded */ - @Nullable @Override public boolean startRecognition( @NonNull ParcelFileDescriptor audioStream, diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index bacc6ec2227b..fed28df964d6 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -49,7 +49,6 @@ import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.SharedMemory; -import android.service.voice.HotwordDetectionService.InitializationStatus; import android.util.Log; import android.util.Slog; @@ -524,16 +523,19 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { * @param result Info about the second stage detection result, provided by the * {@link HotwordDetectionService}. */ - public void onRejected(@Nullable HotwordRejectedResult result) { + public void onRejected(@NonNull HotwordRejectedResult result) { } /** * Called when the {@link HotwordDetectionService} is created by the system and given a * short amount of time to report it's initialization state. * - * @param status Info about initialization state of {@link HotwordDetectionService}. + * @param status Info about initialization state of {@link HotwordDetectionService}; the + * allowed values are {@link HotwordDetectionService#INITIALIZATION_STATUS_SUCCESS}, + * 1<->{@link HotwordDetectionService#getMaxCustomInitializationStatus()}, + * {@link HotwordDetectionService#INITIALIZATION_STATUS_UNKNOWN}. */ - public void onHotwordDetectionServiceInitialized(@InitializationStatus int status) { + public void onHotwordDetectionServiceInitialized(int status) { } /** @@ -1164,7 +1166,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { } @Override - public void onRejected(HotwordRejectedResult result) { + public void onRejected(@NonNull HotwordRejectedResult result) { if (DBG) { Slog.d(TAG, "onRejected(" + result + ")"); } else { diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java index 473e7ae76cc9..3960e3864e1f 100644 --- a/core/java/android/service/voice/HotwordDetectionService.java +++ b/core/java/android/service/voice/HotwordDetectionService.java @@ -16,6 +16,8 @@ package android.service.voice; +import static java.util.Objects.requireNonNull; + import android.annotation.DurationMillisLong; import android.annotation.IntDef; import android.annotation.NonNull; @@ -76,30 +78,17 @@ public abstract class HotwordDetectionService extends Service { /** @hide */ public static final String KEY_INITIALIZATION_STATUS = "initialization_status"; - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, prefix = { "INITIALIZATION_STATUS_" }, value = { - INITIALIZATION_STATUS_SUCCESS, - INITIALIZATION_STATUS_CUSTOM_ERROR_1, - INITIALIZATION_STATUS_CUSTOM_ERROR_2, - INITIALIZATION_STATUS_UNKNOWN, - }) - public @interface InitializationStatus {} - - /** - * Indicates that the updated status is successful. - */ - public static final int INITIALIZATION_STATUS_SUCCESS = 0; - /** - * Indicates that the updated status is failure for some application specific reasons. + * The maximum number of initialization status for some application specific failed reasons. + * + * @hide */ - public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_1 = 1; + public static final int MAXIMUM_NUMBER_OF_INITIALIZATION_STATUS_CUSTOM_ERROR = 2; /** - * Indicates that the updated status is failure for some application specific reasons. + * Indicates that the updated status is successful. */ - public static final int INITIALIZATION_STATUS_CUSTOM_ERROR_2 = 2; + public static final int INITIALIZATION_STATUS_SUCCESS = 0; /** * Indicates that the callback wasn’t invoked within the timeout. @@ -227,6 +216,19 @@ public abstract class HotwordDetectionService extends Service { } /** + * Returns the maximum number of initialization status for some application specific failed + * reasons. + * + * Note: The value 0 is reserved for success. + * + * @hide + */ + @SystemApi + public static int getMaxCustomInitializationStatus() { + return MAXIMUM_NUMBER_OF_INITIALIZATION_STATUS_CUSTOM_ERROR; + } + + /** * Called when the device hardware (such as a DSP) detected the hotword, to request second stage * validation before handing over the audio to the {@link AlwaysOnHotwordDetector}. * <p> @@ -296,9 +298,10 @@ public abstract class HotwordDetectionService extends Service { * such data to the trusted process. * @param callbackTimeoutMillis Timeout in milliseconds for the operation to invoke the * statusCallback. - * @param statusCallback Use this to return the updated result. This is non-null only when the - * {@link HotwordDetectionService} is being initialized; and it is null if the state is updated - * after that. + * @param statusCallback Use this to return the updated result; the allowed values are + * {@link #INITIALIZATION_STATUS_SUCCESS}, 1<->{@link #getMaxCustomInitializationStatus()}. + * This is non-null only when the {@link HotwordDetectionService} is being initialized; and it + * is null if the state is updated after that. * * @hide */ @@ -307,7 +310,7 @@ public abstract class HotwordDetectionService extends Service { @Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory, @DurationMillisLong long callbackTimeoutMillis, - @Nullable @InitializationStatus IntConsumer statusCallback) { + @Nullable IntConsumer statusCallback) { // TODO: Handle the unimplemented case by throwing? } @@ -387,6 +390,10 @@ public abstract class HotwordDetectionService extends Service { if (callback != null) { intConsumer = value -> { + if (value > getMaxCustomInitializationStatus()) { + throw new IllegalArgumentException( + "The initialization status is invalid for " + value); + } try { Bundle status = new Bundle(); status.putInt(KEY_INITIALIZATION_STATUS, value); @@ -414,11 +421,15 @@ public abstract class HotwordDetectionService extends Service { } /** - * Called when the detected result is valid. + * Informs the {@link HotwordDetector} that the keyphrase was detected. + * + * @param result Info about the detection result. This is provided to the + * {@link HotwordDetector}. */ - public void onDetected(@Nullable HotwordDetectedResult hotwordDetectedResult) { + public void onDetected(@NonNull HotwordDetectedResult result) { + requireNonNull(result); try { - mRemoteCallback.onDetected(hotwordDetectedResult); + mRemoteCallback.onDetected(result); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -433,7 +444,8 @@ public abstract class HotwordDetectionService extends Service { * @param result Info about the second stage detection result. This is provided to * the {@link HotwordDetector}. */ - public void onRejected(@Nullable HotwordRejectedResult result) { + public void onRejected(@NonNull HotwordRejectedResult result) { + requireNonNull(result); try { mRemoteCallback.onRejected(result); } catch (RemoteException e) { diff --git a/core/java/android/service/voice/HotwordDetector.java b/core/java/android/service/voice/HotwordDetector.java index 2fb4dbc835ed..b2f810aac09e 100644 --- a/core/java/android/service/voice/HotwordDetector.java +++ b/core/java/android/service/voice/HotwordDetector.java @@ -28,7 +28,6 @@ import android.media.AudioFormat; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.SharedMemory; -import android.service.voice.HotwordDetectionService.InitializationStatus; /** * Basic functionality for hotword detectors. @@ -160,15 +159,18 @@ public interface HotwordDetector { * @param result Info about the second stage detection result, provided by the * {@link HotwordDetectionService}. */ - void onRejected(@Nullable HotwordRejectedResult result); + void onRejected(@NonNull HotwordRejectedResult result); /** * Called when the {@link HotwordDetectionService} is created by the system and given a * short amount of time to report it's initialization state. * - * @param status Info about initialization state of {@link HotwordDetectionService}. + * @param status Info about initialization state of {@link HotwordDetectionService}; the + * allowed values are {@link HotwordDetectionService#INITIALIZATION_STATUS_SUCCESS}, + * 1<->{@link HotwordDetectionService#getMaxCustomInitializationStatus()}, + * {@link HotwordDetectionService#INITIALIZATION_STATUS_UNKNOWN}. */ - void onHotwordDetectionServiceInitialized(@InitializationStatus int status); + void onHotwordDetectionServiceInitialized(int status); /** * Called with the {@link HotwordDetectionService} is restarted. diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index 6c8753b95cc1..000c68503ea4 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -29,6 +29,7 @@ import android.os.Build; import android.util.AttributeSet; import android.widget.RelativeLayout; import android.widget.RemoteViews; +import android.widget.TextView; import com.android.internal.R; import com.android.internal.widget.CachingIconView; @@ -175,6 +176,28 @@ public class NotificationHeaderView extends RelativeLayout { } /** + * This is used to make the low-priority header show the bolded text of a title. + * + * @param styleTextAsTitle true if this header's text is to have the style of a title + */ + @RemotableViewMethod + public void styleTextAsTitle(boolean styleTextAsTitle) { + int styleResId = styleTextAsTitle + ? R.style.TextAppearance_DeviceDefault_Notification_Title + : R.style.TextAppearance_DeviceDefault_Notification_Info; + // Most of the time, we're showing text in the minimized state + View headerText = findViewById(R.id.header_text); + if (headerText instanceof TextView) { + ((TextView) headerText).setTextAppearance(styleResId); + } + // If there's no summary or text, we show the app name instead of nothing + View appNameText = findViewById(R.id.app_name_text); + if (appNameText instanceof TextView) { + ((TextView) appNameText).setTextAppearance(styleResId); + } + } + + /** * Get the current margin end value for the header text. * Add this to {@link #getTopLineBaseMarginEnd()} to get the total margin of the top line. * diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index c32ab3a2d717..c1e394d7456a 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -3010,6 +3010,14 @@ public interface WindowManager extends ViewManager { public int preferredDisplayModeId; /** + * The max display refresh rate while the window is in focus. + * + * This value is ignored if {@link #preferredDisplayModeId} is set. + * @hide + */ + public float preferredMaxDisplayRefreshRate; + + /** * An internal annotation for flags that can be specified to {@link #systemUiVisibility} * and {@link #subtreeSystemUiVisibility}. * diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 42985501d818..7eb840047c9f 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1519,17 +1519,25 @@ public final class InputMethodManager { } void setInputChannelLocked(InputChannel channel) { - if (mCurChannel != channel) { - if (mCurSender != null) { - flushPendingEventsLocked(); - mCurSender.dispose(); - mCurSender = null; - } - if (mCurChannel != null) { - mCurChannel.dispose(); - } - mCurChannel = channel; + if (mCurChannel == channel) { + return; + } + if (mCurChannel != null && channel != null + && mCurChannel.getToken() == channel.getToken()) { + // channel is a dupe of 'mCurChannel', because they have the same token, and represent + // the same connection. Ignore the incoming channel and keep using 'mCurChannel' to + // avoid confusing the InputEventReceiver. + return; + } + if (mCurSender != null) { + flushPendingEventsLocked(); + mCurSender.dispose(); + mCurSender = null; + } + if (mCurChannel != null) { + mCurChannel.dispose(); } + mCurChannel = channel; } /** diff --git a/core/java/android/view/translation/TranslationRequest.java b/core/java/android/view/translation/TranslationRequest.java index 1dc711b27ec2..7082e2b8193c 100644 --- a/core/java/android/view/translation/TranslationRequest.java +++ b/core/java/android/view/translation/TranslationRequest.java @@ -98,9 +98,26 @@ public final class TranslationRequest implements Parcelable { return Collections.emptyList(); } + abstract static class BaseBuilder { + /** + * @deprecated use {@link Builder#setTranslationRequestValues(List)}. + */ + @Deprecated + public abstract Builder addTranslationRequestValue( + @NonNull TranslationRequestValue value); + + /** + * @deprecated use {@link Builder#setViewTranslationRequests(List)}. + */ + @Deprecated + public abstract Builder addViewTranslationRequest( + @NonNull ViewTranslationRequest value); + + } + - // Code below generated by codegen v1.0.22. + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -275,7 +292,7 @@ public final class TranslationRequest implements Parcelable { */ @SuppressWarnings("WeakerAccess") @DataClass.Generated.Member - public static final class Builder { + public static final class Builder extends BaseBuilder { private @RequestFlags int mFlags; private @NonNull List<TranslationRequestValue> mTranslationRequestValues; @@ -312,6 +329,8 @@ public final class TranslationRequest implements Parcelable { /** @see #setTranslationRequestValues */ @DataClass.Generated.Member + @Override + @Deprecated public @NonNull Builder addTranslationRequestValue(@NonNull TranslationRequestValue value) { if (mTranslationRequestValues == null) setTranslationRequestValues(new ArrayList<>()); mTranslationRequestValues.add(value); @@ -333,6 +352,8 @@ public final class TranslationRequest implements Parcelable { /** @see #setViewTranslationRequests */ @DataClass.Generated.Member + @Override + @Deprecated public @NonNull Builder addViewTranslationRequest(@NonNull ViewTranslationRequest value) { if (mViewTranslationRequests == null) setViewTranslationRequests(new ArrayList<>()); mViewTranslationRequests.add(value); @@ -369,10 +390,10 @@ public final class TranslationRequest implements Parcelable { } @DataClass.Generated( - time = 1614132376448L, - codegenVersion = "1.0.22", + time = 1620429997487L, + codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/view/translation/TranslationRequest.java", - inputSignatures = "public static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_TRANSLATION_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_DICTIONARY_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_TRANSLITERATION_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_PARTIAL_RESPONSES\nprivate final @android.view.translation.TranslationRequest.RequestFlags int mFlags\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"translationRequestValue\") java.util.List<android.view.translation.TranslationRequestValue> mTranslationRequestValues\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"viewTranslationRequest\") java.util.List<android.view.translation.ViewTranslationRequest> mViewTranslationRequests\nprivate static int defaultFlags()\nprivate static java.util.List<android.view.translation.TranslationRequestValue> defaultTranslationRequestValues()\nprivate static java.util.List<android.view.translation.ViewTranslationRequest> defaultViewTranslationRequests()\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genBuilder=true)") + inputSignatures = "public static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_TRANSLATION_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_DICTIONARY_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_TRANSLITERATION_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_PARTIAL_RESPONSES\nprivate final @android.view.translation.TranslationRequest.RequestFlags int mFlags\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"translationRequestValue\") java.util.List<android.view.translation.TranslationRequestValue> mTranslationRequestValues\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"viewTranslationRequest\") java.util.List<android.view.translation.ViewTranslationRequest> mViewTranslationRequests\nprivate static int defaultFlags()\nprivate static java.util.List<android.view.translation.TranslationRequestValue> defaultTranslationRequestValues()\nprivate static java.util.List<android.view.translation.ViewTranslationRequest> defaultViewTranslationRequests()\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\npublic abstract @java.lang.Deprecated android.view.translation.TranslationRequest.Builder addTranslationRequestValue(android.view.translation.TranslationRequestValue)\npublic abstract @java.lang.Deprecated android.view.translation.TranslationRequest.Builder addViewTranslationRequest(android.view.translation.ViewTranslationRequest)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genBuilder=true)\npublic abstract @java.lang.Deprecated android.view.translation.TranslationRequest.Builder addTranslationRequestValue(android.view.translation.TranslationRequestValue)\npublic abstract @java.lang.Deprecated android.view.translation.TranslationRequest.Builder addViewTranslationRequest(android.view.translation.ViewTranslationRequest)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/translation/TranslationRequestValue.java b/core/java/android/view/translation/TranslationRequestValue.java index 0619618d8844..5178cb1e4f1f 100644 --- a/core/java/android/view/translation/TranslationRequestValue.java +++ b/core/java/android/view/translation/TranslationRequestValue.java @@ -46,22 +46,17 @@ public final class TranslationRequestValue implements Parcelable { } /** - * @return the text value as a {@link CharSequence}. - * - * @throws IllegalStateException if the format of this {@link TranslationRequestValue} is not a - * text value. + * @return the text value as a {@link CharSequence} or {@code null} if the value is not of type + * text. */ - @NonNull + @Nullable public CharSequence getText() { - if (mText == null) { - throw new IllegalStateException("Value is not of type text"); - } return mText; } - // Code below generated by codegen v1.0.22. + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -111,7 +106,7 @@ public final class TranslationRequestValue implements Parcelable { TranslationRequestValue that = (TranslationRequestValue) o; //noinspection PointlessBooleanExpression return true - && java.util.Objects.equals(mText, that.mText); + && Objects.equals(mText, that.mText); } @Override @@ -121,7 +116,7 @@ public final class TranslationRequestValue implements Parcelable { // int fieldNameHashCode() { ... } int _hash = 1; - _hash = 31 * _hash + java.util.Objects.hashCode(mText); + _hash = 31 * _hash + Objects.hashCode(mText); return _hash; } @@ -171,10 +166,10 @@ public final class TranslationRequestValue implements Parcelable { }; @DataClass.Generated( - time = 1613687761635L, - codegenVersion = "1.0.22", + time = 1620259864154L, + codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/view/translation/TranslationRequestValue.java", - inputSignatures = "private final @android.annotation.Nullable java.lang.CharSequence mText\npublic static @android.annotation.NonNull android.view.translation.TranslationRequestValue forText(java.lang.CharSequence)\npublic @android.annotation.NonNull java.lang.CharSequence getText()\nclass TranslationRequestValue extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genToString=true, genEqualsHashCode=true)") + inputSignatures = "private final @android.annotation.Nullable java.lang.CharSequence mText\npublic static @android.annotation.NonNull android.view.translation.TranslationRequestValue forText(java.lang.CharSequence)\npublic @android.annotation.Nullable java.lang.CharSequence getText()\nclass TranslationRequestValue extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genToString=true, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index e0526f87340a..9e350d9aa328 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -30,6 +30,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Process; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.IntArray; import android.util.Log; import android.util.LongSparseArray; @@ -77,6 +78,11 @@ public class UiTranslationController { private final ArrayMap<Pair<TranslationSpec, TranslationSpec>, Translator> mTranslators; @NonNull private final ArrayMap<AutofillId, WeakReference<View>> mViews; + /** + * Views for which {@link UiTranslationSpec#shouldPadContentForCompat()} is true. + */ + @NonNull + private final ArraySet<AutofillId> mViewsToPadContent; @NonNull private final HandlerThread mWorkerThread; @NonNull @@ -88,6 +94,7 @@ public class UiTranslationController { mContext = context; mViews = new ArrayMap<>(); mTranslators = new ArrayMap<>(); + mViewsToPadContent = new ArraySet<>(); mWorkerThread = new HandlerThread("UiTranslationController_" + mActivity.getComponentName(), @@ -100,19 +107,27 @@ public class UiTranslationController { * Update the Ui translation state. */ public void updateUiTranslationState(@UiTranslationState int state, TranslationSpec sourceSpec, - TranslationSpec targetSpec, List<AutofillId> views) { + TranslationSpec targetSpec, List<AutofillId> views, + UiTranslationSpec uiTranslationSpec) { if (!mActivity.isResumed() && (state == STATE_UI_TRANSLATION_STARTED || state == STATE_UI_TRANSLATION_RESUMED)) { return; } Log.i(TAG, "updateUiTranslationState state: " + stateToString(state) - + (DEBUG ? ", views: " + views : "")); + + (DEBUG ? (", views: " + views + ", spec: " + uiTranslationSpec) : "")); synchronized (mLock) { mCurrentState = state; } switch (state) { case STATE_UI_TRANSLATION_STARTED: + if (uiTranslationSpec != null && uiTranslationSpec.shouldPadContentForCompat()) { + synchronized (mLock) { + mViewsToPadContent.addAll(views); + // TODO: Cleanup disappeared views from mViews and mViewsToPadContent at + // some appropriate place. + } + } final Pair<TranslationSpec, TranslationSpec> specs = new Pair<>(sourceSpec, targetSpec); if (!mTranslators.containsKey(specs)) { @@ -177,6 +192,7 @@ public class UiTranslationController { pw.print(pfx); pw.print("autofillId: "); pw.println(autofillId); pw.print(pfx); pw.print("view:"); pw.println(view); } + pw.print(outerPrefix); pw.print("padded views: "); pw.println(mViewsToPadContent); } // TODO(b/182433547): we will remove debug rom condition before S release then we change // change this back to "DEBUG" @@ -374,8 +390,9 @@ public class UiTranslationController { return; } - // TODO: Do this for specific views on request only. - callback.enableContentPadding(); + if (mViewsToPadContent.contains(autofillId)) { + callback.enableContentPadding(); + } view.onViewTranslationResponse(response); callback.onShowTranslation(view); }); diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index 9eebf06d0674..786e6fce3af8 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -24,7 +24,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.ColorStateList; -import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.BlendMode; import android.graphics.Canvas; @@ -40,8 +39,9 @@ import android.widget.RemoteViews.RemoteView; import java.time.Clock; import java.time.DateTimeException; +import java.time.Duration; import java.time.Instant; -import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.ZoneId; import java.util.Formatter; import java.util.Locale; @@ -61,8 +61,8 @@ import java.util.Locale; @Deprecated public class AnalogClock extends View { private static final String LOG_TAG = "AnalogClock"; - /** How often the clock should refresh to make the seconds hand advance at ~15 FPS. */ - private static final long SECONDS_TICK_FREQUENCY_MS = 1000 / 15; + /** How many times per second that the seconds hand advances. */ + private static final long SECONDS_HAND_FPS = 30; private Clock mClock; @Nullable @@ -106,7 +106,6 @@ public class AnalogClock extends View { public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - final Resources r = context.getResources(); final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.AnalogClock, defStyleAttr, defStyleRes); saveAttributeDataForStyleable(context, com.android.internal.R.styleable.AnalogClock, @@ -716,25 +715,34 @@ public class AnalogClock extends View { } private void onTimeChanged() { - long nowMillis = mClock.millis(); - LocalDateTime localDateTime = toLocalDateTime(nowMillis, mClock.getZone()); - - int hour = localDateTime.getHour(); - int minute = localDateTime.getMinute(); - int second = localDateTime.getSecond(); - - mSeconds = second + localDateTime.getNano() / 1_000_000_000f; - mMinutes = minute + second / 60.0f; - mHour = hour + mMinutes / 60.0f; + Instant now = mClock.instant(); + onTimeChanged(now.atZone(mClock.getZone()).toLocalTime(), now.toEpochMilli()); + } + + private void onTimeChanged(LocalTime localTime, long nowMillis) { + float previousHour = mHour; + float previousMinutes = mMinutes; + + float rawSeconds = localTime.getSecond() + localTime.getNano() / 1_000_000_000f; + // We round the fraction of the second so that the seconds hand always occupies the same + // n positions between two given numbers, where n is the number of ticks per second. This + // ensures the second hand advances by a consistent distance despite our handler callbacks + // occurring at inconsistent frequencies. + mSeconds = Math.round(rawSeconds * SECONDS_HAND_FPS) / (float) SECONDS_HAND_FPS; + mMinutes = localTime.getMinute() + mSeconds / 60.0f; + mHour = localTime.getHour() + mMinutes / 60.0f; mChanged = true; - updateContentDescription(nowMillis); + // Update the content description only if the announced hours and minutes have changed. + if ((int) previousHour != (int) mHour || (int) previousMinutes != (int) mMinutes) { + updateContentDescription(nowMillis); + } } private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { + if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) { createClock(); } @@ -747,15 +755,32 @@ public class AnalogClock extends View { private final Runnable mSecondsTick = new Runnable() { @Override public void run() { + removeCallbacks(this); if (!mVisible || mSecondHand == null) { return; } - onTimeChanged(); + Instant now = mClock.instant(); + LocalTime localTime = now.atZone(mClock.getZone()).toLocalTime(); + // How many milliseconds through the second we currently are. + long millisOfSecond = Duration.ofNanos(localTime.getNano()).toMillis(); + // How many milliseconds there are between tick positions for the seconds hand. + double millisPerTick = 1000 / (double) SECONDS_HAND_FPS; + // How many milliseconds we are past the last tick position. + long millisPastLastTick = Math.round(millisOfSecond % millisPerTick); + // How many milliseconds there are until the next tick position. + long millisUntilNextTick = Math.round(millisPerTick - millisPastLastTick); + // If we are exactly at the tick position, this could be 0 milliseconds due to rounding. + // In this case, advance by the full amount of millis to the next position. + if (millisUntilNextTick <= 0) { + millisUntilNextTick = Math.round(millisPerTick); + } + // Schedule a callback for when the next tick should occur. + postDelayed(this, millisUntilNextTick); - invalidate(); + onTimeChanged(localTime, now.toEpochMilli()); - postDelayed(this, SECONDS_TICK_FREQUENCY_MS); + invalidate(); } }; @@ -782,14 +807,6 @@ public class AnalogClock extends View { setContentDescription(contentDescription); } - private static LocalDateTime toLocalDateTime(long timeMillis, ZoneId zoneId) { - // java.time types like LocalDateTime / Instant can support the full range of "long millis" - // with room to spare so we do not need to worry about overflow / underflow and the - // resulting exceptions while the input to this class is a long. - Instant instant = Instant.ofEpochMilli(timeMillis); - return LocalDateTime.ofInstant(instant, zoneId); - } - /** * Tries to parse a {@link ZoneId} from {@code timeZone}, returning null if it is null or there * is an error parsing. diff --git a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl index 1c7eca8b6c8e..87df5eb9e75a 100644 --- a/core/java/com/android/internal/graphics/fonts/IFontManager.aidl +++ b/core/java/com/android/internal/graphics/fonts/IFontManager.aidl @@ -30,7 +30,5 @@ import java.util.List; interface IFontManager { FontConfig getFontConfig(); - int updateFontFile(in FontUpdateRequest request, int baseVersion); - int updateFontFamily(in List<FontUpdateRequest> request, int baseVersion); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index db67bab25e78..8770a3ac3668 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -610,18 +610,32 @@ public class BatteryStatsImpl extends BatteryStats { } public interface Clocks { - public long elapsedRealtime(); - public long uptimeMillis(); + /** Elapsed Realtime, see SystemClock.elapsedRealtime() */ + long elapsedRealtime(); + + /** Uptime, see SystemClock.uptimeMillis() */ + long uptimeMillis(); + + /** Wall-clock time as per System.currentTimeMillis() */ + long currentTimeMillis(); } public static class SystemClocks implements Clocks { + + @Override public long elapsedRealtime() { return SystemClock.elapsedRealtime(); } + @Override public long uptimeMillis() { return SystemClock.uptimeMillis(); } + + @Override + public long currentTimeMillis() { + return System.currentTimeMillis(); + } } public interface ExternalStatsSync { @@ -1163,7 +1177,7 @@ public class BatteryStatsImpl extends BatteryStats { public BatteryStatsImpl(Clocks clocks) { init(clocks); - mStartClockTimeMs = System.currentTimeMillis(); + mStartClockTimeMs = clocks.currentTimeMillis(); mStatsFile = null; mCheckinFile = null; mDailyFile = null; @@ -3694,7 +3708,7 @@ public class BatteryStatsImpl extends BatteryStats { if (dataSize == 0) { // The history is currently empty; we need it to start with a time stamp. - cur.currentTime = System.currentTimeMillis(); + cur.currentTime = mClocks.currentTimeMillis(); addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_RESET, cur); } addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur); @@ -3914,7 +3928,7 @@ public class BatteryStatsImpl extends BatteryStats { } public void noteCurrentTimeChangedLocked() { - final long currentTime = System.currentTimeMillis(); + final long currentTime = mClocks.currentTimeMillis(); final long elapsedRealtime = mClocks.elapsedRealtime(); final long uptime = mClocks.uptimeMillis(); noteCurrentTimeChangedLocked(currentTime, elapsedRealtime, uptime); @@ -4237,7 +4251,7 @@ public class BatteryStatsImpl extends BatteryStats { if (mPretendScreenOff != pretendScreenOff) { mPretendScreenOff = pretendScreenOff; noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON, - mClocks.elapsedRealtime(), mClocks.uptimeMillis(), System.currentTimeMillis()); + mClocks.elapsedRealtime(), mClocks.uptimeMillis(), mClocks.currentTimeMillis()); } } @@ -4827,7 +4841,7 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") public void noteScreenStateLocked(int state) { noteScreenStateLocked(state, mClocks.elapsedRealtime(), mClocks.uptimeMillis(), - System.currentTimeMillis()); + mClocks.currentTimeMillis()); } @GuardedBy("this") @@ -6972,7 +6986,7 @@ public class BatteryStatsImpl extends BatteryStats { } @Override public long getStartClockTime() { - final long currentTimeMs = System.currentTimeMillis(); + final long currentTimeMs = mClocks.currentTimeMillis(); if ((currentTimeMs > MILLISECONDS_IN_YEAR && mStartClockTimeMs < (currentTimeMs - MILLISECONDS_IN_YEAR)) || (mStartClockTimeMs > currentTimeMs)) { @@ -10735,7 +10749,7 @@ public class BatteryStatsImpl extends BatteryStats { public void updateDailyDeadlineLocked() { // Get the current time. - long currentTimeMs = mDailyStartTimeMs = System.currentTimeMillis(); + long currentTimeMs = mDailyStartTimeMs = mClocks.currentTimeMillis(); Calendar calDeadline = Calendar.getInstance(); calDeadline.setTimeInMillis(currentTimeMs); @@ -10763,7 +10777,7 @@ public class BatteryStatsImpl extends BatteryStats { public void recordDailyStatsLocked() { DailyItem item = new DailyItem(); item.mStartTime = mDailyStartTimeMs; - item.mEndTime = System.currentTimeMillis(); + item.mEndTime = mClocks.currentTimeMillis(); boolean hasData = false; if (mDailyDischargeStepTracker.mNumStepDurations > 0) { hasData = true; @@ -11128,7 +11142,7 @@ public class BatteryStatsImpl extends BatteryStats { } void initTimes(long uptimeUs, long realtimeUs) { - mStartClockTimeMs = System.currentTimeMillis(); + mStartClockTimeMs = mClocks.currentTimeMillis(); mOnBatteryTimeBase.init(uptimeUs, realtimeUs); mOnBatteryScreenOffTimeBase.init(uptimeUs, realtimeUs); mRealtimeUs = 0; @@ -11194,7 +11208,7 @@ public class BatteryStatsImpl extends BatteryStats { final long elapsedRealtimeUs = elapsedRealtimeMillis * 1000; mStartCount = 0; initTimes(uptimeUs, elapsedRealtimeUs); - mScreenOnTimer.reset(false, uptimeUs); + mScreenOnTimer.reset(false, elapsedRealtimeUs); mScreenDozeTimer.reset(false, elapsedRealtimeUs); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i].reset(false, elapsedRealtimeUs); @@ -13562,7 +13576,7 @@ public class BatteryStatsImpl extends BatteryStats { private void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs, boolean reset) { mRecordingHistory = true; - mHistoryCur.currentTime = System.currentTimeMillis(); + mHistoryCur.currentTime = mClocks.currentTimeMillis(); addHistoryBufferLocked(elapsedRealtimeMs, reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME, mHistoryCur); @@ -13604,7 +13618,7 @@ public class BatteryStatsImpl extends BatteryStats { final int chargeFullUah, final long chargeTimeToFullSeconds) { setBatteryStateLocked(status, health, plugType, level, temp, voltageMv, chargeUah, chargeFullUah, chargeTimeToFullSeconds, - mClocks.elapsedRealtime(), mClocks.uptimeMillis(), System.currentTimeMillis()); + mClocks.elapsedRealtime(), mClocks.uptimeMillis(), mClocks.currentTimeMillis()); } public void setBatteryStateLocked(final int status, final int health, final int plugType, @@ -14389,7 +14403,7 @@ public class BatteryStatsImpl extends BatteryStats { } public void shutdownLocked() { - recordShutdownLocked(System.currentTimeMillis(), mClocks.elapsedRealtime()); + recordShutdownLocked(mClocks.currentTimeMillis(), mClocks.elapsedRealtime()); writeSyncLocked(); mShuttingDown = true; } @@ -14938,7 +14952,7 @@ public class BatteryStatsImpl extends BatteryStats { startRecordingHistory(elapsedRealtimeMs, uptimeMs, false); } - recordDailyStatsIfNeededLocked(false, System.currentTimeMillis()); + recordDailyStatsIfNeededLocked(false, mClocks.currentTimeMillis()); } public int describeContents() { diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index 498955983ef2..d4d61256a7a3 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -108,9 +108,9 @@ public class BatteryUsageStatsProvider { ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size()); synchronized (mStats) { mStats.prepareForDumpLocked(); - + final long currentTimeMillis = currentTimeMillis(); for (int i = 0; i < queries.size(); i++) { - results.add(getBatteryUsageStats(queries.get(i))); + results.add(getBatteryUsageStats(queries.get(i), currentTimeMillis)); } } return results; @@ -121,6 +121,11 @@ public class BatteryUsageStatsProvider { */ @VisibleForTesting public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) { + return getBatteryUsageStats(query, currentTimeMillis()); + } + + private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query, + long currentTimeMs) { final long realtimeUs = elapsedRealtime() * 1000; final long uptimeUs = uptimeMillis() * 1000; @@ -129,7 +134,10 @@ public class BatteryUsageStatsProvider { final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder( mStats.getCustomEnergyConsumerNames(), includePowerModels); + // TODO(b/188068523): use a monotonic clock to ensure resilience of order and duration + // of stats sessions to wall-clock adjustments batteryUsageStatsBuilder.setStatsStartTimestamp(mStats.getStartClockTime()); + batteryUsageStatsBuilder.setStatsEndTimestamp(currentTimeMs); SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats(); for (int i = uidStats.size() - 1; i >= 0; i--) { @@ -217,4 +225,12 @@ public class BatteryUsageStatsProvider { return SystemClock.uptimeMillis(); } } + + private long currentTimeMillis() { + if (mStats instanceof BatteryStatsImpl) { + return ((BatteryStatsImpl) mStats).mClocks.currentTimeMillis(); + } else { + return System.currentTimeMillis(); + } + } } diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index c297a1fc4279..57e1bf79622c 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -1127,11 +1127,10 @@ public class BinderCallsStats implements BinderInternal.Observer { private final int mProcessSource; public SettingsObserver(Context context, BinderCallsStats binderCallsStats, - int processSource, int userHandle) { + int processSource) { super(BackgroundThread.getHandler()); mContext = context; - context.getContentResolver().registerContentObserver(mUri, false, this, - userHandle); + context.getContentResolver().registerContentObserver(mUri, false, this); mBinderCallsStats = binderCallsStats; mProcessSource = processSource; // Always kick once to ensure that we match current state diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java index 6e99bbbf2331..c322258eda85 100644 --- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java +++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java @@ -51,7 +51,7 @@ public class BluetoothPowerCalculator extends PowerCalculator { @Override public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - if (!mHasBluetoothPowerController || !batteryStats.hasBluetoothActivityReporting()) { + if (!batteryStats.hasBluetoothActivityReporting()) { return; } @@ -69,8 +69,8 @@ public class BluetoothPowerCalculator extends PowerCalculator { final ControllerActivityCounter activityCounter = batteryStats.getBluetoothControllerActivity(); final long systemDurationMs = calculateDuration(activityCounter); - final double systemPowerMah = - calculatePowerMah(powerModel, measuredChargeUC, activityCounter); + final double systemPowerMah = calculatePowerMah(powerModel, measuredChargeUC, + activityCounter, query.shouldForceUsePowerProfileModel()); // Subtract what the apps used, but clamp to 0. final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs); @@ -100,7 +100,8 @@ public class BluetoothPowerCalculator extends PowerCalculator { final ControllerActivityCounter activityCounter = app.getBatteryStatsUid().getBluetoothControllerActivity(); final long durationMs = calculateDuration(activityCounter); - final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter); + final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter, + query.shouldForceUsePowerProfileModel()); app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah, powerModel); @@ -132,7 +133,7 @@ public class BluetoothPowerCalculator extends PowerCalculator { batteryStats.getBluetoothControllerActivity(); final long systemDurationMs = calculateDuration(activityCounter); final double systemPowerMah = - calculatePowerMah(powerModel, measuredChargeUC, activityCounter); + calculatePowerMah(powerModel, measuredChargeUC, activityCounter, false); // Subtract what the apps used, but clamp to 0. final double powerMah = Math.max(0, systemPowerMah - total.powerMah); @@ -165,7 +166,8 @@ public class BluetoothPowerCalculator extends PowerCalculator { final int powerModel = getPowerModel(measuredChargeUC); final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity(); final long durationMs = calculateDuration(activityCounter); - final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter); + final double powerMah = calculatePowerMah(powerModel, measuredChargeUC, activityCounter, + false); app.bluetoothRunningTimeMs = durationMs; app.bluetoothPowerMah = powerMah; @@ -188,7 +190,7 @@ public class BluetoothPowerCalculator extends PowerCalculator { /** Returns bluetooth power usage based on the best data available. */ private double calculatePowerMah(@BatteryConsumer.PowerModel int powerModel, - long measuredChargeUC, ControllerActivityCounter counter) { + long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower) { if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) { return uCtoMah(measuredChargeUC); } @@ -197,12 +199,17 @@ public class BluetoothPowerCalculator extends PowerCalculator { return 0; } - final double powerMah = - counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED) - / (double) (1000 * 60 * 60); + if (!ignoreReportedPower) { + final double powerMah = + counter.getPowerCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED) + / (double) (1000 * 60 * 60); + if (powerMah != 0) { + return powerMah; + } + } - if (powerMah != 0) { - return powerMah; + if (!mHasBluetoothPowerController) { + return 0; } final long idleTimeMs = diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java index e670178d05ae..50df166605db 100644 --- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java +++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java @@ -477,18 +477,17 @@ public abstract class KernelCpuUidTimeReader<T> { } copyToCurTimes(); boolean notify = false; - boolean valid = true; for (int i = 0; i < mFreqCount; i++) { // Unit is 10ms. mDeltaTimes[i] = mCurTimes[i] - lastTimes[i]; if (mDeltaTimes[i] < 0) { Slog.e(mTag, "Negative delta from freq time for uid: " + uid + ", delta: " + mDeltaTimes[i]); - valid = false; + return; } notify |= mDeltaTimes[i] > 0; } - if (notify && valid) { + if (notify) { System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount); if (cb != null) { cb.onUidCpuTime(uid, mDeltaTimes); @@ -826,11 +825,11 @@ public abstract class KernelCpuUidTimeReader<T> { if (mDeltaTime[i] < 0) { Slog.e(mTag, "Negative delta from cluster time for uid: " + uid + ", delta: " + mDeltaTime[i]); - valid = false; + return; } notify |= mDeltaTime[i] > 0; } - if (notify && valid) { + if (notify) { System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters); if (cb != null) { cb.onUidCpuTime(uid, mDeltaTime); diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java index d5941072acf9..e0ef1291800d 100644 --- a/core/java/com/android/internal/os/WakelockPowerCalculator.java +++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java @@ -75,22 +75,29 @@ public class WakelockPowerCalculator extends PowerCalculator { // this remainder to the OS, if possible. calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs, BatteryStats.STATS_SINCE_CHARGED, osPowerMah, osDurationMs, totalAppDurationMs); + final double remainingPowerMah = result.powerMah; if (osBatteryConsumer != null) { osBatteryConsumer.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.durationMs) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah); + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, remainingPowerMah); } - final long wakeTimeMillis = - calculateWakeTimeMillis(batteryStats, rawRealtimeUs, rawUptimeUs); - final double powerMah = mPowerEstimator.calculatePower(wakeTimeMillis); + long wakeTimeMs = calculateWakeTimeMillis(batteryStats, rawRealtimeUs, rawUptimeUs); + if (wakeTimeMs < 0) { + wakeTimeMs = 0; + } builder.getAggregateBatteryConsumerBuilder( BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) - .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, wakeTimeMillis) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, powerMah); + .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, + wakeTimeMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, + appPowerMah + remainingPowerMah); builder.getAggregateBatteryConsumerBuilder( BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, appPowerMah); + .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WAKELOCK, + totalAppDurationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, + appPowerMah); } @Override @@ -167,9 +174,16 @@ public class WakelockPowerCalculator extends PowerCalculator { } result.durationMs = osDurationMs + wakeTimeMillis; result.powerMah = osPowerMah + power; + } else { + result.durationMs = 0; + result.powerMah = 0; } } + /** + * Return on-battery/screen-off time. May be negative if the screen-on time exceeds + * the on-battery time. + */ private long calculateWakeTimeMillis(BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs) { final long batteryUptimeUs = batteryStats.getBatteryUptime(rawUptimeUs); diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java deleted file mode 100644 index d2d822083a95..000000000000 --- a/core/java/com/android/internal/util/Preconditions.java +++ /dev/null @@ -1,771 +0,0 @@ -/* - * Copyright (C) 2011 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; - -import android.annotation.IntRange; -import android.annotation.NonNull; -import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; -import android.text.TextUtils; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Objects; - -/** - * Simple static methods to be called at the start of your own methods to verify - * correct arguments and state. - */ -public class Preconditions { - - /** - * Ensures that an expression checking an argument is true. - * - * @param expression the expression to check - * @throws IllegalArgumentException if {@code expression} is false - */ - @UnsupportedAppUsage - public static void checkArgument(boolean expression) { - if (!expression) { - throw new IllegalArgumentException(); - } - } - - /** - * Ensures that an expression checking an argument is true. - * - * @param expression the expression to check - * @param errorMessage the exception message to use if the check fails; will - * be converted to a string using {@link String#valueOf(Object)} - * @throws IllegalArgumentException if {@code expression} is false - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static void checkArgument(boolean expression, final Object errorMessage) { - if (!expression) { - throw new IllegalArgumentException(String.valueOf(errorMessage)); - } - } - - /** - * Ensures that an expression checking an argument is true. - * - * @param expression the expression to check - * @param messageTemplate a printf-style message template to use if the check fails; will - * be converted to a string using {@link String#format(String, Object...)} - * @param messageArgs arguments for {@code messageTemplate} - * @throws IllegalArgumentException if {@code expression} is false - */ - public static void checkArgument( - final boolean expression, - final @NonNull String messageTemplate, - final Object... messageArgs) { - if (!expression) { - throw new IllegalArgumentException(String.format(messageTemplate, messageArgs)); - } - } - - /** - * Ensures that an string reference passed as a parameter to the calling - * method is not empty. - * - * @param string an string reference - * @return the string reference that was validated - * @throws IllegalArgumentException if {@code string} is empty - */ - public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string) { - if (TextUtils.isEmpty(string)) { - throw new IllegalArgumentException(); - } - return string; - } - - /** - * Ensures that an string reference passed as a parameter to the calling - * method is not empty. - * - * @param string an string reference - * @param errorMessage the exception message to use if the check fails; will - * be converted to a string using {@link String#valueOf(Object)} - * @return the string reference that was validated - * @throws IllegalArgumentException if {@code string} is empty - */ - public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string, - final Object errorMessage) { - if (TextUtils.isEmpty(string)) { - throw new IllegalArgumentException(String.valueOf(errorMessage)); - } - return string; - } - - /** - * Ensures that an string reference passed as a parameter to the calling method is not empty. - * - * @param string an string reference - * @param messageTemplate a printf-style message template to use if the check fails; will be - * converted to a string using {@link String#format(String, Object...)} - * @param messageArgs arguments for {@code messageTemplate} - * @return the string reference that was validated - * @throws IllegalArgumentException if {@code string} is empty - */ - public static @NonNull <T extends CharSequence> T checkStringNotEmpty( - final T string, - final @NonNull String messageTemplate, - final Object... messageArgs) { - if (TextUtils.isEmpty(string)) { - throw new IllegalArgumentException(String.format(messageTemplate, messageArgs)); - } - return string; - } - - /** - * Ensures that an object reference passed as a parameter to the calling - * method is not null. - * - * @param reference an object reference - * @return the non-null reference that was validated - * @throws NullPointerException if {@code reference} is null - * @deprecated - use {@link java.util.Objects.requireNonNull} instead. - */ - @Deprecated - @UnsupportedAppUsage - public static @NonNull <T> T checkNotNull(final T reference) { - if (reference == null) { - throw new NullPointerException(); - } - return reference; - } - - /** - * Ensures that an object reference passed as a parameter to the calling - * method is not null. - * - * @param reference an object reference - * @param errorMessage the exception message to use if the check fails; will - * be converted to a string using {@link String#valueOf(Object)} - * @return the non-null reference that was validated - * @throws NullPointerException if {@code reference} is null - * @deprecated - use {@link java.util.Objects#requireNonNull} instead. - */ - @Deprecated - @UnsupportedAppUsage - public static @NonNull <T> T checkNotNull(final T reference, final Object errorMessage) { - if (reference == null) { - throw new NullPointerException(String.valueOf(errorMessage)); - } - return reference; - } - - /** - * Ensures that an object reference passed as a parameter to the calling - * method is not null. - * - * @param messageTemplate a printf-style message template to use if the check fails; will - * be converted to a string using {@link String#format(String, Object...)} - * @param messageArgs arguments for {@code messageTemplate} - * @throws NullPointerException if {@code reference} is null - */ - public static @NonNull <T> T checkNotNull( - final T reference, - final @NonNull String messageTemplate, - final Object... messageArgs) { - if (reference == null) { - throw new NullPointerException(String.format(messageTemplate, messageArgs)); - } - return reference; - } - - /** - * Ensures the truth of an expression involving the state of the calling - * instance, but not involving any parameters to the calling method. - * - * @param expression a boolean expression - * @throws IllegalStateException if {@code expression} is false - */ - @UnsupportedAppUsage - public static void checkState(final boolean expression) { - checkState(expression, null); - } - - /** - * Ensures the truth of an expression involving the state of the calling - * instance, but not involving any parameters to the calling method. - * - * @param expression a boolean expression - * @param errorMessage the exception message to use if the check fails; will - * be converted to a string using {@link String#valueOf(Object)} - * @throws IllegalStateException if {@code expression} is false - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static void checkState(final boolean expression, String errorMessage) { - if (!expression) { - throw new IllegalStateException(errorMessage); - } - } - - /** - * Ensures the truth of an expression involving the state of the calling - * instance, but not involving any parameters to the calling method. - * - * @param expression a boolean expression - * @param messageTemplate a printf-style message template to use if the check fails; will - * be converted to a string using {@link String#format(String, Object...)} - * @param messageArgs arguments for {@code messageTemplate} - * @throws IllegalStateException if {@code expression} is false - */ - public static void checkState( - final boolean expression, - final @NonNull String messageTemplate, - final Object... messageArgs) { - if (!expression) { - throw new IllegalStateException(String.format(messageTemplate, messageArgs)); - } - } - - /** - * Ensures the truth of an expression involving whether the calling identity is authorized to - * call the calling method. - * - * @param expression a boolean expression - * @throws SecurityException if {@code expression} is false - */ - public static void checkCallAuthorization(final boolean expression) { - if (!expression) { - throw new SecurityException("Calling identity is not authorized"); - } - } - - /** - * Ensures the truth of an expression involving whether the calling identity is authorized to - * call the calling method. - * - * @param expression a boolean expression - * @param message the message of the security exception to be thrown - * @throws SecurityException if {@code expression} is false - */ - public static void checkCallAuthorization(final boolean expression, final String message) { - if (!expression) { - throw new SecurityException(message); - } - } - - /** - * Ensures the truth of an expression involving whether the calling identity is authorized to - * call the calling method. - * - * @param expression a boolean expression - * @param messageTemplate a printf-style message template to use if the check fails; will - * be converted to a string using {@link String#format(String, Object...)} - * @param messageArgs arguments for {@code messageTemplate} - * @throws SecurityException if {@code expression} is false - */ - public static void checkCallAuthorization( - final boolean expression, - final @NonNull String messageTemplate, - final Object... messageArgs) { - if (!expression) { - throw new SecurityException(String.format(messageTemplate, messageArgs)); - } - } - - /** - * Ensures the truth of an expression involving whether the calling user is authorized to - * call the calling method. - * - * @param expression a boolean expression - * @throws SecurityException if {@code expression} is false - */ - public static void checkCallingUser(final boolean expression) { - if (!expression) { - throw new SecurityException("Calling user is not authorized"); - } - } - - /** - * Check the requested flags, throwing if any requested flags are outside - * the allowed set. - * - * @return the validated requested flags. - */ - public static int checkFlagsArgument(final int requestedFlags, final int allowedFlags) { - if ((requestedFlags & allowedFlags) != requestedFlags) { - throw new IllegalArgumentException("Requested flags 0x" - + Integer.toHexString(requestedFlags) + ", but only 0x" - + Integer.toHexString(allowedFlags) + " are allowed"); - } - - return requestedFlags; - } - - /** - * Ensures that that the argument numeric value is non-negative (greater than or equal to 0). - * - * @param value a numeric int value - * @param errorMessage the exception message to use if the check fails - * @return the validated numeric value - * @throws IllegalArgumentException if {@code value} was negative - */ - public static @IntRange(from = 0) int checkArgumentNonnegative(final int value, - final String errorMessage) { - if (value < 0) { - throw new IllegalArgumentException(errorMessage); - } - - return value; - } - - /** - * Ensures that that the argument numeric value is non-negative (greater than or equal to 0). - * - * @param value a numeric int value - * - * @return the validated numeric value - * @throws IllegalArgumentException if {@code value} was negative - */ - public static @IntRange(from = 0) int checkArgumentNonnegative(final int value) { - if (value < 0) { - throw new IllegalArgumentException(); - } - - return value; - } - - /** - * Ensures that that the argument numeric value is non-negative (greater than or equal to 0). - * - * @param value a numeric long value - * @return the validated numeric value - * @throws IllegalArgumentException if {@code value} was negative - */ - public static long checkArgumentNonnegative(final long value) { - if (value < 0) { - throw new IllegalArgumentException(); - } - - return value; - } - - /** - * Ensures that that the argument numeric value is non-negative (greater than or equal to 0). - * - * @param value a numeric long value - * @param errorMessage the exception message to use if the check fails - * @return the validated numeric value - * @throws IllegalArgumentException if {@code value} was negative - */ - public static long checkArgumentNonnegative(final long value, final String errorMessage) { - if (value < 0) { - throw new IllegalArgumentException(errorMessage); - } - - return value; - } - - /** - * Ensures that that the argument numeric value is positive (greater than 0). - * - * @param value a numeric int value - * @param errorMessage the exception message to use if the check fails - * @return the validated numeric value - * @throws IllegalArgumentException if {@code value} was not positive - */ - public static int checkArgumentPositive(final int value, final String errorMessage) { - if (value <= 0) { - throw new IllegalArgumentException(errorMessage); - } - - return value; - } - - /** - * Ensures that the argument floating point value is non-negative (greater than or equal to 0). - * @param value a floating point value - * @param errorMessage the exteption message to use if the check fails - * @return the validated numeric value - * @throws IllegalArgumentException if {@code value} was negative - */ - public static float checkArgumentNonNegative(final float value, final String errorMessage) { - if (value < 0) { - throw new IllegalArgumentException(errorMessage); - } - - return value; - } - - /** - * Ensures that the argument floating point value is positive (greater than 0). - * @param value a floating point value - * @param errorMessage the exteption message to use if the check fails - * @return the validated numeric value - * @throws IllegalArgumentException if {@code value} was not positive - */ - public static float checkArgumentPositive(final float value, final String errorMessage) { - if (value <= 0) { - throw new IllegalArgumentException(errorMessage); - } - - return value; - } - - /** - * Ensures that the argument floating point value is a finite number. - * - * <p>A finite number is defined to be both representable (that is, not NaN) and - * not infinite (that is neither positive or negative infinity).</p> - * - * @param value a floating point value - * @param valueName the name of the argument to use if the check fails - * - * @return the validated floating point value - * - * @throws IllegalArgumentException if {@code value} was not finite - */ - public static float checkArgumentFinite(final float value, final String valueName) { - if (Float.isNaN(value)) { - throw new IllegalArgumentException(valueName + " must not be NaN"); - } else if (Float.isInfinite(value)) { - throw new IllegalArgumentException(valueName + " must not be infinite"); - } - - return value; - } - - /** - * Ensures that the argument floating point value is within the inclusive range. - * - * <p>While this can be used to range check against +/- infinity, note that all NaN numbers - * will always be out of range.</p> - * - * @param value a floating point value - * @param lower the lower endpoint of the inclusive range - * @param upper the upper endpoint of the inclusive range - * @param valueName the name of the argument to use if the check fails - * - * @return the validated floating point value - * - * @throws IllegalArgumentException if {@code value} was not within the range - */ - public static float checkArgumentInRange(float value, float lower, float upper, - String valueName) { - if (Float.isNaN(value)) { - throw new IllegalArgumentException(valueName + " must not be NaN"); - } else if (value < lower) { - throw new IllegalArgumentException( - String.format( - "%s is out of range of [%f, %f] (too low)", valueName, lower, upper)); - } else if (value > upper) { - throw new IllegalArgumentException( - String.format( - "%s is out of range of [%f, %f] (too high)", valueName, lower, upper)); - } - - return value; - } - - /** - * Ensures that the argument floating point value is within the inclusive range. - * - * <p>While this can be used to range check against +/- infinity, note that all NaN numbers - * will always be out of range.</p> - * - * @param value a floating point value - * @param lower the lower endpoint of the inclusive range - * @param upper the upper endpoint of the inclusive range - * @param valueName the name of the argument to use if the check fails - * - * @return the validated floating point value - * - * @throws IllegalArgumentException if {@code value} was not within the range - */ - public static double checkArgumentInRange(double value, double lower, double upper, - String valueName) { - if (Double.isNaN(value)) { - throw new IllegalArgumentException(valueName + " must not be NaN"); - } else if (value < lower) { - throw new IllegalArgumentException( - String.format( - "%s is out of range of [%f, %f] (too low)", valueName, lower, upper)); - } else if (value > upper) { - throw new IllegalArgumentException( - String.format( - "%s is out of range of [%f, %f] (too high)", valueName, lower, upper)); - } - - return value; - } - - /** - * Ensures that the argument int value is within the inclusive range. - * - * @param value a int value - * @param lower the lower endpoint of the inclusive range - * @param upper the upper endpoint of the inclusive range - * @param valueName the name of the argument to use if the check fails - * - * @return the validated int value - * - * @throws IllegalArgumentException if {@code value} was not within the range - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static int checkArgumentInRange(int value, int lower, int upper, - String valueName) { - if (value < lower) { - throw new IllegalArgumentException( - String.format( - "%s is out of range of [%d, %d] (too low)", valueName, lower, upper)); - } else if (value > upper) { - throw new IllegalArgumentException( - String.format( - "%s is out of range of [%d, %d] (too high)", valueName, lower, upper)); - } - - return value; - } - - /** - * Ensures that the argument long value is within the inclusive range. - * - * @param value a long value - * @param lower the lower endpoint of the inclusive range - * @param upper the upper endpoint of the inclusive range - * @param valueName the name of the argument to use if the check fails - * - * @return the validated long value - * - * @throws IllegalArgumentException if {@code value} was not within the range - */ - public static long checkArgumentInRange(long value, long lower, long upper, - String valueName) { - if (value < lower) { - throw new IllegalArgumentException( - String.format( - "%s is out of range of [%d, %d] (too low)", valueName, lower, upper)); - } else if (value > upper) { - throw new IllegalArgumentException( - String.format( - "%s is out of range of [%d, %d] (too high)", valueName, lower, upper)); - } - - return value; - } - - /** - * Ensures that the array is not {@code null}, and none of its elements are {@code null}. - * - * @param value an array of boxed objects - * @param valueName the name of the argument to use if the check fails - * - * @return the validated array - * - * @throws NullPointerException if the {@code value} or any of its elements were {@code null} - */ - public static <T> T[] checkArrayElementsNotNull(final T[] value, final String valueName) { - if (value == null) { - throw new NullPointerException(valueName + " must not be null"); - } - - for (int i = 0; i < value.length; ++i) { - if (value[i] == null) { - throw new NullPointerException( - String.format("%s[%d] must not be null", valueName, i)); - } - } - - return value; - } - - /** - * Ensures that the {@link Collection} is not {@code null}, and none of its elements are - * {@code null}. - * - * @param value a {@link Collection} of boxed objects - * @param valueName the name of the argument to use if the check fails - * - * @return the validated {@link Collection} - * - * @throws NullPointerException if the {@code value} or any of its elements were {@code null} - */ - public static @NonNull <C extends Collection<T>, T> C checkCollectionElementsNotNull( - final C value, final String valueName) { - if (value == null) { - throw new NullPointerException(valueName + " must not be null"); - } - - long ctr = 0; - for (T elem : value) { - if (elem == null) { - throw new NullPointerException( - String.format("%s[%d] must not be null", valueName, ctr)); - } - ++ctr; - } - - return value; - } - - /** - * Ensures that the {@link Collection} is not {@code null}, and contains at least one element. - * - * @param value a {@link Collection} of boxed elements. - * @param valueName the name of the argument to use if the check fails. - - * @return the validated {@link Collection} - * - * @throws NullPointerException if the {@code value} was {@code null} - * @throws IllegalArgumentException if the {@code value} was empty - */ - public static <T> Collection<T> checkCollectionNotEmpty(final Collection<T> value, - final String valueName) { - if (value == null) { - throw new NullPointerException(valueName + " must not be null"); - } - if (value.isEmpty()) { - throw new IllegalArgumentException(valueName + " is empty"); - } - return value; - } - - /** - * Ensures that the given byte array is not {@code null}, and contains at least one element. - * - * @param value an array of elements. - * @param valueName the name of the argument to use if the check fails. - - * @return the validated array - * - * @throws NullPointerException if the {@code value} was {@code null} - * @throws IllegalArgumentException if the {@code value} was empty - */ - @NonNull - public static byte[] checkByteArrayNotEmpty(final byte[] value, final String valueName) { - if (value == null) { - throw new NullPointerException(valueName + " must not be null"); - } - if (value.length == 0) { - throw new IllegalArgumentException(valueName + " is empty"); - } - return value; - } - - /** - * Ensures that argument {@code value} is one of {@code supportedValues}. - * - * @param supportedValues an array of string values - * @param value a string value - * - * @return the validated value - * - * @throws NullPointerException if either {@code value} or {@code supportedValues} is null - * @throws IllegalArgumentException if the {@code value} is not in {@code supportedValues} - */ - @NonNull - public static String checkArgumentIsSupported(final String[] supportedValues, - final String value) { - checkNotNull(value); - checkNotNull(supportedValues); - - if (!contains(supportedValues, value)) { - throw new IllegalArgumentException(value + "is not supported " - + Arrays.toString(supportedValues)); - } - return value; - } - - private static boolean contains(String[] values, String value) { - if (values == null) { - return false; - } - for (int i = 0; i < values.length; ++i) { - if (Objects.equals(value, values[i])) { - return true; - } - } - return false; - } - - /** - * Ensures that all elements in the argument floating point array are within the inclusive range - * - * <p>While this can be used to range check against +/- infinity, note that all NaN numbers - * will always be out of range.</p> - * - * @param value a floating point array of values - * @param lower the lower endpoint of the inclusive range - * @param upper the upper endpoint of the inclusive range - * @param valueName the name of the argument to use if the check fails - * - * @return the validated floating point value - * - * @throws IllegalArgumentException if any of the elements in {@code value} were out of range - * @throws NullPointerException if the {@code value} was {@code null} - */ - public static float[] checkArrayElementsInRange(float[] value, float lower, float upper, - String valueName) { - checkNotNull(value, "%s must not be null", valueName); - - for (int i = 0; i < value.length; ++i) { - float v = value[i]; - - if (Float.isNaN(v)) { - throw new IllegalArgumentException(valueName + "[" + i + "] must not be NaN"); - } else if (v < lower) { - throw new IllegalArgumentException( - String.format("%s[%d] is out of range of [%f, %f] (too low)", - valueName, i, lower, upper)); - } else if (v > upper) { - throw new IllegalArgumentException( - String.format("%s[%d] is out of range of [%f, %f] (too high)", - valueName, i, lower, upper)); - } - } - - return value; - } - - /** - * Ensures that all elements in the argument integer array are within the inclusive range - * - * @param value an integer array of values - * @param lower the lower endpoint of the inclusive range - * @param upper the upper endpoint of the inclusive range - * @param valueName the name of the argument to use if the check fails - * - * @return the validated integer array - * - * @throws IllegalArgumentException if any of the elements in {@code value} were out of range - * @throws NullPointerException if the {@code value} was {@code null} - */ - public static int[] checkArrayElementsInRange(int[] value, int lower, int upper, - String valueName) { - checkNotNull(value, "%s must not be null", valueName); - - for (int i = 0; i < value.length; ++i) { - int v = value[i]; - - if (v < lower) { - throw new IllegalArgumentException( - String.format("%s[%d] is out of range of [%d, %d] (too low)", - valueName, i, lower, upper)); - } else if (v > upper) { - throw new IllegalArgumentException( - String.format("%s[%d] is out of range of [%d, %d] (too high)", - valueName, i, lower, upper)); - } - } - - return value; - } -} diff --git a/core/proto/android/server/biometrics.proto b/core/proto/android/server/biometrics.proto index c918dbbede26..ac9e3e001d50 100644 --- a/core/proto/android/server/biometrics.proto +++ b/core/proto/android/server/biometrics.proto @@ -113,11 +113,16 @@ message SensorStateProto { IRIS = 3; } + enum ModalityFlag { + FINGERPRINT_UDFPS = 0; + } + option (.android.msg_privacy).dest = DEST_AUTOMATIC; // Unique sensorId optional int32 sensor_id = 1; + // The type of the sensor. optional Modality modality = 2; // The current strength (see {@link BiometricManager.Authenticators}) of this sensor, taking any @@ -137,6 +142,9 @@ message SensorStateProto { // True if a HAT is required (field above) AND a challenge needs to be generated by the // biometric TEE (or equivalent), and wrapped within the HAT. optional bool reset_lockout_requires_challenge = 7; + + // Detailed information about the sensor's modality, if available. + repeated ModalityFlag modality_flags = 8; } // State of a specific user for a specific sensor. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ffb8d1daac90..50fb01b49616 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -593,8 +593,6 @@ <protected-broadcast android:name="android.intent.action.DYNAMIC_SENSOR_CHANGED" /> - <protected-broadcast android:name="android.intent.action.ACTION_RADIO_OFF" /> - <protected-broadcast android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" /> <protected-broadcast android:name="android.accounts.action.ACCOUNT_REMOVED" /> <protected-broadcast android:name="android.accounts.action.VISIBLE_ACCOUNTS_CHANGED" /> @@ -2900,6 +2898,12 @@ android:description="@string/permdesc_runInBackground" android:protectionLevel="normal" /> + <!-- Allows a companion app to start a foreground service from the background. + {@see android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND} + --> + <permission android:name="android.permission.REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND" + android:protectionLevel="normal"/> + <!-- Allows a companion app to use data in the background. <p>Protection level: normal --> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 73adeaa02691..2be8a5c5cdd2 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gebruik kortpad"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Kleuromkering"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Kleurkorreksie"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Eenhandmodus"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra donker"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aangeskakel."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is afgeskakel"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index f8ff93456566..2fffaa60b049 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -1869,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"በእርስዎ አስተዳዳሪ ተዘምኗል"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"በእርስዎ አስተዳዳሪ ተሰርዟል"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"እሺ"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"የባትሪ ኃይል ቆጣቢ ጠቆር ያለ ገጽታን ያበራል እና የጀርባ እንቅስቃሴን፣ አንዳንድ ምስላዊ ተጽዕኖዎችን፣ የተወሰኑ ባህሪያትን እና አንዳንድ አውታረ መረብ ግንኙነቶችን ይገድባል ወይም ያጠፋል።\n\n"<annotation id="url">"የበለጠ ለመረዳት"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"ባትሪ ቆጣቢ ጠቆር ያለ ገጽታን ያበራል እና የጀርባ እንቅስቃሴን፣ አንዳንድ ዕይታዊ ውጤቶችን፣ አንዳንድ ባህሪዎችን፣ እና አንዳንድ የአውታረ መረብ ግንኙነቶችን ይገድባል ወይም ያጠፋል።"</string> <string name="data_saver_description" msgid="4995164271550590517">"የውሂብ አጠቃቀም እንዲቀንስ ለማገዝ ውሂብ ቆጣቢ አንዳንድ መተግበሪያዎች ከበስተጀርባ ሆነው ውሂብ እንዳይልኩ ወይም እንዳይቀበሉ ይከለክላቸዋል። በአሁኑ ጊዜ እየተጠቀሙበት ያለ መተግበሪያ ውሂብ ሊደርስ ይችላል፣ ነገር ግን ባነሰ ተደጋጋሚነት ሊሆን ይችላል። ይሄ ማለት ለምሳሌ ምስሎችን መታ እስኪያደርጓቸው ድረስ ላይታዩ ይችላሉ ማለት ነው።"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ውሂብ ቆጣቢ ይጥፋ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"አብራ"</string> @@ -1975,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> አሁን ላይ አይገኝም። በ<xliff:g id="APP_NAME_1">%2$s</xliff:g> የሚተዳደር ነው።"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"የበለጠ ለመረዳት"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"መተግበሪያን ላፍታ እንዳይቆም አድርግ"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"የሥራ መተግበሪያዎች ይብሩ?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"የእርስዎን የሥራ መተግበሪያዎች እና ማሳወቂያዎች መዳረሻ ያግኙ"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"አብራ"</string> <string name="app_blocked_title" msgid="7353262160455028160">"መተግበሪያ አይገኝም"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> አሁን አይገኝም።"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index c2be67cfcc02..31dcceb5ab89 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -1034,8 +1034,7 @@ <string name="text_copied" msgid="2531420577879738860">"تم نسخ النص إلى الحافظة."</string> <string name="copied" msgid="4675902854553014676">"تم النسخ."</string> <string name="pasted_from_app" msgid="5627698450808256545">"تم لصق محتوى في <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> من <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>."</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"لصَق تطبيق <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> محتوىً من الحافظة."</string> <string name="pasted_text" msgid="4298871641549173733">"لصق تطبيق <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> النص الذي نسخته."</string> <string name="pasted_image" msgid="4729097394781491022">"لصق تطبيق <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> الصورة التي نسختها."</string> <string name="pasted_content" msgid="646276353060777131">"لصق تطبيق <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> المحتوى الذي نسخته."</string> @@ -1340,14 +1339,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"جارٍ تحضير <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"بدء التطبيقات."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"جارٍ إعادة التشغيل."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"هل تريد إيقاف الشاشة؟"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"أثناء إعداد بصمة الإصبع، ضغطت على زر التشغيل.\n\nيؤدي هذا الإجراء عادةً إلى إيقاف الشاشة."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"إيقاف"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"إلغاء"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> يعمل"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"انقر للعودة إلى اللعبة"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"اختيار اللعبة"</string> @@ -1806,8 +1801,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استخدام الاختصار"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"قلب الألوان"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"تصحيح الألوان"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"وضع \"التصفح بيد واحدة\""</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"زيادة تعتيم الشاشة"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم تفعيل <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم إيقاف <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> @@ -1967,10 +1961,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"تم التحديث بواسطة المشرف"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"تم الحذف بواسطة المشرف"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"حسنًا"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"يؤدي استخدام ميزة \"توفير شحن البطارية\" إلى تفعيل وضع \"المظهر الداكن\" وتقييد أو إيقاف الأنشطة في الخلفية وبعض التأثيرات المرئية وميزات معيّنة وبعض اتصالات الشبكات.\n\n"<annotation id="url">"مزيد من المعلومات"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"يؤدي استخدام ميزة \"توفير شحن البطارية\" إلى تفعيل وضع \"المظهر الداكن\" وتقييد أو إيقاف الأنشطة في الخلفية وبعض التأثيرات المرئية وميزات معيّنة وبعض اتصالات الشبكات."</string> <string name="data_saver_description" msgid="4995164271550590517">"للمساعدة في خفض استخدام البيانات، تمنع ميزة \"توفير البيانات\" بعض التطبيقات من إرسال البيانات وتلقّيها في الخلفية. يمكن للتطبيقات المتاحة لديك الآن استخدام البيانات، ولكن لا يمكنها الإكثار من ذلك. وهذا يعني أن الصور مثلاً لا تظهر حتى تنقر عليها."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"هل تريد تفعيل توفير البيانات؟"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"تفعيل"</string> @@ -2109,10 +2101,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"التطبيق <xliff:g id="APP_NAME_0">%1$s</xliff:g> غير متاح الآن، وهو مُدار بواسطة <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"مزيد من المعلومات"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"استئناف تشغيل التطبيق"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"هل تريد تفعيل تطبيقات العمل؟"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"الوصول إلى تطبيقات العمل وإشعاراتها"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"تفعيل"</string> <string name="app_blocked_title" msgid="7353262160455028160">"التطبيق غير متاح"</string> <string name="app_blocked_message" msgid="542972921087873023">"تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> غير متاح الآن."</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 8101c28647ed..74f98fc70df3 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"ক্লিপব\'র্ডলৈ বাৰ্তা প্ৰতিলিপি কৰা হ’ল।"</string> <string name="copied" msgid="4675902854553014676">"প্ৰতিলিপি কৰা হ’ল"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>ৰ পৰা <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> পে’ষ্ট কৰা হৈছে"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>এ আপোনাৰ ক্লিপব’ৰ্ডৰ পৰা পে’ষ্ট কৰিছে"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>এ আপুনি প্ৰতিলিপি কৰা সমল পে’ষ্ট কৰিছে"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>এ আপুনি প্ৰতিলিপি কৰা এটা প্ৰতিচ্ছবি পে’ষ্ট কৰিছে"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>এ আপুনি প্ৰতিলিপি কৰা সমল পে’ষ্ট কৰিছে"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>সাজু কৰি থকা হৈছে।"</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"আৰম্ভ হৈ থকা এপসমূহ।"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"বুট কাৰ্য সমাপ্ত কৰিছে।"</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"স্ক্ৰীন অফ কৰিবনে?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"আপোনাৰ ফিংগাৰপ্ৰিণ্ট ছেট আপ কৰাৰ সময়ত, আপুনি পাৱাৰ বুটামটো টিপিছে।\n\nএইটোৱে সচৰাচৰ আপোনাৰ স্ক্ৰীনখন অফ কৰে।"</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"অফ কৰক"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"বাতিল কৰক"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> চলি আছে"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"গেইমলৈ উভতি যাওক"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"গেইম বাছনি কৰক"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index e8abac1f039c..f4c6c9997a3f 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Mətn panoya kopyalandı."</string> <string name="copied" msgid="4675902854553014676">"Kopyalandı"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> tətbiqindən əlavə edilib"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> mübadilə buferinizdən əlavə edilib"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kopyaladığınız mətni əlavə etdi"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kopyaladığınız şəkli əlavə etdi"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kopyaladığınız kontenti əlavə etdi"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> proqramının hazırlanması."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Tətbiqlər başladılır."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Yükləmə başa çatır."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ekran deaktiv edilsin?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Barmaq izinizi ayarlayarkən Qidalanma düyməsinə basdınız.\n\nBu, adətən ekranınızı deaktiv edir."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Deaktiv edin"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Ləğv edin"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> çalışır"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Oyuna qayıtmaq üçün klikləyin"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Oyun seçin"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Qısayol İstifadə edin"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Rəng İnversiyası"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Rəng korreksiyası"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Bir əlli rejim"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Əlavə qaraltma"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Səs səviyyəsi düymələrinə basıb saxlayın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktiv edildi."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Səs səviyyəsi düymələrinə basılaraq saxlanıb. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> deaktiv edilib."</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Admin tərəfindən yeniləndi"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Admin tərəfindən silindi"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Enerjiyə Qənaət rejimi Tünd temanı aktivləşdirir, habelə arxa fon fəaliyyətini, bəzi vizual effektləri, müəyyən xüsusiyyətləri və bəzi şəbəkə bağlantılarını məhdudlaşdırır, yaxud söndürür.\n\n"<annotation id="url">"Ətraflı məlumat"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Enerjiyə Qənaət rejimi Tünd temanı aktivləşdirir, habelə arxa fon fəaliyyətini, bəzi vizual effektləri, müəyyən xüsusiyyətləri və bəzi şəbəkə bağlantılarını məhdudlaşdırır, yaxud söndürür."</string> <string name="data_saver_description" msgid="4995164271550590517">"Mobil interneti qənaətlə işlətmək məqsədilə Data Qanaəti bəzi tətbiqlərin fonda data göndərməsinin və qəbulunun qarşısını alır. Hazırda işlətdiyiniz tətbiq nisbətən az müntəzəmliklə data istifadə edə bilər. Örnək olaraq bu, o deməkdir ki, şəkil fayllarına toxunmadıqca onlar açılmayacaq."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Trafikə qənaət edilsin?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivləşdirin"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> hazırda əlçatan deyil. Bunu <xliff:g id="APP_NAME_1">%2$s</xliff:g> idarə edir."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Ətraflı məlumat"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Tətbiqi davam etdirin"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"İş tətbiqləri aktiv edilsin?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"İş tətbiqlərinizə və bildirişlərinizə giriş əldə edin"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivləşdirin"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Tətbiq əlçatan deyil"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> hazırda əlçatan deyil."</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 26814512f6cd..91ac4887d7f6 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -1892,10 +1892,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je administrator"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je administrator"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Potvrdi"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizuelne efekte, određene funkcije i mrežne veze.\n\n"<annotation id="url">"Saznajte više"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Ušteda baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizuelne efekte, određene funkcije i neke mrežne veze."</string> <string name="data_saver_description" msgid="4995164271550590517">"Da bi se smanjila potrošnja podataka, Ušteda podataka sprečava neke aplikacije da šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može da pristupa podacima, ali će to činiti ređe. Na primer, slike se neće prikazivati dok ih ne dodirnete."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Želite da uključite Uštedu podataka?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string> @@ -2007,10 +2005,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> trenutno nije dostupna. <xliff:g id="APP_NAME_1">%2$s</xliff:g> upravlja dostupnošću."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Saznajte više"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Opozovi pauziranje aplikacije"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Uključiti poslovne aplikacije?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Pristupajte poslovnim aplikacijama i obaveštenjima"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Uključi"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string> <string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno nije dostupna."</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 9f7fc35e3699..9ae5c92c93a1 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -1028,8 +1028,7 @@ <string name="text_copied" msgid="2531420577879738860">"Тэкст скапіраваны ў буфер абмену."</string> <string name="copied" msgid="4675902854553014676">"Скапіравана"</string> <string name="pasted_from_app" msgid="5627698450808256545">"Праграма \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\" была ўстаўлена з праграмы \"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>\""</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"Праграма (\"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\") уставіла даныя з буфера абмену"</string> <string name="pasted_text" msgid="4298871641549173733">"Скапіраваны вамі тэкст устаўлены праграмай \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\""</string> <string name="pasted_image" msgid="4729097394781491022">"Скапіраваны вамі відарыс устаўлены праграмай \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\""</string> <string name="pasted_content" msgid="646276353060777131">"Скапіраванае вамі змесціва ўстаўлена праграмай \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\""</string> @@ -1300,14 +1299,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Падрыхтоўка <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск прыкладанняў."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Завяршэнне загрузкі."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Выключыць экран?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Падчас наладкі адбітка пальца вы націскалі кнопку сілкавання.\n\nЗвычайна гэта дзеянне выключае экран."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Выключыць"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Скасаваць"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"Прыкладанне \"<xliff:g id="APP">%1$s</xliff:g>\" запушчанае"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Націсніце, каб вярнуцца да гульні"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Выберыце гульню"</string> @@ -1762,8 +1757,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Выкарыстоўваць камбінацыю хуткага доступу"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія колераў"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Карэкцыя колераў"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Рэжым кіравання адной рукой"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дадатковае памяншэнне яркасці"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Клавішы гучнасці ўтрымліваліся націснутымі. Уключана служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Клавішы гучнасці ўтрымліваліся націснутымі. Служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" выключана."</string> @@ -1921,10 +1915,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Абноўлены вашым адміністратарам"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Выдалены вашым адміністратарам"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"У рэжыме эканоміі зараду ўключаецца цёмная тэма і выключаюцца ці абмяжоўваюцца дзеянні ў фонавым рэжыме, некаторыя візуальныя эфекты, пэўныя функцыі і падключэнні да сетак.\n\n"<annotation id="url">"Даведацца больш"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"У рэжыме эканоміі зараду ўключаецца цёмная тэма і выключаюцца ці абмяжоўваюцца дзеянні ў фонавым рэжыме, некаторыя візуальныя эфекты, пэўныя функцыі і падключэнні да сетак."</string> <string name="data_saver_description" msgid="4995164271550590517">"У рэжыме \"Эканомія трафіка\" фонавая перадача для некаторых праграмам адключана. Праграма, якую вы зараз выкарыстоўваеце, можа атрымліваць доступ да даных, але радзей, чым звычайна. Напрыклад, відарысы могуць не загружацца, пакуль вы не націсніце на іх."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Уключыць Эканомію трафіка?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Уключыць"</string> @@ -2045,10 +2037,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Праграма \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" цяпер недаступная. Яна кіруецца праграмай \"<xliff:g id="APP_NAME_1">%2$s</xliff:g>\"."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Даведацца больш"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Скасаваць прыпыненне для праграмы"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Уключыць працоўныя праграмы?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Атрымаць доступ да працоўных праграм і апавяшчэнняў"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Уключыць"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Праграма недаступная"</string> <string name="app_blocked_message" msgid="542972921087873023">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" цяпер недаступная."</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 619dcb7e191a..d82f189cb090 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Текстът е копиран в буферната памет."</string> <string name="copied" msgid="4675902854553014676">"Копирано"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> постави данни от <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> постави данни от буферната памет"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> постави копиран от вас текст"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> постави копирано от вас изображение"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> постави копирано от вас съдържание"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> се подготвя."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Приложенията се стартират."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Зареждането завършва."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Да се изключи ли екранът?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"При настройването на отпечатъка си натиснахте бутона за захранване.\n\nТова действие обикновено изключва екрана."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Изключване"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Отказ"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> се изпълнява"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Докоснете, за да се върнете към играта"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Избиране на игра"</string> @@ -1874,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Актуализирано от администратора ви"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Изтрито от администратора ви"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Режимът за запазване на батерията включва тъмната тема и ограничава или изключва активността на заден план, някои визуални ефекти, определени функции и някои връзки с мрежата.\n\n"<annotation id="url">"Научете повече"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Режимът за запазване на батерията включва тъмната тема и ограничава или изключва активността на заден план, някои визуални ефекти, определени функции и някои връзки с мрежата."</string> <string name="data_saver_description" msgid="4995164271550590517">"С цел намаляване на преноса на данни функцията за икономия на данни не позволява на някои приложения да изпращат или получават данни на заден план. Понастоящем използвано от вас приложение може да използва данни, но по-рядко. Това например може да означава, че изображенията не се показват, докато не ги докоснете."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Включване на „Икономия на данни“?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Включване"</string> @@ -1980,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"В момента няма достъп до <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Това се управлява от <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Научете повече"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Отмяна на паузата за приложението"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Включване на служ. приложения?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Получете достъп до служебните си приложения и известия"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Включване"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Приложението не е достъпно"</string> <string name="app_blocked_message" msgid="542972921087873023">"В момента няма достъп до <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 9dedb888f47a..66ce6bd7007a 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"শর্টকাট ব্যবহার করুন"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"রঙ উল্টানো"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"রঙ সংশোধন"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"এক হাতে ব্যবহার করার মোড"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"অতিরিক্ত কম আলো"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> চালু করা হয়েছে।"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> বন্ধ করা হয়েছে।"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index c918d8c5028b..35f5e4b748b5 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -1892,10 +1892,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je vaš administrator"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je vaš administrator"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Uredu"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnost u pozadini, određene vizuelne efekte i funkcije te neke mrežne veze.\n\n"<annotation id="url">"Saznajte više"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnost u pozadini, određene vizuelne efekte i funkcije i neke mrežne veze."</string> <string name="data_saver_description" msgid="4995164271550590517">"Radi smanjenja prijenosa podataka, Ušteda podataka sprečava da neke aplikacije šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može pristupiti podacima, ali će to činiti rjeđe. Naprimjer, to može značiti da se slike ne prikazuju dok ih ne dodirnete."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Uključiti Uštedu podataka?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string> @@ -2007,10 +2005,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> trenutno nije dostupna. Ovim upravlja aplikacija <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Saznajte više"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Ponovo aktiviraj aplikaciju"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Uključiti poslovne aplikacije?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Pristupite poslovnim aplikacijama i obavještenjima"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Uključi"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string> <string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno nije dostupna."</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 402402fda832..dfe8f45c605b 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilitza la drecera"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversió de colors"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Correcció de color"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode d\'una mà"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Atenuació extra"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S\'han mantingut premudes les tecles de volum. S\'ha activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S\'han mantingut premudes les tecles de volum. S\'ha desactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> @@ -1870,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualitzat per l\'administrador"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Suprimit per l\'administrador"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"D\'acord"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals, determinades funcions i algunes connexions a la xarxa.\n\n"<annotation id="url">"Més informació"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals, determinades funcions i algunes connexions a la xarxa."</string> <string name="data_saver_description" msgid="4995164271550590517">"Per reduir l\'ús de dades, la funció Economitzador de dades evita que determinades aplicacions enviïn o rebin dades en segon pla. L\'aplicació que estiguis fent servir podrà accedir a les dades, però menys sovint. Això vol dir, per exemple, que les imatges no es mostraran fins que no les toquis."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Activar l\'Economitzador de dades?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activa"</string> @@ -1976,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> no està disponible en aquests moments. Aquesta opció es gestiona a <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Més informació"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Reactiva l\'aplicació"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Activar aplicacions de treball?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Accedeix a les teves aplicacions de treball i a les notificacions"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Activa"</string> <string name="app_blocked_title" msgid="7353262160455028160">"L\'aplicació no està disponible"</string> <string name="app_blocked_message" msgid="542972921087873023">"Ara mateix, <xliff:g id="APP_NAME">%1$s</xliff:g> no està disponible."</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 17ddb0edb39c..2f9b6c005737 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -1915,10 +1915,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Aktualizováno administrátorem"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Smazáno administrátorem"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Spořič baterie zapíná tmavý motiv a omezuje či vypíná aktivitu na pozadí, některé vizuální efekty, některé funkce a připojení k některým sítím.\n\n"<annotation id="url">"Další informace"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Spořič baterie zapíná tmavý motiv a omezuje či vypíná aktivitu na pozadí, některé vizuální efekty, některé funkce a připojení k některým sítím."</string> <string name="data_saver_description" msgid="4995164271550590517">"Z důvodu snížení využití dat brání spořič dat některým aplikacím v odesílání nebo příjmu dat na pozadí. Aplikace, kterou právě používáte, data přenášet může, ale může tak činit méně často. V důsledku toho se například obrázky nemusejí zobrazit, dokud na ně neklepnete."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Chcete zapnout Spořič dat?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Zapnout"</string> @@ -2039,10 +2037,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikace <xliff:g id="APP_NAME_0">%1$s</xliff:g> momentálně není dostupná. Tato předvolba se spravuje v aplikaci <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Další informace"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Zrušit pozastavení aplikace"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Zapnout pracovní aplikace?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Získejte přístup ke svým pracovním aplikacím a oznámením"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Zapnout"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Aplikace není k dispozici"</string> <string name="app_blocked_message" msgid="542972921087873023">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> v tuto chvíli není k dispozici."</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index d4aced530bdc..2c91f8aa3882 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Brug genvej"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Ombytning af farver"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Farvekorrigering"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhåndstilstand"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra dæmpet belysning"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er aktiveret."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er deaktiveret."</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index bd01896a5cb3..f2904e001f01 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Text in Zwischenablage kopiert."</string> <string name="copied" msgid="4675902854553014676">"Kopiert"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat etwas von <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> eingefügt"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aus der Zwischenablage eingefügt"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat einen von dir kopierten Text eingefügt"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat ein von dir kopiertes Bild eingefügt"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat den von dir kopierten Inhalt eingefügt"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> wird vorbereitet"</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Apps werden gestartet..."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Start wird abgeschlossen..."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Display ausschalten?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Beim Einrichten deines Fingerabdrucks hast du die Ein-/Aus-Taste gedrückt.\n\nDamit wird üblicherweise das Display ausgeschaltet."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Ausschalten"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Abbrechen"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> läuft"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Tippe, um zum Spiel zurückzukehren"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Spiel wählen"</string> @@ -1874,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Von deinem Administrator aktualisiert"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Von deinem Administrator gelöscht"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Der Energiesparmodus aktiviert das dunkle Design und schränkt Hintergrundaktivitäten, einige Funktionen und optische Effekte und manche Netzwerkverbindungen ein oder deaktiviert sie.\n\n"<annotation id="url">"Weitere Informationen"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Der Energiesparmodus aktiviert das dunkle Design und schränkt Hintergrundaktivitäten, einige Funktionen und optische Effekte und manche Netzwerkverbindungen ein oder deaktiviert sie."</string> <string name="data_saver_description" msgid="4995164271550590517">"Der Datensparmodus verhindert zum einen, dass manche Apps im Hintergrund Daten senden oder empfangen, sodass weniger Daten verbraucht werden. Zum anderen werden die Datenzugriffe der gerade aktiven App eingeschränkt, was z. B. dazu führen kann, dass Bilder erst angetippt werden müssen, bevor sie sichtbar werden."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Datensparmodus aktivieren?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivieren"</string> @@ -1980,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ist momentan nicht verfügbar. Dies wird über die App \"<xliff:g id="APP_NAME_1">%2$s</xliff:g>\" verwaltet."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Weitere Informationen"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"App-Pausierung aufheben"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Geschäftliche Apps aktivieren?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Du erhältst Zugriff auf deine geschäftlichen Apps und Benachrichtigungen"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivieren"</string> <string name="app_blocked_title" msgid="7353262160455028160">"App ist nicht verfügbar"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist derzeit nicht verfügbar."</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 1f1166458eb9..2ed6cda658b4 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -1869,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ενημερώθηκε από τον διαχειριστή σας"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Διαγράφηκε από τον διαχειριστή σας"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Η Εξοικονόμηση μπαταρίας ενεργοποιεί το Σκούρο θέμα και περιορίζει ή απενεργοποιεί τη δραστηριότητα στο παρασκήνιο, ορισμένα οπτικά εφέ, συγκεκριμένες λειτουργίες και ορισμένες συνδέσεις δικτύου.\n\n"<annotation id="url">"Μάθετε περισσότερα"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Η Εξοικονόμηση μπαταρίας ενεργοποιεί το Σκούρο θέμα και περιορίζει ή απενεργοποιεί τη δραστηριότητα στο παρασκήνιο, ορισμένα οπτικά εφέ, συγκεκριμένες λειτουργίες και ορισμένες συνδέσεις δικτύου."</string> <string name="data_saver_description" msgid="4995164271550590517">"Προκειμένου να μειωθεί η χρήση δεδομένων, η Εξοικονόμηση δεδομένων αποτρέπει την αποστολή ή λήψη δεδομένων από ορισμένες εφαρμογές στο παρασκήνιο. Μια εφαρμογή που χρησιμοποιείτε αυτήν τη στιγμή μπορεί να χρησιμοποιήσει δεδομένα αλλά με μικρότερη συχνότητα. Για παράδειγμα, οι εικόνες μπορεί να μην εμφανίζονται μέχρι να τις πατήσετε."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Ενεργ.Εξοικονόμησης δεδομένων;"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Ενεργοποίηση"</string> @@ -1975,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Η εφαρμογή <xliff:g id="APP_NAME_0">%1$s</xliff:g> δεν είναι διαθέσιμη αυτήν τη στιγμή. Η διαχείριση πραγματοποιείται από την εφαρμογή <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Μάθετε περισσότερα"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Κατάργηση παύσης εφαρμογής"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Ενεργοπ. εφαρμογών εργασιών;"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Αποκτήστε πρόσβαση στις εφαρμογές εργασιών και τις ειδοποιήσεις"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Ενεργοποίηση"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Η εφαρμογή δεν είναι διαθέσιμη"</string> <string name="app_blocked_message" msgid="542972921087873023">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> δεν είναι διαθέσιμη αυτήν τη στιγμή."</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 00a752267d23..27b8f6908d40 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1869,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizado por el administrador"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado por el administrador"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"El modo Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales, ciertas funciones y algunas conexiones de red.\n\n"<annotation id="url">"Más información"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"El modo Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales, ciertas funciones y algunas conexiones de red."</string> <string name="data_saver_description" msgid="4995164271550590517">"Ahorro de datos evita que algunas aplicaciones envíen o reciban datos en segundo plano, lo que puede reducir el uso de datos. Una aplicación que estés usando de forma activa puede acceder a los datos, aunque con menos frecuencia. Esto significa que es posible que, por ejemplo, algunas imágenes no se muestren hasta que las toques."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"¿Activar Ahorro de datos?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string> @@ -1975,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> no está disponible en este momento. Esta opción se administra en <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Más información"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Anular pausa de aplicación"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"¿Activar apps de trabajo?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Accede a tus aplicaciones y notificaciones de trabajo"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Activar"</string> <string name="app_blocked_title" msgid="7353262160455028160">"La aplicación no está disponible"</string> <string name="app_blocked_message" msgid="542972921087873023">"En estos momentos, <xliff:g id="APP_NAME">%1$s</xliff:g> no está disponible."</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index dc56e12bd6ea..3d147056e1ae 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Lõikelauale kopeeritud tekst."</string> <string name="copied" msgid="4675902854553014676">"Kopeeritud"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kleepis rakendusest <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kleepis teie lõikelaualt"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kleepis teie kopeeritud teksti"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kleepis teie kopeeritud pildi"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kleepis teie kopeeritud sisu"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Rakenduse <xliff:g id="APPNAME">%1$s</xliff:g> ettevalmistamine."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Rakenduste käivitamine."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Käivitamise lõpuleviimine."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Kas lülitada ekraan välja?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Sõrmejälje seadistamisel vajutasite toitenuppu.\n\nSee lülitab ekraanikuva tavaliselt välja."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Lülita välja"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Tühista"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> töötab"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Puudutage mängu naasmiseks"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Valige mäng"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kasuta otseteed"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Värvide ümberpööramine"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Värvide korrigeerimine"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Ühekäerežiim"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Eriti tume"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati sisse."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati välja."</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Administraator on seda värskendanud"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Administraator on selle kustutanud"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Akusäästja lülitab sisse tumeda teema ja lülitab välja taustategevused, mõned visuaalsed efektid, teatud funktsioonid ja võrguühendused või piirab neid.\n\n"<annotation id="url">"Lisateave"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Akusäästja lülitab sisse tumeda teema ja lülitab välja taustategevused, mõned visuaalsed efektid, teatud funktsioonid ja võrguühendused või piirab neid."</string> <string name="data_saver_description" msgid="4995164271550590517">"Andmekasutuse vähendamiseks keelab andmemahu säästja mõne rakenduse puhul andmete taustal saatmise ja vastuvõtmise. Rakendus, mida praegu kasutate, pääseb andmesidele juurde, kuid võib seda teha väiksema sagedusega. Seetõttu võidakse näiteks pildid kuvada alles siis, kui neid puudutate."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Lülitada andmemahu säästja sisse?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Lülita sisse"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> pole praegu saadaval. Seda haldab rakendus <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Lisateave"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Jätka rakendust"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Lülitada töörakendused sisse?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Hankige juurdepääs oma töörakendustele ja märguannetele"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Lülita sisse"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Rakendus ei ole saadaval"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei ole praegu saadaval."</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 428832db4241..a9e6c544c502 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -906,7 +906,7 @@ <string name="emergency_calls_only" msgid="3057351206678279851">"Larrialdi-deiak soilik"</string> <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Sarea blokeatuta dago"</string> <string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"SIM txartela PUK bidez blokeatuta dago."</string> - <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Ikusi erabiltzailearen gida edo jarri bezeroarentzako laguntza-zerbitzuarekin harremanetan."</string> + <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Ikusi erabiltzailearentzako gida edo jarri bezeroarentzako laguntza-zerbitzuarekin harremanetan."</string> <string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"SIM txartela blokeatuta dago."</string> <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"SIM txartela desblokeatzen…"</string> <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%2$d</xliff:g> segundo barru."</string> @@ -1869,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Administratzaileak eguneratu du"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Administratzaileak ezabatu du"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Ados"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Bateria-aurrezleak gai iluna aktibatzen du, eta murriztu edo desaktibatu egiten ditu atzeko planoko jarduerak, zenbait efektu bisual, eta eginbide jakin eta sareko konexio batzuk.\n\n"<annotation id="url">"Lortu informazio gehiago"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Bateria-aurrezleak gai iluna aktibatzen du, eta murriztu edo desaktibatu egiten ditu atzeko planoko jarduerak, zenbait efektu bisual, eta eginbide jakin eta sareko konexio batzuk."</string> <string name="data_saver_description" msgid="4995164271550590517">"Datuen erabilera murrizteko, atzeko planoan datuak bidaltzea eta jasotzea galarazten die datu-aurrezleak aplikazio batzuei. Une honetan erabiltzen ari zaren aplikazio batek datuak atzitu ahal izango ditu, baina baliteke maiztasun txikiagoarekin atzitzea. Horrela, adibidez, baliteke irudiak ez erakustea haiek sakatu arte."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Datu-aurrezlea aktibatu nahi duzu?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktibatu"</string> @@ -1975,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ez dago erabilgarri une honetan. Haren erabilgarritasuna <xliff:g id="APP_NAME_1">%2$s</xliff:g> aplikazioak kudeatzen du."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Lortu informazio gehiago"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Kendu pausaldia"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Laneko aplikazioak aktibatu nahi dituzu?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Atzitu laneko aplikazioak eta jakinarazpenak"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Aktibatu"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Aplikazioa ez dago erabilgarri"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ez dago erabilgarri une honetan."</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index b66ad5ad0aec..42e084c03c87 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استفاده از میانبر"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"وارونگی رنگ"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"تصحیح رنگ"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"حالت تکحرکت"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"بسیار کمنور"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> روشن شد."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> خاموش شد."</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 270d594a4646..411618736dde 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Teksti kopioitu leikepöydälle."</string> <string name="copied" msgid="4675902854553014676">"Kopioitu"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> liitetty täältä: <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> liitti leikepöydältäsi"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> on liittänyt kopioimasi tekstin"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> on liittänyt kopioimasi kuvan"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> on liittänyt kopioimasi sisällön"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Valmistellaan: <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Käynnistetään sovelluksia."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Viimeistellään päivitystä."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Sammutetaanko näyttö?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Painoit virtapainiketta ottaessasi sormenjäljen käyttöön.\n\nTämä saa yleensä näytön sammumaan."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Laita pois päältä"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Peru"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> käynnissä"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Palaa peliin napauttamalla"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Valitse peli"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Käytä pikanäppäintä"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Käänteiset värit"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Värinkorjaus"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Yhden käden moodi"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Erittäin himmeä"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin päälle."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin pois päältä."</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Järjestelmänvalvoja päivitti tämän."</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Järjestelmänvalvoja poisti tämän."</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Virransäästö laittaa tumman teeman päälle ja rajoittaa tai laittaa pois päältä taustatoimintoja, tiettyjä ominaisuuksia sekä joitakin visuaalisia tehosteita ja verkkoyhteyksiä.\n\n"<annotation id="url">"Lue lisää"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Virransäästö laittaa tumman teeman päälle ja rajoittaa tai laittaa pois päältä taustatoimintoja, tiettyjä ominaisuuksia sekä joitakin visuaalisia tehosteita ja verkkoyhteyksiä."</string> <string name="data_saver_description" msgid="4995164271550590517">"Data Saver estää joitakin sovelluksia lähettämästä tai vastaanottamasta tietoja taustalla, jotta datan käyttöä voidaan vähentää. Käytössäsi oleva sovellus voi yhä käyttää dataa, mutta se saattaa tehdä niin tavallista harvemmin. Tämä voi tarkoittaa esimerkiksi sitä, että kuva ladataan vasta, kun kosketat sitä."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Otetaanko Data Saver käyttöön?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Ota käyttöön"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ei ole juuri nyt saatavilla. Tästä vastaa <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Lue lisää"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Peru keskeytys"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Käytetäänkö työsovelluksia?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Palauta työsovellukset ja ilmoitukset käyttöön"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Ota käyttöön"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Sovellus ei ole käytettävissä"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei ole nyt käytettävissä."</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index d717f5bfc3d4..b00bce07fdb4 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Le texte a été copié dans le presse-papiers."</string> <string name="copied" msgid="4675902854553014676">"Copié"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> collé à partir de <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé du contenu de votre presse-papiers"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé du texte que vous avez copié"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé une image que vous avez copiée"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé le contenu que vous avez copié"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Préparation de <xliff:g id="APPNAME">%1$s</xliff:g> en cours…"</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Lancement des applications…"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Finalisation de la mise à jour."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Éteindre l\'écran?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Pendant que vous configuriez votre empreinte digitale, vous avez appuyé sur l\'interrupteur.\n\nAppuyer sur ce bouton ferme habituellement l\'écran."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Éteindre"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Annuler"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Touchez pour revenir au jeu"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Choisissez un jeu"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utiliser le raccourci"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversion des couleurs"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Correction des couleurs"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode Une main"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Réduction supplémentaire de la luminosité"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume maintenues enfoncées. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume maintenues enfoncées. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Mise à jour par votre administrateur"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Supprimé par votre administrateur"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Le mode Économiseur de pile active le mode sombre et limite ou désactive l\'activité en arrière-plan, certains effets visuels, d\'autres fonctionnalités et certaines connexions réseau.\n\n"<annotation id="url">"En savoir plus"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Le mode Économiseur de pile active le thème sombre et limite ou désactive l\'activité en arrière-plan, certains effets visuels, certaines fonctionnalités et certaines connexions réseau."</string> <string name="data_saver_description" msgid="4995164271550590517">"Pour aider à diminuer l\'utilisation des données, la fonctionnalité Économiseur de données empêche certaines applications d\'envoyer ou de recevoir des données en arrière-plan. Une application que vous utilisez actuellement peut accéder à des données, mais peut le faire moins souvent. Cela peut signifier, par exemple, que les images ne s\'affichent pas jusqu\'à ce que vous les touchiez."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'économiseur de données?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"L\'application <xliff:g id="APP_NAME_0">%1$s</xliff:g> n\'est pas accessible pour le moment. Ceci est géré par <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"En savoir plus"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Réactiver l\'application"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Activer applis professionnelles?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Accédez à vos applications professionnelles et à vos notifications"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Activer"</string> <string name="app_blocked_title" msgid="7353262160455028160">"L\'application n\'est pas accessible"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas accessible pour le moment."</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 28990b47d0c0..22a1bcd4921a 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -1869,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Mis à jour par votre administrateur"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Supprimé par votre administrateur"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"L\'économiseur de batterie active le thème sombre et limite ou désactive les activités en arrière-plan ainsi que certains effets visuels, fonctionnalités et connexions réseau.\n\n"<annotation id="url">"En savoir plus"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"L\'économiseur de batterie active le thème sombre et limite ou désactive les activités en arrière-plan ainsi que certains effets visuels, fonctionnalités et connexions réseau."</string> <string name="data_saver_description" msgid="4995164271550590517">"Pour réduire la consommation des données, l\'Économiseur de données empêche certaines applis d\'envoyer ou de recevoir des données en arrière-plan. Les applis que vous utiliserez pourront toujours accéder aux données, mais le feront moins fréquemment. Par exemple, les images pourront ne pas s\'afficher tant que vous n\'aurez pas appuyé pas dessus."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Activer l\'économiseur de données ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activer"</string> @@ -1975,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"L\'application <xliff:g id="APP_NAME_0">%1$s</xliff:g> n\'est pas disponible pour le moment. Cette suspension est gérée par l\'application <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"En savoir plus"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Débloquer l\'application"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Activer les applis pros ?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Accéder à vos applis et notifications professionnelles"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Activer"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Application non disponible"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas disponible pour le moment."</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index e2bd3cb31d64..bbc868f28af1 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -1869,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizado polo teu administrador"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado polo teu administrador"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Coa función Aforro de batería, actívase o tema escuro e restrínxense ou desactívanse a actividade en segundo plano, algúns efectos visuais e determinadas funcións e conexións de rede.\n\n"<annotation id="url">"Máis información"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Coa función Aforro de batería, actívase o tema escuro e restrínxense ou desactívanse a actividade en segundo plano, algúns efectos visuais e determinadas funcións e conexións de rede."</string> <string name="data_saver_description" msgid="4995164271550590517">"Para contribuír a reducir o uso de datos, o aforro de datos impide que algunhas aplicacións envíen ou reciban datos en segundo plano. Cando esteas utilizando unha aplicación, esta poderá acceder aos datos, pero é posible que o faga con menos frecuencia. Por exemplo, poida que as imaxes non se mostren ata que as toques."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Queres activar o aforro de datos?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string> @@ -1975,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"A aplicación <xliff:g id="APP_NAME_0">%1$s</xliff:g> non está dispoñible neste momento. A dispoñibilidade está xestionada por <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Máis información"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Volver activar aplicación"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Activar as apps do traballo?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Obtén acceso ás túas aplicacións e notificacións do traballo"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Activar"</string> <string name="app_blocked_title" msgid="7353262160455028160">"A aplicación non está dispoñible"</string> <string name="app_blocked_message" msgid="542972921087873023">"A aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> non está dispoñible neste momento."</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 4f32f29941fe..e837891b5634 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -1718,8 +1718,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"શૉર્ટકટનો ઉપયોગ કરો"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"વિપરીત રંગમાં બદલવું"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"રંગ સુધારણા"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"એક-હાથે વાપરો મોડ"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"એક્સ્ટ્રા ડિમ"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ચાલુ કરી."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> બંધ કરી."</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index b4594e1bcdb3..feacceeb62fe 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1869,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"आपके व्यवस्थापक ने अपडेट किया है"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"आपके व्यवस्थापक ने हटा दिया है"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ठीक है"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"बैटरी सेवर, गहरे रंग वाली थीम को चालू करता है. साथ ही, यह बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, कुछ खास सुविधाओं, और कुछ खास तरह के इंटरनेट कनेक्शन इस्तेमाल करने से डिवाइस को रोकता है या इन्हें बंद कर देता है.\n\n"<annotation id="url">"ज़्यादा जानें"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"बैटरी सेवर, गहरे रंग वाली थीम को चालू करता है. साथ ही, यह बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, कुछ खास सुविधाओं, और कुछ खास तरह के इंटरनेट कनेक्शन इस्तेमाल करने से डिवाइस को रोकता है या इन्हें बंद कर देता है."</string> <string name="data_saver_description" msgid="4995164271550590517">"डेटा खर्च को कम करने के लिए, डेटा बचाने की सेटिंग कुछ ऐप्लिकेशन को बैकग्राउंड में डेटा भेजने या डेटा पाने से रोकती है. फ़िलहाल, आप जिस ऐप्लिकेशन का इस्तेमाल कर रहे हैं वह डेटा ऐक्सेस कर सकता है, लेकिन ऐसा कभी-कभी ही हो पाएगा. उदाहरण के लिए, इमेज तब तक दिखाई नहीं देंगी जब तक कि आप उन पर टैप नहीं करते."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा बचाने की सेटिंग चालू करें?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"चालू करें"</string> @@ -1975,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"फ़िलहाल <xliff:g id="APP_NAME_0">%1$s</xliff:g> उपलब्ध नहीं है. इसे <xliff:g id="APP_NAME_1">%2$s</xliff:g> के ज़रिए प्रबंधित किया जाता है."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"ज़्यादा जानें"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ऐप्लिकेशन पर लगी रोक हटाएं"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन चालू करना चाहते हैं?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"अपने ऑफ़िस के काम से जुड़े ऐप्लिकेशन और सूचनाओं का ऐक्सेस पाएं"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"चालू करें"</string> <string name="app_blocked_title" msgid="7353262160455028160">"ऐप्लिकेशन उपलब्ध नहीं है"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> इस समय उपलब्ध नहीं है."</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 8759f7ad098b..490cb6eb3ced 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -1892,10 +1892,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao administrator"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao administrator"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"U redu"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Štednja baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizualne efekte, određene značajke i neke mrežne veze.\n\n"<annotation id="url">"Saznajte više"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Štednja baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizualne efekte, određene značajke i neke mrežne veze."</string> <string name="data_saver_description" msgid="4995164271550590517">"Da bi se smanjio podatkovni promet, značajka Štednja podatkovnog prometa onemogućuje nekim aplikacijama slanje ili primanje podataka u pozadini. Aplikacija koju trenutačno upotrebljavate može pristupiti podacima, no možda će to činiti rjeđe. To može značiti da se, na primjer, slike neće prikazivati dok ih ne dodirnete."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Uključiti Štednju podatkovnog prometa?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Uključi"</string> @@ -2007,10 +2005,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> trenutačno nije dostupna. Ovime upravlja aplikacija <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Saznajte više"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Prekini pauzu aplikacije"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Uključiti poslovne aplikacije?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Pristupite svojim poslovnim aplikacijama i obavijestima"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Uključi"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string> <string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutačno nije dostupna."</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index dd4afa53c2c5..4671f4a6f4a9 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Billentyűparancs használata"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Színek invertálása"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Színkorrekció"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Egykezes mód"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extrasötét"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> bekapcsolva."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kikapcsolva."</string> @@ -1870,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"A rendszergazda által frissítve"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"A rendszergazda által törölve"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Az Akkumulátorkímélő mód bekapcsolja a Sötét témát, és korlátozza vagy kikapcsolja a háttérbeli tevékenységeket, valamint bizonyos vizuális effekteket, funkciókat és hálózati kapcsolatokat.\n\n"<annotation id="url">"További információ"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Az Akkumulátorkímélő mód bekapcsolja a Sötét témát, és korlátozza vagy kikapcsolja a háttérbeli tevékenységeket, valamint bizonyos vizuális effekteket, funkciókat és hálózati kapcsolatokat."</string> <string name="data_saver_description" msgid="4995164271550590517">"Az adatforgalom csökkentése érdekében az Adatforgalom-csökkentő megakadályozza, hogy egyes alkalmazások adatokat küldjenek vagy fogadjanak a háttérben. Az Ön által jelenleg használt alkalmazások hozzáférhetnek az adatokhoz, de csak ritkábban. Ez például azt jelentheti, hogy a képek csak rákoppintás után jelennek meg."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Bekapcsolja az Adatforgalom-csökkentőt?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Bekapcsolás"</string> @@ -1976,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"A(z) <xliff:g id="APP_NAME_0">%1$s</xliff:g> alkalmazás jelenleg nem áll rendelkezésre. Ezt a(z) <xliff:g id="APP_NAME_1">%2$s</xliff:g> kezeli."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"További információ"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Alkalmazás szüneteltetésének feloldása"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Bekapcsolja a munkaappokat?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Hozzáférést kaphat munkahelyi alkalmazásaihoz és értesítéseihez"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Bekapcsolás"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Az alkalmazás nem hozzáférhető"</string> <string name="app_blocked_message" msgid="542972921087873023">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> jelenleg nem hozzáférhető."</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 988010bcb1eb..15bf3e96bb7c 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Տեքստը պատճենված է սեղմատախտակին:"</string> <string name="copied" msgid="4675902854553014676">"Պատճենվեց"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> հավելվածը տվյալներ տեղադրեց <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> հավելվածից"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> հավելվածը տվյալներ է տեղադրել ձեր սեղմատախտակից"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> հավելվածը տեղադրեց ձեր պատճենած տեքստը"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> հավելվածը տեղադրեց ձեր պատճենած պատկերը"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> հավելվածը տեղադրեց ձեր պատճենած բովանդակությունը"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> հավելվածը պատրաստվում է:"</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Հավելվածները մեկնարկում են:"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Բեռնումն ավարտվում է:"</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Անջատե՞լ էկրանը"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Մատնահետքը կարգավորելու ժամանակ դուք սեղմել եք սնուցման կոճակը։\n\nՍովորաբար դրա արդյունքում էկրանն անջատվում է։"</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Անջատել"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Չեղարկել"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g>-ն աշխատում է"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Հպեք՝ խաղին վերադառնալու համար"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Ընտրեք խաղ"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Օգտագործել դյուրանցումը"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Գունաշրջում"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Գունաշտկում"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Մեկ ձեռքի ռեժիմ"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Հավելյալ խամրեցում"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը միացավ։"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունն անջատվեց։"</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Թարմացվել է ձեր ադմինիստրատորի կողմից"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Ջնջվել է ձեր ադմինիստրատորի կողմից"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Եղավ"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"«Մարտկոցի տնտեսում» գործառույթը միացնում է մուգ թեման և անջատում կամ սահմանափակում է աշխատանքը ֆոնային ռեժիմում, որոշ վիզուալ էֆեկտներ, կապի ծառայություններ և այլ գործառույթներ։\n\n"<annotation id="url">"Իմանալ ավելին"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"«Մարտկոցի տնտեսում» գործառույթը միացնում է մուգ թեման և անջատում կամ սահմանափակում է աշխատանքը ֆոնային ռեժիմում, որոշ վիզուալ էֆեկտներ, կապի ծառայություններ և այլ գործառույթներ։"</string> <string name="data_saver_description" msgid="4995164271550590517">"Թրաֆիկի տնտեսման ռեժիմում որոշ հավելվածների համար տվյալների ֆոնային փոխանցումն անջատված է։ Հավելվածը, որն օգտագործում եք, կարող է տվյալներ փոխանցել և ստանալ, սակայն ոչ այնքան հաճախ: Օրինակ՝ պատկերները կցուցադրվեն միայն դրանց վրա սեղմելուց հետո։"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Միացնե՞լ թրաֆիկի տնտեսումը"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Միացնել"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> հավելվածը հասանելի չէ։ Դրա աշխատանքը սահմանափակում է <xliff:g id="APP_NAME_1">%2$s</xliff:g> հավելվածը։"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Մանրամասն"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Չեղարկել դադարեցումը"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Միացնե՞լ հավելվածները"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Միացրեք աշխատանքային հավելվածներն ու ծանուցումները"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Միացնել"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Հավելվածը հասանելի չէ"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն այս պահին հասանելի չէ։"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 990f94dcc807..b4c9aa145673 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Teks disalin ke papan klip."</string> <string name="copied" msgid="4675902854553014676">"Disalin"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ditempelkan dari <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempel dari papan klip"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan teks yang Anda salin"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan gambar yang Anda salin"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan konten yang Anda salin"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Menyiapkan <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Memulai aplikasi."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Menyelesaikan boot."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Nonaktifkan layar?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Saat menyiapkan sidik jari, Anda menekan Tombol daya.\n\nTindakan ini biasanya akan menonaktifkan layar."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Nonaktifkan"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Batal"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> sedang berjalan"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ketuk untuk kembali ke game"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pilih game"</string> @@ -1874,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Diupdate oleh admin Anda"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Dihapus oleh admin Anda"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Oke"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Penghemat Baterai akan mengaktifkan Tema gelap dan membatasi atau menonaktifkan aktivitas latar belakang, beberapa efek visual, fitur tertentu, dan beberapa koneksi jaringan.\n\n"<annotation id="url">"Pelajari lebih lanjut"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Penghemat Baterai akan mengaktifkan Tema gelap dan membatasi atau menonaktifkan aktivitas latar belakang, beberapa efek visual, fitur tertentu, dan beberapa koneksi jaringan."</string> <string name="data_saver_description" msgid="4995164271550590517">"Untuk membantu mengurangi penggunaan data, Penghemat Data mencegah beberapa aplikasi mengirim atau menerima data di latar belakang. Aplikasi yang sedang digunakan dapat mengakses data, tetapi frekuensinya agak lebih jarang. Misalnya saja, gambar hanya akan ditampilkan setelah diketuk."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Aktifkan Penghemat Data?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktifkan"</string> @@ -1980,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> saat ini tidak tersedia. Aplikasi ini dikelola oleh <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Pelajari lebih lanjut"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Batalkan jeda aplikasi"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Aktifkan aplikasi kerja?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Dapatkan akses ke aplikasi kerja dan notifikasi"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Aktifkan"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Aplikasi tidak tersedia"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak tersedia saat ini."</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index ba889b675dd8..e8f807b28a95 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Nota flýtileið"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Umsnúningur lita"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Litaleiðrétting"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Einhent stilling"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Mjög dökkt"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Hljóðstyrkstökkum haldið inni. Kveikt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Hljóðstyrkstökkum haldið inni. Slökkt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> @@ -1870,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Kerfisstjóri uppfærði"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Kerfisstjóri eyddi"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Í lagi"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Rafhlöðusparnaður kveikir á dökku þema og takmarkar eða slekkur á bakgrunnsvirkni, sumum áhrifum, tilteknum eiginleikum og sumum nettengingum.\n\n"<annotation id="url">"Nánar"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Rafhlöðusparnaður kveikir á dökku þema og takmarkar eða slekkur á bakgrunnsvirkni, sumum áhrifum, tilteknum eiginleikum og sumum nettengingum."</string> <string name="data_saver_description" msgid="4995164271550590517">"Gagnasparnaður getur hjálpað til við að draga úr gagnanotkun með því að hindra forrit í að senda eða sækja gögn í bakgrunni. Forrit sem er í notkun getur náð í gögn, en gerir það kannski sjaldnar. Niðurstaðan getur verið að myndir eru ekki birtar fyrr en þú ýtir á þær, svo dæmi sé tekið."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Kveikja á gagnasparnaði?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Kveikja"</string> @@ -1976,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> er ekki í boði eins og er. Þessu er stjórnað með <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Nánari upplýsingar"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Halda áfram að nota"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Kveikja á vinnuforritum?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Fá aðgang að vinnuforritum og tilkynningum"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Kveikja"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Forrit er ekki tiltækt"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ekki tiltækt núna."</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 411c4e145215..c4697ba28a30 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usa scorciatoia"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversione dei colori"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Correzione del colore"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modalità one-hand"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Attenuazione extra"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> attivato."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> disattivato."</string> @@ -1870,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Aggiornato dall\'amministratore"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminato dall\'amministratore"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"L\'opzione Risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete.\n\n"<annotation id="url">"Scopri di più"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"L\'opzione Risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete."</string> <string name="data_saver_description" msgid="4995164271550590517">"Per contribuire a ridurre l\'utilizzo dei dati, la funzione Risparmio dati impedisce ad alcune app di inviare o ricevere dati in background. Un\'app in uso può accedere ai dati, ma potrebbe farlo con meno frequenza. Esempio: le immagini non vengono visualizzate finché non le tocchi."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Attivare Risparmio dati?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Attiva"</string> @@ -1976,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> non è al momento disponibile. Viene gestita tramite <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Scopri di più"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Riattiva app"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Attivare le app di lavoro?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Attiva l\'accesso alle app di lavoro e alle notifiche"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Attiva"</string> <string name="app_blocked_title" msgid="7353262160455028160">"L\'app non è disponibile"</string> <string name="app_blocked_message" msgid="542972921087873023">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> non è al momento disponibile."</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 9f705fb854fb..55e90bc50e0c 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1028,8 +1028,7 @@ <string name="text_copied" msgid="2531420577879738860">"הטקסט הועתק ללוח."</string> <string name="copied" msgid="4675902854553014676">"ההעתקה בוצעה"</string> <string name="pasted_from_app" msgid="5627698450808256545">"האפליקציה <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> הודבקה מ-<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> הודבקה מהלוח שלך"</string> <string name="pasted_text" msgid="4298871641549173733">"טקסט שהעתקת הודבק על ידי <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string> <string name="pasted_image" msgid="4729097394781491022">"תמונה שהעתקת הודבקה על ידי <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string> <string name="pasted_content" msgid="646276353060777131">"התוכן שהעתקת הודבק על ידי <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string> @@ -1300,14 +1299,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"המערכת מכינה את <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"מתבצעת הפעלה של אפליקציות."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"תהליך האתחול בשלבי סיום."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"לכבות את המסך?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"כשהגדרת את טביעת האצבע, לחצת על לחצן ההפעלה.\n\nלרוב, הפעולה הזו מכבה את המסך."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"כיבוי"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ביטול"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"האפליקציה <xliff:g id="APP">%1$s</xliff:g> פועלת"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"יש להקיש כדי לחזור למשחק"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"בחירת משחק"</string> @@ -1762,8 +1757,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"שימוש בקיצור הדרך"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"היפוך צבעים"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"תיקון צבעים"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"מצב שימוש ביד אחת"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"מעומעם במיוחד"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. שירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הופעל."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. שירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הושבת."</string> @@ -1921,10 +1915,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"עודכנה על ידי מנהל המערכת"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"נמחקה על ידי מנהל המערכת"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"אישור"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"התכונה \'חיסכון בסוללה\' מפעילה עיצוב כהה ומגבילה או מכבה פעילות ברקע, חלק מהאפקטים החזותיים, תכונות מסוימות וחלק מהחיבורים לרשתות.\n\n"<annotation id="url">"מידע נוסף"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"התכונה \'חיסכון בסוללה\' מפעילה עיצוב כהה ומגבילה או מכבה פעילות ברקע, חלק מהאפקטים החזותיים, תכונות מסוימות וחלק מהחיבורים לרשתות."</string> <string name="data_saver_description" msgid="4995164271550590517">"כדי לסייע בהפחתת השימוש בנתונים, חוסך הנתונים (Data Saver) מונע מאפליקציות מסוימות לשלוח או לקבל נתונים ברקע. אפליקציות שבהן נעשה שימוש כרגע יכולות לגשת לנתונים, אבל בתדירות נמוכה יותר. המשמעות היא, למשל, שתמונות יוצגו רק לאחר שמקישים עליהן."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"להפעיל את חוסך הנתונים?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"הפעלה"</string> @@ -2045,10 +2037,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"האפליקציה <xliff:g id="APP_NAME_0">%1$s</xliff:g> לא זמינה כרגע. אפשר לנהל זאת באפליקציה <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"מידע נוסף"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ביטול ההשהיה של האפליקציה"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"להפעיל את האפליקציות לעבודה?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"קבלת גישה להתראות ולאפליקציות לעבודה"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"הפעלה"</string> <string name="app_blocked_title" msgid="7353262160455028160">"האפליקציה לא זמינה"</string> <string name="app_blocked_message" msgid="542972921087873023">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> לא זמינה בשלב זה."</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 7238cde459a3..f5a8ba1c0217 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ショートカットを使用"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"色反転"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"色補正"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"片手モード"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"さらに輝度を下げる"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が ON になりました。"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が OFF になりました。"</string> @@ -1870,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"管理者により更新されています"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"管理者により削除されています"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"バッテリー セーバーを有効にすると、ダークテーマが ON になり、バックグラウンド アクティビティ、一部の視覚効果、特定の機能、一部のネットワーク接続が制限されるか OFF になります。\n\n"<annotation id="url">"詳細"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"バッテリー セーバーを有効にすると、ダークテーマが ON になり、バックグラウンド アクティビティ、一部の視覚効果、特定の機能、一部のネットワーク接続が制限されるか OFF になります。"</string> <string name="data_saver_description" msgid="4995164271550590517">"データセーバーは、一部のアプリによるバックグラウンドでのデータ送受信を停止することでデータ使用量を抑制します。使用中のアプリからデータを送受信することはできますが、その頻度は低くなる場合があります。この影響として、たとえば画像はタップしないと表示されないようになります。"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"データセーバーを ON にしますか?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ON にする"</string> @@ -1976,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"現在、<xliff:g id="APP_NAME_0">%1$s</xliff:g> は使用できません。このアプリの使用は [<xliff:g id="APP_NAME_1">%2$s</xliff:g>] で管理されています。"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"詳細"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"アプリの一時停止を解除"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"仕事用アプリを ON にしますか?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"仕事用のアプリや通知を利用する"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"ON にする"</string> <string name="app_blocked_title" msgid="7353262160455028160">"アプリの利用不可"</string> <string name="app_blocked_message" msgid="542972921087873023">"現在 <xliff:g id="APP_NAME">%1$s</xliff:g> はご利用になれません。"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index a2b337d2b6fb..0cea47a37d37 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"ტექსტი დაკოპირებულია გაცვლის ბუფერში."</string> <string name="copied" msgid="4675902854553014676">"დაკოპირდა"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>-დან <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>-ში ჩასმული"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ჩასმულია თქვენი გაცვლის ბუფერიდან"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>-მ(ა) ჩასვა თქვენ მიერ დაკოპირებული ტექსტი"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>-მ(ა) ჩასვა თქვენ მიერ დაკოპირებული სურათი"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>-მ(ა) ჩასვა თქვენ მიერ დაკოპირებული კონტენტი"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"ემზადება <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"აპების ჩართვა"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"ჩატვირთვის დასასრული."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"გამოირთოს ეკრანი?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"თითის ანაბეჭდის დაყენებისას ჩართვის ღილაკს დააჭირეთ.\n\nეს, ჩვეულებრივ, თქვენს ეკრანს გამორთავს."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"გამორთვა"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"გაუქმება"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> გაშვებულია"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"შეეხეთ თამაშში დასაბრუნებლად"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"აირჩიეთ თამაში"</string> @@ -1874,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"განახლებულია თქვენი ადმინისტრატორის მიერ"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"წაიშალა თქვენი ადმინისტრატორის მიერ"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"კარგი"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"ბატარეის დამზოგი ჩართავს მუქ თემას და შეზღუდავს ან გამორთავს ფონურ აქტივობას, ზოგიერთ ვიზუალურ ეფექტს, გარკვეულ ფუნქციებსა და ზოგიერთ ქსელთან კავშირს.\n\n"<annotation id="url">"შეიტყვეთ მეტი"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"ბატარეის დამზოგი ჩართავს მუქ თემას და შეზღუდავს ან გამორთავს ფონურ აქტივობას, ზოგიერთ ვიზუალურ ეფექტს, გარკვეულ ფუნქციებსა და ზოგიერთ ქსელთან კავშირს."</string> <string name="data_saver_description" msgid="4995164271550590517">"მობილური ინტერნეტის მოხმარების შემცირების მიზნით, მონაცემთა დამზოგველი ზოგიერთ აპს ფონურ რეჟიმში მონაცემთა გაგზავნასა და მიღებას შეუზღუდავს. თქვენ მიერ ამჟამად გამოყენებული აპი მაინც შეძლებს მობილურ ინტერნეტზე წვდომას, თუმცა ამას ნაკლები სიხშირით განახორციელებს. ეს ნიშნავს, რომ, მაგალითად, სურათები არ გამოჩნდება მანამ, სანამ მათ საგანგებოდ არ შეეხებით."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ჩაირთოს მონაცემთა დამზოგველი?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ჩართვა"</string> @@ -1980,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ამჟამად მიუწვდომელია. ის იმართება <xliff:g id="APP_NAME_1">%2$s</xliff:g>-ის მიერ."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"შეიტყვეთ მეტი"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"აპის დაპაუზების გაუქმება"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"გსურთ სამსახურის აპების ჩართვა?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"თქვენი სამსახურის აპებსა და შეტყობინებებზე წვდომის მოპოვება"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"ჩართვა"</string> <string name="app_blocked_title" msgid="7353262160455028160">"აპი მიუწვდომელია"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ამჟამად მიუწვდომელია."</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index e7485454ee5c..6938a088c757 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Мәтін ақпарат алмастыру қорына сақталды."</string> <string name="copied" msgid="4675902854553014676">"Көшірілді"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> қолданбасынан <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> қолданбасына қойылды."</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> қолданбасы буферіңізден алынған деректерді қойды."</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> сіз көшірген мәтінді қойды."</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> сіз көшірген суретті қойды."</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> сіз көшірген мазмұнды қойды."</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> дайындалуда."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Қолданбалар іске қосылуда."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Қосуды аяқтауда."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Экранды өшіру керек пе?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Саусақ ізіңізді орнату кезінде қуат түймесін басып қалдыңыз.\n\nБұл әрекет әдетте экранды өшіреді."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Өшіру"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Бас тарту"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> қосылған"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ойынды жалғастыру үшін түртіңіз"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Ойынды таңдаңыз"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Төте жолды пайдалану"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Түс инверсиясы"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Түсті түзету"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Бір қолмен басқару режимі"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Экранды қарайту"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Пайдаланушы дыбыс деңгейі пернелерін басып ұстап тұрды. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қосулы."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дыбыс деңгейі пернелерін басып тұрған соң, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өшірілді."</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Әкімші жаңартқан"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Әкімші жойған"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Жарайды"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Батареяны үнемдеу режимі қараңғы тақырыпты іске қосады және фондық әрекеттерге, кейбір визуалдық әсерлерге, белгілі бір функциялар мен кейбір желі байланыстарына шектеу қояды немесе оларды өшіреді.\n\n"<annotation id="url">"Толығырақ"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Батареяны үнемдеу режимі қараңғы тақырыпты іске қосады және фондық әрекеттерге, кейбір визуалдық әсерлерге, белгілі бір функциялар мен кейбір желі байланыстарына шектеу қояды немесе оларды өшіреді."</string> <string name="data_saver_description" msgid="4995164271550590517">"Дерек шығынын азайту үшін Трафикті үнемдеу режимінде кейбір қолданбаларға деректі фондық режимде жіберуге және алуға тыйым салынады. Ашық тұрған қолданба деректі шектеулі шамада пайдаланады (мысалы, кескіндер оларды түрткенге дейін көрсетілмейді)."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Трафикті үнемдеу режимі қосылсын ба?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Қосу"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> дәл қазір қолжетімді емес. Ол <xliff:g id="APP_NAME_1">%2$s</xliff:g> арқылы басқарылады."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Толығырақ"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Қолданбаны қайта қосу"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Жұмыс қолданбаларын қосасыз ба?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Жұмыс қолданбалары мен хабарландыруларына рұқсат алу"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Қосу"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Қолданба қолжетімді емес"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> қазір қолжетімді емес."</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 31f2b50c8975..69de7d075781 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ប្រើប្រាស់ផ្លូវកាត់"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"បញ្ច្រាសពណ៌"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"ការកែពណ៌"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"មុខងារប្រើដៃម្ខាង"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ពន្លឺតិចខ្លាំង"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"បានសង្កត់គ្រាប់ចុចកម្រិតសំឡេងជាប់។ បានបើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"បានសង្កត់គ្រាប់ចុចកម្រិតសំឡេងជាប់។ បានបិទ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string> @@ -1870,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"ធ្វើបច្ចុប្បន្នភាពដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"លុបដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"យល់ព្រម"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"មុខងារសន្សំថ្មបើករចនាប័ទ្មងងឹត និងដាក់កំហិត ឬបិទសកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពលរូបភាពមួយចំនួន មុខងារជាក់លាក់ និងការតភ្ជាប់បណ្ដាញមួយចំនួន។\n\n"<annotation id="url">"ស្វែងយល់បន្ថែម"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"មុខងារសន្សំថ្មបើករចនាប័ទ្មងងឹត និងដាក់កំហិត ឬបិទសកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពលរូបភាពមួយចំនួន មុខងារជាក់លាក់ និងការតភ្ជាប់បណ្ដាញមួយចំនួន។"</string> <string name="data_saver_description" msgid="4995164271550590517">"ដើម្បីជួយកាត់បន្ថយការប្រើប្រាស់ទិន្នន័យ កម្មវិធីសន្សំសំចៃទិន្នន័យរារាំងកម្មវិធីមួយចំនួនមិនឲ្យបញ្ជូន ឬទទួលទិន្នន័យនៅផ្ទៃខាងក្រោយទេ។ កម្មវិធីដែលអ្នកកំពុងប្រើនាពេលបច្ចុប្បន្នអាចចូលប្រើប្រាស់ទិន្នន័យបាន ប៉ុន្តែអាចនឹងមិនញឹកញាប់ដូចមុនទេ។ ឧទាហរណ៍ រូបភាពមិនបង្ហាញទេ លុះត្រាតែអ្នកប៉ះរូបភាពទាំងនោះ។"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"បើកកម្មវិធីសន្សំសំចៃទិន្នន័យ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"បើក"</string> @@ -1976,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> មិនអាចប្រើបានទេនៅពេលនេះ។ វាស្ថិតក្រោមការគ្រប់គ្រងរបស់ <xliff:g id="APP_NAME_1">%2$s</xliff:g> ។"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"ស្វែងយល់បន្ថែម"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ឈប់ផ្អាកកម្មវិធី"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"បើកកម្មវិធីការងារឬ?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"ទទួលបានសិទ្ធិចូលប្រើការជូនដំណឹង និងកម្មវិធីការងាររបស់អ្នក"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"បើក"</string> <string name="app_blocked_title" msgid="7353262160455028160">"មិនអាចប្រើកម្មវិធីនេះបានទេ"</string> <string name="app_blocked_message" msgid="542972921087873023">"មិនអាចប្រើ <xliff:g id="APP_NAME">%1$s</xliff:g> នៅពេលនេះបានទេ។"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index f3d0d58c7bd8..403a76aacb20 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"ಪಠ್ಯವನ್ನು ಕ್ಲಿಪ್ಬೋರ್ಡ್ಗೆ ನಕಲಿಸಲಾಗಿದೆ."</string> <string name="copied" msgid="4675902854553014676">"ನಕಲಿಸಲಾಗಿದೆ"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ಅನ್ನು <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ಅನ್ನು ನಿಮ್ಮ ಕ್ಲಿಪ್ಬೋರ್ಡ್ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string> <string name="pasted_text" msgid="4298871641549173733">"ನೀವು ನಕಲಿಸಿರುವ ಪಠ್ಯವನ್ನು <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string> <string name="pasted_image" msgid="4729097394781491022">"ನೀವು ನಕಲಿಸಿರುವ ಚಿತ್ರವನ್ನು <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string> <string name="pasted_content" msgid="646276353060777131">"ನೀವು ನಕಲಿಸಿರುವ ವಿಷಯವನ್ನು <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> ಸಿದ್ಧಪಡಿಸಲಾಗುತ್ತಿದೆ."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"ಬೂಟ್ ಪೂರ್ಣಗೊಳಿಸಲಾಗುತ್ತಿದೆ."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ಸ್ಕ್ರೀನ್ ಆಫ್ ಮಾಡಬೇಕೇ?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಅನ್ನು ಸೆಟ್ಟಪ್ ಮಾಡುವಾಗ ನೀವು ಪವರ್ ಬಟನ್ಅನ್ನು ಒತ್ತಿದ್ದೀರಿ \n\nಸಾಮಾನ್ಯವಾಗಿ ಇದರಿಂದ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಆಫ್ ಆಗುತ್ತದೆ."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ಆಫ್ ಮಾಡಿ"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ರದ್ದುಗೊಳಿಸಿ"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ರನ್ ಆಗುತ್ತಿದೆ"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ಆಟಕ್ಕೆ ಹಿಂತಿರುಗಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ಆಟವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ಶಾರ್ಟ್ಕಟ್ ಬಳಸಿ"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"ಬಣ್ಣ ವಿಲೋಮ"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ಒಂದು ಕೈ ಮೋಡ್"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ಇನ್ನಷ್ಟು ಮಬ್ಬು"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಲಾಗಿದೆ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index c74eae72d925..68468a2cd9a5 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"텍스트가 클립보드에 복사되었습니다."</string> <string name="copied" msgid="4675902854553014676">"복사 완료"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> 앱이 <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> 앱에서 복사하여 붙여넣음"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>에서 클립보드 데이터를 붙여넣음"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>에서 복사한 텍스트를 붙여넣음"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>에서 복사한 이미지를 붙여넣음"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>에서 복사한 콘텐츠를 붙여넣음"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> 준비 중..."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"앱을 시작하는 중입니다."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"부팅 완료"</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"화면을 끄시겠습니까?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"지문을 설정하는 중에 전원 버튼이 눌렸습니다.\n\n이렇게 하면 보통 화면이 꺼집니다."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"끄기"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"취소"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> 실행 중"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"게임으로 돌아가려면 탭하세요."</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"게임 선택"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"단축키 사용"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"색상 반전"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"색상 보정"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"한 손 사용 모드"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"더 어둡게"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 설정되었습니다."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 중지되었습니다."</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"관리자에 의해 업데이트되었습니다."</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"관리자에 의해 삭제되었습니다."</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"확인"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"절전 기능은 어두운 테마를 사용 설정하고 백그라운드 활동, 일부 시각 효과, 특정 기능 및 일부 네트워크 연결을 제한하거나 사용 중지합니다.\n\n"<annotation id="url">"자세히 알아보기"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"절전 기능은 어두운 테마를 사용 설정하고 백그라운드 활동, 일부 시각 효과, 특정 기능 및 일부 네트워크 연결을 제한하거나 사용 중지합니다."</string> <string name="data_saver_description" msgid="4995164271550590517">"데이터 사용량을 줄이기 위해 데이터 절약 모드는 일부 앱이 백그라운드에서 데이터를 전송하거나 수신하지 못하도록 합니다. 현재 사용 중인 앱에서 데이터에 액세스할 수 있지만 빈도가 줄어듭니다. 예를 들면, 이미지를 탭하기 전에는 이미지가 표시되지 않습니다."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"데이터 절약 모드를 사용 설정하시겠습니까?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"사용 설정"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>은(는) 현재 사용할 수 없습니다. <xliff:g id="APP_NAME_1">%2$s</xliff:g>에서 관리하는 앱입니다."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"자세히 알아보기"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"앱 일시중지 해제"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"직장 앱을 사용 설정하시겠습니까?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"직장 앱 및 알림에 액세스하세요."</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"사용 설정"</string> <string name="app_blocked_title" msgid="7353262160455028160">"앱을 사용할 수 없습니다"</string> <string name="app_blocked_message" msgid="542972921087873023">"현재 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱을 사용할 수 없습니다."</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index a60bdd582f17..0426a8143825 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Текст алмашуу буферине көчүрүлдү."</string> <string name="copied" msgid="4675902854553014676">"Көчүрүлдү"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> колдонмосунан чапталды"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> алмашуу буферинен чапталды"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: көчүрүлгөн текст чапталды"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: көчүрүлгөн сүрөт чапталды"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: көчүрүлгөн мазмун чапталды"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> даярдалууда."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Колдонмолорду иштетип баштоо"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Жүктөлүүдө"</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Экран өчүрүлсүнбү?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Манжаңыздын изин жөндөп жатканда күйгүзүү/өчүрүү баскычын басып алдыңыз.\n\nБул адатта экранды өчүрөт."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Өчүрүү"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Жокко чыгаруу"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> иштеп жатат"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Оюнга кайтуу үчүн таптаңыз"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Оюн тандоо"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Кыска жолду колдонуу"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Түстү инверсиялоо"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Түсүн тууралоо"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Бир колдуу режим"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Кошумча караңгылатуу"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> күйгүзүлдү."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өчүрүлдү."</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Администраторуңуз жаңыртып койгон"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Администраторуңуз жок кылып салган"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ЖАРАЙТ"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Батареяны үнөмдөгүч режиминде Караңгы тема күйгүзүлүп, фондогу аракеттер, айрым визуалдык эффекттер, белгилүү бир функциялар жана айрым тармакка туташуулар чектелип же өчүрүлөт.\n\n"<annotation id="url">"Кеңири маалымат"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Батареяны үнөмдөгүч режиминде Караңгы тема күйгүзүлүп, фондогу аракеттер, айрым визуалдык эффекттер, белгилүү бир функциялар жана айрым тармакка туташуулар чектелип же өчүрүлөт."</string> <string name="data_saver_description" msgid="4995164271550590517">"Трафикти үнөмдөө режиминде айрым колдонмолор маалыматтарды фондо өткөрө алышпайт. Учурда сиз пайдаланып жаткан колдонмо маалыматтарды жөнөтүп/ала алат, бирок адаттагыдан азыраак өткөргөндүктөн, анын айрым функциялары талаптагыдай иштебей коюшу мүмкүн. Мисалы, сүрөттөр басылмайынча жүктөлбөйт."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Трафикти үнөмдөө режимин иштетесизби?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Күйгүзүү"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> колдонмосу учурда жеткиликсиз. Анын жеткиликтүүлүгү <xliff:g id="APP_NAME_1">%2$s</xliff:g> тарабынан башкарылат."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Кеңири маалымат"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Колдонмону иштетүү"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Жумуш колдонмолору күйгүзүлсүнбү?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Жумуш колдонмолоруңузга жана билдирмелериңизге мүмкүнчүлүк алыңыз"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Күйгүзүү"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Колдонмо учурда жеткиликсиз"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> учурда жеткиликсиз"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 667057f3fb80..327faf978eb7 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -1869,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"ຖືກອັບໂຫລດໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"ຖືກລຶບອອກໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ຕົກລົງ"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"ຕົວປະຢັດແບັດເຕີຣີຈະເປີດໃຊ້ຮູບແບບສີສັນມືດ ແລະ ຈຳກັດ ຫຼື ປິດການເຄື່ອນໄຫວໃນພື້ນຫຼັງ, ເອັບເຟັກທາງພາບຈຳນວນໜຶ່ງ, ຄຸນສົມບັດບາງຢ່າງ ແລະ ການເຊື່ອມຕໍ່ເຄືອຂ່າຍບາງອັນ.\n\n"<annotation id="url">"ສຶກສາເພີ່ມເຕີມ"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"ຕົວປະຢັດແບັດເຕີຣີຈະເປີດໃຊ້ຮູບແບບສີສັນມືດ ແລະ ຈຳກັດ ຫຼື ປິດການເຄື່ອນໄຫວໃນພື້ນຫຼັງ, ເອັບເຟັກທາງພາບຈຳນວນໜຶ່ງ, ຄຸນສົມບັດບາງຢ່າງ ແລະ ການເຊື່ອມຕໍ່ເຄືອຂ່າຍບາງອັນ."</string> <string name="data_saver_description" msgid="4995164271550590517">"ເພື່ອຊ່ວຍຫຼຸດຜ່ອນການນຳໃຊ້ຂໍ້ມູນ, ຕົວປະຢັດອິນເຕີເນັດຈະປ້ອງກັນບໍ່ໃຫ້ບາງແອັບສົ່ງ ຫຼື ຮັບຂໍ້ມູນໃນພື້ນຫຼັງ. ແອັບໃດໜຶ່ງທີ່ທ່ານກຳລັງໃຊ້ຢູ່ຈະສາມາດເຂົ້າເຖິງຂໍ້ມູນໄດ້ ແຕ່ອາດເຂົ້າເຖິງໄດ້ຖີ່ໜ້ອຍລົງ. ນີ້ອາດໝາຍຄວາມວ່າ ຮູບພາບຕ່າງໆອາດບໍ່ສະແດງຈົນກວ່າທ່ານຈະແຕະໃສ່ກ່ອນ."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ເປີດຕົວປະຢັດອິນເຕີເນັດບໍ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ເປີດໃຊ້"</string> @@ -1975,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ບໍ່ສາມາດໃຊ້ໄດ້ໃນຕອນນີ້. ມັນຖືກຈັດການໂດຍ <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"ສຶກສາເພີ່ມເຕີມ"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ຍົກເລີກການຢຸດແອັບຊົ່ວຄາວ"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"ເປີດໃຊ້ແອັບບ່ອນເຮັດວຽກບໍ?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"ຮັບສິດເຂົ້າເຖິງແອັບບ່ອນເຮັດວຽກ ແລະ ການແຈ້ງເຕືອນຂອງທ່ານ"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"ເປີດ"</string> <string name="app_blocked_title" msgid="7353262160455028160">"ແອັບບໍ່ສາມາດໃຊ້ໄດ້"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ່ສາມາດໃຊ້ໄດ້ໃນຕອນນີ້."</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 75facc1c1e26..e7b019f1ca50 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -1757,8 +1757,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Naudoti spartųjį klavišą"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Spalvų inversija"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Spalvų taisymas"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Vienos rankos režimas"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Itin blanku"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ įjungta."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ išjungta."</string> @@ -1916,10 +1915,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Atnaujino administratorius"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Ištrynė administratorius"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Gerai"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Akumuliatoriaus tausojimo priemonė įjungia tamsiąją temą ir apriboja arba išjungia veiklą fone, kai kuriuos vaizdinius efektus, tam tikras funkcijas bei kai kuriuos tinklo ryšius.\n\n"<annotation id="url">"Sužinokite daugiau"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Akumuliatoriaus tausojimo priemonė įjungia tamsiąją temą ir apriboja arba išjungia veiklą fone, kai kuriuos vaizdinius efektus, tam tikras funkcijas bei kai kuriuos tinklo ryšius."</string> <string name="data_saver_description" msgid="4995164271550590517">"Kad padėtų sumažinti duomenų naudojimą, Duomenų taupymo priemonė neleidžia kai kurioms programoms siųsti ar gauti duomenų fone. Šiuo metu naudojama programa gali pasiekti duomenis, bet tai bus daroma rečiau. Tai gali reikšti, kad, pvz., vaizdai nebus pateikiami, jei jų nepaliesite."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Įj. Duomenų taupymo priemonę?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Įjungti"</string> @@ -2040,10 +2037,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Programa „<xliff:g id="APP_NAME_0">%1$s</xliff:g>“ šiuo metu nepasiekiama. Tai tvarkoma naudojant programą „<xliff:g id="APP_NAME_1">%2$s</xliff:g>“."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Sužinoti daugiau"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Atšaukti programos pristabdymą"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Įjungti darbo programas?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Pasiekite darbo programas ir pranešimus"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Įjungti"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Programa nepasiekiama."</string> <string name="app_blocked_message" msgid="542972921087873023">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ šiuo metu nepasiekiama."</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index dbbf427400c0..429a8b08e5e1 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -1025,8 +1025,7 @@ <string name="text_copied" msgid="2531420577879738860">"Teksts ir kopēts uz starpliktuvi."</string> <string name="copied" msgid="4675902854553014676">"Nokopēts"</string> <string name="pasted_from_app" msgid="5627698450808256545">"Lietotnē <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> tika ielīmēti dati no lietotnes <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>."</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"Lietotne <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ielīmēja datus no starpliktuves."</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ielīmēja jūsu nokopēto tekstu"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ielīmēja jūsu nokopēto attēlu"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ielīmēja jūsu nokopēto saturu"</string> @@ -1280,14 +1279,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Notiek lietotnes <xliff:g id="APPNAME">%1$s</xliff:g> sagatavošana."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Notiek lietotņu palaišana."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Tiek pabeigta sāknēšana."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vai izslēgt ekrānu?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Iestatot pirksta nospiedumu, jūs nospiedāt barošanas pogu.\n\nTādējādi parasti tiek izslēgts ekrāns."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Izslēgt"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Atcelt"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> darbojas"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Pieskarieties, lai atgrieztos spēlē"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Spēles izvēlēšanās"</string> @@ -1740,8 +1735,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Izmantot saīsni"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Krāsu inversija"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Krāsu korekcija"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Vienas rokas režīms"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Papildu aptumšošana"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika ieslēgts."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika izslēgts."</string> @@ -1898,10 +1892,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Atjaunināja administrators"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Dzēsa administrators"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Labi"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Akumulatora enerģijas taupīšanas režīmā tiek ieslēgts tumšais motīvs un tiek ierobežotas vai izslēgtas darbības fonā, daži vizuālie efekti, noteiktas funkcijas un noteikti tīkla savienojumi.\n\n"<annotation id="url">"Uzzināt vairāk"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Akumulatora enerģijas taupīšanas režīmā tiek ieslēgts tumšais motīvs un tiek ierobežotas vai izslēgtas darbības fonā, daži vizuālie efekti, noteiktas funkcijas un noteikti tīkla savienojumi."</string> <string name="data_saver_description" msgid="4995164271550590517">"Lai samazinātu datu lietojumu, datu lietojuma samazinātājs neļauj dažām lietotnēm fonā nosūtīt vai saņemt datus. Lietotne, kuru pašlaik izmantojat, var piekļūt datiem, bet, iespējams, piekļūs tiem retāk (piemēram, attēli tiks parādīti tikai tad, kad tiem pieskarsieties)."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Vai ieslēgt datu lietojuma samazinātāju?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Ieslēgt"</string> @@ -2013,10 +2005,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> pašlaik nav pieejama. Šo darbību pārvalda <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Uzzināt vairāk"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Atsākt lietotnes darbību"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Vai ieslēgt darba lietotnes?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Iegūstiet piekļuvi darba lietotnēm un paziņojumiem"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Ieslēgt"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Lietotne nav pieejama"</string> <string name="app_blocked_message" msgid="542972921087873023">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pašlaik nav pieejama."</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 876f5225dad6..dfa2aab3cb56 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи кратенка"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија на бои"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Корекција на бои"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим со една рака"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дополнително затемнување"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е вклучена."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е исклучена."</string> @@ -1870,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирано од администраторот"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Избришано од администраторот"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Во ред"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"„Штедачот на батерија“ вклучува темна тема и ја ограничува или исклучува активноста во заднина, некои визуелни ефекти, одредени функции и некои мрежни врски.\n\n"<annotation id="url">"Дознајте повеќе"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"„Штедачот на батерија“ вклучува темна тема и ја ограничува или исклучува активноста во заднина, некои визуелнни ефекти, одредени функции и некои мрежни врски."</string> <string name="data_saver_description" msgid="4995164271550590517">"За да се намали користењето интернет, „Штедачот на интернет“ спречува дел од апликациите да испраќаат или да примаат податоци во заднина. Одредена апликација што ја користите ќе може да користи интернет, но можеби тоа ќе го прави поретко. Ова значи, на пример, дека сликите нема да се прикажуваат додека не ги допрете."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Да се вклучи „Штедач на интернет“?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Вклучи"</string> @@ -1976,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Апликацијата <xliff:g id="APP_NAME_0">%1$s</xliff:g> не е достапна во моментов. Со ова управува <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Дознај повеќе"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Прекини ја паузата"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Да се вклучат работни апликации?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Добијте пристап до вашите работни апликации и известувања"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Вклучи"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Апликацијата не е достапна"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> не е достапна во моментов."</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 5448be5a495e..b66923cbddaf 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"ടെക്സ്റ്റ് ക്ലിപ്ബോർഡിലേക്ക് പകർത്തി."</string> <string name="copied" msgid="4675902854553014676">"പകർത്തി"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> എന്നതിൽ നിന്ന് <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ഒട്ടിച്ചു"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> നിങ്ങളുടെ ക്ലിപ്പ്ബോർഡിൽ നിന്ന് ഒട്ടിച്ചു"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> നിങ്ങൾ പകർത്തിയ ടെക്സ്റ്റ് ഒട്ടിച്ചു"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> നിങ്ങൾ പകർത്തിയ ഒരു ചിത്രം ഒട്ടിച്ചു"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> നിങ്ങൾ പകർത്തിയ ഉള്ളടക്കം ഒട്ടിച്ചു"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> തയ്യാറാക്കുന്നു."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"അപ്ലിക്കേഷനുകൾ ആരംഭിക്കുന്നു."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"ബൂട്ട് ചെയ്യൽ പൂർത്തിയാകുന്നു."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"സ്ക്രീൻ ഓഫാക്കണോ?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ഫിംഗർപ്രിന്റ് സജ്ജീകരിച്ച് കൊണ്ടിരുന്നപ്പോൾ നിങ്ങൾ പവർ ബട്ടൺ അമർത്തി.\n\nഇത് സാധാരണയായി സ്ക്രീൻ ഓഫാകുന്നതിന് കാരണമാകും."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ഓഫാക്കുക"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"റദ്ദാക്കുക"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> പ്രവർത്തിക്കുന്നു"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ഗെയിമിലേക്ക് മടങ്ങാൻ ടാപ്പ് ചെയ്യുക"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ഗെയിം തിരഞ്ഞെടുക്കുക"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"കുറുക്കുവഴി ഉപയോഗിക്കുക"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"വർണ്ണ വിപര്യയം"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"നിറം ക്രമീകരിക്കൽ"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ഒറ്റക്കൈ മോഡ്"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"കൂടുതൽ ഡിം ചെയ്യൽ"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"വോളിയം കീകൾ പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓണാക്കി."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"വോളിയം കീകൾ അമർത്തിപ്പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓഫാക്കി."</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"നിങ്ങളുടെ അഡ്മിൻ അപ്ഡേറ്റ് ചെയ്യുന്നത്"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"നിങ്ങളുടെ അഡ്മിൻ ഇല്ലാതാക്കുന്നത്"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ശരി"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"ബാറ്ററി ലാഭിക്കൽ ഡാർക്ക് തീം ഓണാക്കുന്നു, പശ്ചാത്തല ആക്റ്റിവിറ്റിയും ചില വിഷ്വൽ ഇഫക്റ്റുകളും ചില ഫീച്ചറുകളും ചില നെറ്റ്വർക്ക് കണക്ഷനുകളും അത് പരിമിതപ്പെടുത്തുകയോ ഓഫാക്കുകയോ ചെയ്യുന്നു.\n\n"<annotation id="url">"കൂടുതലറിയുക"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"ബാറ്ററി ലാഭിക്കൽ ഡാർക്ക് തീം ഓണാക്കുന്നു, പശ്ചാത്തല ആക്റ്റിവിറ്റിയും ചില വിഷ്വൽ ഇഫക്റ്റുകളും ചില ഫീച്ചറുകളും ചില നെറ്റ്വർക്ക് കണക്ഷനുകളും അത് പരിമിതപ്പെടുത്തുകയോ ഓഫാക്കുകയോ ചെയ്യുന്നു."</string> <string name="data_saver_description" msgid="4995164271550590517">"ഡാറ്റാ ഉപയോഗം കുറയ്ക്കാൻ സഹായിക്കുന്നതിനായി പശ്ചാത്തലത്തിൽ ഡാറ്റ അയയ്ക്കുകയോ സ്വീകരിക്കുകയോ ചെയ്യുന്നതിൽ നിന്ന് ചില ആപ്പുകളെ ഡാറ്റാ സേവർ തടയുന്നു. നിങ്ങൾ നിലവിൽ ഉപയോഗിക്കുന്ന ഒരു ആപ്പിന് ഡാറ്റ ആക്സസ് ചെയ്യാനാകും, എന്നാൽ വല്ലപ്പോഴും മാത്രമെ സംഭവിക്കുന്നുള്ളു. ഇതിനർത്ഥം, ഉദാഹരണമായി നിങ്ങൾ ടാപ്പ് ചെയ്യുന്നത് വരെ ചിത്രങ്ങൾ പ്രദർശിപ്പിക്കുകയില്ല എന്നാണ്."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ഡാറ്റ സേവർ ഓണാക്കണോ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ഓണാക്കുക"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ഇപ്പോൾ ലഭ്യമല്ല. <xliff:g id="APP_NAME_1">%2$s</xliff:g> ആണ് ഇത് മാനേജ് ചെയ്യുന്നത്."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"കൂടുതലറിയുക"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ആപ്പ് പുനഃരാംഭിക്കുക"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"ഔദ്യോഗിക ആപ്പുകൾ ഓണാക്കണോ?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"നിങ്ങളുടെ ഔദ്യോഗിക ആപ്പുകളിലേക്കും അറിയിപ്പുകളിലേക്കും ആക്സസ് നേടുക"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"ഓണാക്കുക"</string> <string name="app_blocked_title" msgid="7353262160455028160">"ആപ്പ് ലഭ്യമല്ല"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ഇപ്പോൾ ലഭ്യമല്ല."</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index ab083841dcc9..838c49a43d8b 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Товчлол ашиглах"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Өнгө хувиргалт"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Өнгөний засвар"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Нэг гарын горим"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Хэт бүүдгэр"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г асаалаа."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г унтраалаа."</string> @@ -1870,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Таны админ шинэчилсэн"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Таны админ устгасан"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Батарей хэмнэгч нь Бараан загварыг асааж, дэвсгэрийн үйл ажиллагаа, зарим визуал эффект, тодорхой онцлогууд болон зарим сүлжээний холболтыг хязгаарлах эсвэл унтраана.\n\n"<annotation id="url">"Нэмэлт мэдээлэл авах"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Батарей хэмнэгч нь Бараан загварыг асааж, дэвсгэрийн үйл ажиллагаа, зарим визуал эффект, тодорхой онцлогууд болон зарим сүлжээний холболтыг хязгаарлах эсвэл унтраана."</string> <string name="data_saver_description" msgid="4995164271550590517">"Дата ашиглалтыг багасгахын тулд дата хэмнэгч нь ар талд ажиллаж буй зарим апп-н өгөгдлийг илгээх болон авахаас сэргийлдэг. Таны одоогийн ашиглаж буй апп нь өгөгдөлд хандах боломжтой хэдий ч тогтмол хандахгүй. Энэ нь жишээлбэл зургийг товших хүртэл харагдахгүй гэсэн үг юм."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Дата хэмнэгчийг асаах уу?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Асаах"</string> @@ -1976,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> одоогоор боломжгүй байна. Үүнийг <xliff:g id="APP_NAME_1">%2$s</xliff:g>-р удирддаг."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Дэлгэрэнгүй үзэх"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Аппыг түр зогсоохоо болих"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Ажлын аппуудыг асаах уу?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Ажлын аппууд болон мэдэгдлүүддээ хандах эрх аваарай"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Асаах"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Апп боломжгүй байна"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> яг одоо боломжгүй байна."</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 2e8d6f523c8e..ad954c8c992e 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"मजकूर क्लिपबोर्डवर कॉपी केला."</string> <string name="copied" msgid="4675902854553014676">"कॉपी केले"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> वरून <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> पेस्ट केले"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ने तुमच्या क्लिपबोर्डवरून पेस्ट केले"</string> <string name="pasted_text" msgid="4298871641549173733">"तुम्ही कॉपी केलेला मजकूर <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ने पेस्ट केला"</string> <string name="pasted_image" msgid="4729097394781491022">"तुम्ही कॉपी केलेली इमेज <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ने पेस्ट केली"</string> <string name="pasted_content" msgid="646276353060777131">"तुम्ही कॉपी केलेला आशय <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ने पेस्ट केला"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> तयार करत आहे."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"अॅप्स सुरू करत आहे."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"बूट समाप्त होत आहे."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"स्क्रीन बंद करायची आहे का?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"तुम्ही तुमची फिंगरप्रिंट सेट करत असताना पॉवर बटण दाबले.\n\nयामुळे सहसा तुमची स्क्रीन बंद होते."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"बंद करा"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"रद्द करा"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"रन होणारे <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"गेमवर परत जाण्यासाठी टॅप करा"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"गेम निवडा"</string> @@ -1612,7 +1607,7 @@ <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"सर्व पहा"</string> <string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"ॲक्टिव्हिटी निवडा"</string> <string name="share_action_provider_share_with" msgid="1904096863622941880">"यांच्यासह शेअर करा"</string> - <string name="sending" msgid="206925243621664438">"पाठवित आहे..."</string> + <string name="sending" msgid="206925243621664438">"पाठवत आहे..."</string> <string name="launchBrowserDefault" msgid="6328349989932924119">"ब्राउझर लाँच करायचा?"</string> <string name="SetupCallDefault" msgid="5581740063237175247">"कॉल स्वीकारायचा?"</string> <string name="activity_resolver_use_always" msgid="5575222334666843269">"नेहमी"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"शॉर्टकट वापरा"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"रंगांची उलटापालट"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"रंग सुधारणा"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"एकहाती मोड"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"आणखी डिम"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> सुरू केला आहे."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केले आहे."</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index d1545824776a..27647adca57a 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Teks disalin ke papan keratan"</string> <string name="copied" msgid="4675902854553014676">"Disalin"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ditampalkan daripada <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ditampal daripada papan keratan anda"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> telah menampal teks yang anda salin"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> telah menampal imej yang anda salin"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> telah menampal kandungan yang anda salin"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Menyediakan <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Memulakan apl."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"But akhir."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Matikan skrin?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Semasa menyediakan cap jari anda, anda menekan butang Kuasa.\n\nTindakan ini biasanya mematikan skrin anda."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Matikan"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Batal"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> dijalankan"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Ketik untuk kembali ke permainan"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pilih permainan"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 2054e47ed438..f7754551de8f 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -219,7 +219,7 @@ <string name="silent_mode" msgid="8796112363642579333">"အသံတိတ်စနစ်"</string> <string name="turn_on_radio" msgid="2961717788170634233">"wirelessအားဖွင့်မည်"</string> <string name="turn_off_radio" msgid="7222573978109933360">"wirelessအားပိတ်မည်"</string> - <string name="screen_lock" msgid="2072642720826409809">"ဖန်သားပြင် လော့ခ်ချခြင်း"</string> + <string name="screen_lock" msgid="2072642720826409809">"ဖန်သားပြင် လော့ခ်ချရန်"</string> <string name="power_off" msgid="4111692782492232778">"စက်ပိတ်ပါ"</string> <string name="silent_mode_silent" msgid="5079789070221150912">"ဖုန်းမြည်သံပိတ်ထားသည်"</string> <string name="silent_mode_vibrate" msgid="8821830448369552678">"တုန်ခါခြင်း ဖုန်းမြည်သံ"</string> @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"clipboardထံ စာသားအားကူးယူမည်"</string> <string name="copied" msgid="4675902854553014676">"မိတ္တူကူးပြီးပါပြီ"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> မှ <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> သို့ ကူးထည့်ထားသည်"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> က ဒေတာကို သင့်ကလစ်ဘုတ်မှ ကူးထည့်ထားသည်"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> က သင်မိတ္တူကူးထားသော စာသားကို ထည့်လိုက်သည်"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> က သင်မိတ္တူကူးထားသော ပုံကို ထည့်လိုက်သည်"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> က သင်မိတ္တူကူးထားသော အကြောင်းအရာကို ထည့်လိုက်သည်"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> အားပြင်ဆင်နေသည်။"</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"အက်ပ်များကို စတင်နေ"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"လုပ်ငန်းစနစ်ထည့်သွင်း၍ ပြန်လည်စတင်ရန် ပြီးပါပြီ"</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ဖန်သားပြင်ကို ပိတ်မလား။"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"သင့်လက်ဗွေကို စနစ်ထည့်သွင်းနေစဉ် ဖွင့်ပိတ်ခလုတ်ကို ဖိထားပါ။\n\n၎င်းက အများအားဖြင့် သင့်ဖန်သားပြင်ကို ပိတ်ပါသည်။"</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ပိတ်ရန်"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"မလုပ်တော့"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> က အလုပ်လုပ်နေသည်"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ဂိမ်းသို့ ပြန်သွားရန် တို့ပါ"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ဂိမ်းကို ရွေးခြင်း"</string> @@ -1874,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"သင်၏ စီမံခန့်ခွဲသူက အပ်ဒိတ်လုပ်ထားသည်"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"သင်၏ စီမံခန့်ခွဲသူက ဖျက်လိုက်ပါပြီ"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"‘ဘက်ထရီ အားထိန်း’ က ‘မှောင်သည့် အပြင်အဆင်’ ကို ဖွင့်ပြီး နောက်ခံလုပ်ဆောင်ချက်၊ ပြသမှုဆိုင်ရာ အထူးပြုလုပ်ချက်အချို့၊ ဝန်ဆောင်မှုအချို့နှင့် ကွန်ရက်ချိတ်ဆက်မှုအချို့တို့ကို ကန့်သတ်သည် သို့မဟုတ် ပိတ်သည်။\n\n"<annotation id="url">"ပိုမိုလေ့လာရန်"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"‘ဘက်ထရီ အားထိန်း’ က ‘မှောင်သည့် အပြင်အဆင်’ ကို ဖွင့်ပြီး နောက်ခံလုပ်ဆောင်ချက်၊ ပြသမှုဆိုင်ရာ အထူးပြုလုပ်ချက်အချို့၊ ဝန်ဆောင်မှုအချို့နှင့် ကွန်ရက်ချိတ်ဆက်မှုအချို့တို့ကို ကန့်သတ်သည် သို့မဟုတ် ပိတ်သည်။"</string> <string name="data_saver_description" msgid="4995164271550590517">"ဒေတာအသုံးလျှော့ချနိုင်ရန်အတွက် အက်ပ်များကို နောက်ခံတွင် ဒေတာပို့ခြင်းနှင့် လက်ခံခြင်းမပြုရန် \'ဒေတာချွေတာမှု\' စနစ်က တားဆီးထားပါသည်။ ယခုအက်ပ်ဖြင့် ဒေတာအသုံးပြုနိုင်သော်လည်း အကြိမ်လျှော့၍သုံးရပါမည်။ ဥပမာ၊ သင်က မတို့မချင်း ပုံများပေါ်လာမည် မဟုတ်ပါ။"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ဒေတာချွေတာမှုစနစ် ဖွင့်မလား။"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ဖွင့်ပါ"</string> @@ -1980,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ကို လောလောဆယ် မရနိုင်ပါ။ ၎င်းကို <xliff:g id="APP_NAME_1">%2$s</xliff:g> က စီမံထားပါသည်။"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"ပိုမိုလေ့လာရန်"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"အက်ပ်ကို ခဏမရပ်တော့ရန်"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"အလုပ်သုံးအက်ပ်များ ဖွင့်မလား။"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"သင့်အလုပ်သုံးအက်ပ်နှင့် အကြောင်းကြားချက်များသုံးခွင့် ရယူပါ"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"ဖွင့်ပါ"</string> <string name="app_blocked_title" msgid="7353262160455028160">"အက်ပ်ကို မရနိုင်ပါ"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ယခု မရနိုင်ပါ။"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index a6f945c9bf99..4e55f78b436b 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -1869,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Oppdatert av administratoren din"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Slettet av administratoren din"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Batterisparing slår på mørkt tema og begrenser eller slår av bakgrunnsaktivitet, enkelte visuelle effekter, noen funksjoner og noen nettverkstilkoblinger.\n\n"<annotation id="url">"Finn ut mer"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Batterisparing slår på mørkt tema og begrenser eller slår av bakgrunnsaktivitet, enkelte visuelle effekter, noen funksjoner og noen nettverkstilkoblinger."</string> <string name="data_saver_description" msgid="4995164271550590517">"Datasparing hindrer noen apper fra å sende og motta data i bakgrunnen, for å redusere dataforbruket. Aktive apper kan bruke data, men kanskje ikke så mye som ellers – for eksempel vises ikke bilder før du trykker på dem."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Vil du slå på Datasparing?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Slå på"</string> @@ -1975,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> er ikke tilgjengelig akkurat nå. Dette administreres av <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Finn ut mer"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Opphev pause for appen"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Vil du slå på jobbapper?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Få tilgang til jobbapper og -varsler"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Slå på"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Appen er ikke tilgjengelig"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ikke tilgjengelig for øyeblikket."</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index d986f2ce1c56..53a8273179d4 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -1718,8 +1718,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"सर्टकट प्रयोग गर्नुहोस्"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"रङ्ग उल्टाउने सुविधा"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"रङ्ग सच्याउने सुविधा"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"एक हाते मोड"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"अझै मधुरो"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन भयो।"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अफ भयो।"</string> @@ -1875,10 +1874,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"तपाईंका प्रशासकले अद्यावधिक गर्नुभएको"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"तपाईंका प्रशासकले मेट्नुभएको"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ठिक छ"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"ब्याट्री सेभरले अँध्यारो थिम अन गर्छ र ब्याकग्राउन्डमा हुने क्रियाकलाप, केही भिजुअल इफेक्ट, निश्चित सुविधा र केही नेटवर्क कनेक्सनहरू अफ गर्छ वा सीमित रूपमा मात्र चल्न दिन्छ।\n\n"<annotation id="url">"थप जान्नुहोस्"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"ब्याट्री सेभरले अँध्यारो थिम अन गर्छ र ब्याकग्राउन्डमा हुने क्रियाकलाप, केही भिजुअल इफेक्ट, निश्चित सुविधा र केही नेटवर्क कनेक्सनहरू अफ गर्छ वा सीमित रूपमा मात्र चल्न दिन्छ।"</string> <string name="data_saver_description" msgid="4995164271550590517">"डेटा सेभरले डेटा खपत कम गर्न केही एपहरूलाई ब्याकग्राउन्डमा डेटा पठाउन वा प्राप्त गर्न दिँदैन। तपाईंले अहिले प्रयोग गरिरहनुभएको एपले सीमित रूपमा मात्र डेटा चलाउन पाउँछ। उदाहरणका लागि, तपाईंले फोटोमा ट्याप गर्नुभयो भने मात्र फोटो देखिन्छ नत्र देखिँदैन।"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा सेभर अन गर्ने हो?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"सक्रिय गर्नुहोस्"</string> @@ -1981,10 +1978,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> अहिले उपलब्ध छैन। यो <xliff:g id="APP_NAME_1">%2$s</xliff:g> द्वारा व्यवस्थित छ।"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"थप जान्नुहोस्"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"एपको पज हटाउनुहोस्"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"कामसम्बन्धी एपहरू सक्षम पार्ने हो?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"कामसम्बन्धी एप चलाउने र सूचना प्राप्त गर्ने सुविधा अन गर्नुहोस्"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"सक्रिय गर्नुहोस्"</string> <string name="app_blocked_title" msgid="7353262160455028160">"एप उपलब्ध छैन"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> अहिले उपलब्ध छैन।"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 15a053157dc4..e69fe12b386b 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ଶର୍ଟକଟ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"ରଙ୍ଗ ବଦଳାଇବାର ସୁବିଧା"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"ରଙ୍ଗ ସଂଶୋଧନ"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ଏକ-ହାତ ମୋଡ୍"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ଅତିରିକ୍ତ ଡିମ୍"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଚାଲୁ ହୋଇଛି।"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବନ୍ଦ ହୋଇଛି।"</string> @@ -1870,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"ଆପଣଙ୍କ ଆଡମିନ୍ ଅପଡେଟ୍ କରିଛନ୍ତି"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"ଆପଣଙ୍କ ଆଡମିନ୍ ଡିଲିଟ୍ କରିଛନ୍ତି"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ଠିକ୍ ଅଛି"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"ବ୍ୟାଟେରୀ ସେଭର୍ ଗାଢ଼ା ଥିମକୁ ଚାଲୁ କରେ ଏବଂ ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ଇଫେକ୍ଟ, କିଛି ଫିଚର୍ ଏବଂ କିଛି ନେଟୱାର୍କ ସଂଯୋଗକୁ ସୀମିତ କିମ୍ବା ବନ୍ଦ କରେ।\n\n"<annotation id="url">"ଅଧିକ ଜାଣନ୍ତୁ"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"ବ୍ୟାଟେରୀ ସେଭର୍ ଗାଢ଼ା ଥିମକୁ ଚାଲୁ କରେ ଏବଂ ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ଇଫେକ୍ଟ, କିଛି ଫିଚର୍ ଏବଂ କିଛି ନେଟୱାର୍କ ସଂଯୋଗକୁ ସୀମିତ କିମ୍ବା ବନ୍ଦ କରେ।"</string> <string name="data_saver_description" msgid="4995164271550590517">"ଡାଟା ବ୍ୟବହାର କମ୍ କରିବାରେ ସାହାଯ୍ୟ କରିବାକୁ, ଡାଟା ସେଭର୍ ବ୍ୟାକ୍ଗ୍ରାଉଣ୍ଡରେ ଡାଟା ପଠାଇବା କିମ୍ବା ପ୍ରାପ୍ତ କରିବାକୁ କିଛି ଆପ୍କୁ ବାରଣ କରେ। ଆପଣ ବର୍ତ୍ତମାନ ବ୍ୟବହାର କରୁଥିବା ଆପ୍, ଡାଟା ଆକ୍ସେସ୍ କରିପାରେ, କିନ୍ତୁ ଏହା କମ୍ ଥର କରିପାରେ। ଏହାର ଅର୍ଥ ହୋଇପାରେ ଯେମିତି ଆପଣ ଇମେଜଗୁଡ଼ିକୁ ଟାପ୍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ସେଗୁଡ଼ିକ ଡିସପ୍ଲେ ହୁଏ ନାହିଁ।"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ଡାଟା ସେଭର୍ ଚାଲୁ କରିବେ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ଚାଲୁ କରନ୍ତୁ"</string> @@ -1976,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"ବର୍ତ୍ତମାନ <xliff:g id="APP_NAME_0">%1$s</xliff:g> ଉପଲବ୍ଧ ନାହିଁ। ଏହା <xliff:g id="APP_NAME_1">%2$s</xliff:g> ଦ୍ଵାରା ପରିଚାଳିତ ହେଉଛି।"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"ଅଧିକ ଜାଣନ୍ତୁ"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ଆପ୍ ଅନପଜ୍ କରନ୍ତୁ"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"ୱାର୍କ ଆପଗୁଡ଼ିକୁ ଚାଲୁ କରିବେ?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"ଆପଣଙ୍କ ୱାର୍କ ଆପ୍ ଏବଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଆକ୍ସେସ୍ ପାଆନ୍ତୁ"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"ଚାଲୁ କରନ୍ତୁ"</string> <string name="app_blocked_title" msgid="7353262160455028160">"ଆପ୍ ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବର୍ତ୍ତମାନ ଉପଲବ୍ଧ ନାହିଁ।"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 50e1f98e6afd..f7568e568f30 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"ਟੈਕਸਟ ਕਲਿਪਬੋਰਡ ਤੇ ਕਾਪੀ ਕੀਤਾ।"</string> <string name="copied" msgid="4675902854553014676">"ਕਾਪੀ ਕੀਤੀ ਗਈ"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> ਤੋਂ ਕਾਪੀ ਕੀਤੇ ਡਾਟੇ ਨੂੰ <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ਵਿੱਚ ਪੇਸਟ ਕੀਤਾ ਗਿਆ"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ਨੇ ਤੁਹਾਡੇ ਕਲਿੱਪਬੋਰਡ ਤੋਂ ਪੇਸਟ ਕੀਤਾ"</string> <string name="pasted_text" msgid="4298871641549173733">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਕਾਪੀ ਕੀਤੀ ਗਈ ਲਿਖਤ ਨੂੰ <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ਨੇ ਪੇਸਟ ਕੀਤਾ"</string> <string name="pasted_image" msgid="4729097394781491022">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਕਾਪੀ ਕੀਤੇ ਗਏ ਚਿੱਤਰ ਨੂੰ <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ਨੇ ਪੇਸਟ ਕੀਤਾ"</string> <string name="pasted_content" msgid="646276353060777131">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਕਾਪੀ ਕੀਤੀ ਗਈ ਸਮੱਗਰੀ ਨੂੰ <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ਨੇ ਪੇਸਟ ਕੀਤਾ"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> ਤਿਆਰ ਕਰ ਰਿਹਾ ਹੈ।"</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ਐਪਸ ਚਾਲੂ ਕਰ ਰਿਹਾ ਹੈ।"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"ਬੂਟ ਪੂਰਾ ਕਰ ਰਿਹਾ ਹੈ।"</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ਕੀ ਸਕ੍ਰੀਨ ਬੰਦ ਕਰਨੀ ਹੈ?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨ ਵੇਲੇ, ਤੁਸੀਂ ਪਾਵਰ ਬਟਨ ਨੂੰ ਦਬਾਇਆ ਹੈ।\n\nਇਹ ਆਮ ਤੌਰ \'ਤੇ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਨੂੰ ਬੰਦ ਕਰ ਦਿੰਦਾ ਹੈ।"</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ਬੰਦ ਕਰੋ"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ਰੱਦ ਕਰੋ"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> ਚੱਲ ਰਿਹਾ ਹੈ"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"ਗੇਮ \'ਤੇ ਵਾਪਸ ਜਾਣ ਲਈ ਟੈਪ ਕਰੋ"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"ਗੇਮ ਚੁਣੋ"</string> @@ -1874,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਮਿਟਾਇਆ ਗਿਆ"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ਠੀਕ ਹੈ"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"ਬੈਟਰੀ ਸੇਵਰ ਗੂੜ੍ਹੇ ਥੀਮ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਸਰਗਰਮੀ, ਕੁਝ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਭਾਵਾਂ, ਕੁਝ ਖਾਸ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਅਤੇ ਕੁਝ ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨਾਂ ਨੂੰ ਸੀਮਤ ਜਾਂ ਬੰਦ ਕਰਦਾ ਹੈ।\n\n"<annotation id="url">"ਹੋਰ ਜਾਣੋ"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"ਬੈਟਰੀ ਸੇਵਰ ਗੂੜ੍ਹੇ ਥੀਮ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਸਰਗਰਮੀ, ਕੁਝ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਭਾਵਾਂ, ਕੁਝ ਖਾਸ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਅਤੇ ਕੁਝ ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨਾਂ ਨੂੰ ਸੀਮਤ ਜਾਂ ਬੰਦ ਕਰਦਾ ਹੈ।"</string> <string name="data_saver_description" msgid="4995164271550590517">"ਡਾਟਾ ਵਰਤੋਂ ਘਟਾਉਣ ਵਿੱਚ ਮਦਦ ਲਈ, ਡਾਟਾ ਸੇਵਰ ਕੁਝ ਐਪਾਂ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਡਾਟਾ ਭੇਜਣ ਜਾਂ ਪ੍ਰਾਪਤ ਕਰਨ ਤੋਂ ਰੋਕਦਾ ਹੈ। ਤੁਹਾਡੇ ਵੱਲੋਂ ਵਰਤਮਾਨ ਤੌਰ \'ਤੇ ਵਰਤੀ ਜਾ ਰਹੀ ਐਪ ਡਾਟਾ \'ਤੇ ਪਹੁੰਚ ਕਰ ਸਕਦੀ ਹੈ, ਪਰ ਉਹ ਇੰਝ ਕਦੇ-ਕਦਾਈਂ ਕਰ ਸਕਦੀ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਇਸ ਦਾ ਮਤਲਬ ਇਹ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਚਿੱਤਰ ਤਦ ਤੱਕ ਨਹੀਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕੀਤੇ ਜਾਂਦੇ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਉਹਨਾਂ \'ਤੇ ਟੈਪ ਨਹੀਂ ਕਰਦੇ।"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ਕੀ ਡਾਟਾ ਸੇਵਰ ਚਾਲੂ ਕਰਨਾ ਹੈ?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ਚਾਲੂ ਕਰੋ"</string> @@ -1980,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ਐਪ ਫਿਲਹਾਲ ਉਪਲਬਧ ਨਹੀਂ ਹੈ। ਇਸਦਾ ਪ੍ਰਬੰਧਨ <xliff:g id="APP_NAME_1">%2$s</xliff:g> ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"ਹੋਰ ਜਾਣੋ"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ਐਪ ਤੋਂ ਰੋਕ ਹਟਾਓ"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਚਾਲੂ ਕਰਨੀਆਂ ਹਨ?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"ਆਪਣੀਆਂ ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਅਤੇ ਸੂਚਨਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਪ੍ਰਾਪਤ ਕਰੋ"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"ਚਾਲੂ ਕਰੋ"</string> <string name="app_blocked_title" msgid="7353262160455028160">"ਐਪ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਐਪ ਇਸ ਵੇਲੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index b77be3d5fa54..4478b8933fdf 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -1915,10 +1915,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Zaktualizowany przez administratora"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Usunięty przez administratora"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Oszczędzanie baterii uruchamia ciemny motyw oraz wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne, pewne funkcje oraz wybrane połączenia sieciowe.\n\n"<annotation id="url">"Więcej informacji"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Oszczędzanie baterii uruchamia ciemny motyw oraz wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne, pewne funkcje oraz wybrane połączenia sieciowe."</string> <string name="data_saver_description" msgid="4995164271550590517">"Oszczędzanie danych uniemożliwia niektórym aplikacjom wysyłanie i odbieranie danych w tle, zmniejszając w ten sposób ich użycie. Aplikacja, z której w tej chwili korzystasz, może uzyskiwać dostęp do danych, ale rzadziej. Może to powodować, że obrazy będą się wyświetlać dopiero po kliknięciu."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Włączyć Oszczędzanie danych?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Włącz"</string> @@ -2039,10 +2037,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikacja <xliff:g id="APP_NAME_0">%1$s</xliff:g> nie jest teraz dostępna. Zarządza tym aplikacja <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Więcej informacji"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Wznów działanie aplikacji"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Włączyć aplikacje służbowe?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Uzyskaj dostęp do aplikacji służbowych i powiadomień"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Włącz"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacja jest niedostępna"</string> <string name="app_blocked_message" msgid="542972921087873023">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest obecnie niedostępna."</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index db561c9c0f8d..3e29cb0c4a85 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -1869,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Atualizado pelo seu gestor"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado pelo seu gestor"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"A Poupança de bateria ativa o tema escuro e limita ou desativa a atividade em segundo plano, alguns efeitos visuais, determinadas funcionalidades e algumas ligações de rede.\n\n"<annotation id="url">"Saiba mais"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"A Poupança de bateria ativa o tema escuro e limita ou desativa a atividade em segundo plano, alguns efeitos visuais, determinadas funcionalidades e algumas ligações de rede."</string> <string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir a utilização de dados, a Poupança de dados impede que algumas aplicações enviem ou recebam dados em segundo plano. Uma determinada app que esteja a utilizar atualmente pode aceder aos dados, mas é possível que o faça com menos frequência. Isto pode significar, por exemplo, que as imagens não são apresentadas até que toque nas mesmas."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Pretende ativar a Poupança de dados?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string> @@ -1975,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"A app <xliff:g id="APP_NAME_0">%1$s</xliff:g> não está disponível neste momento. A app <xliff:g id="APP_NAME_1">%2$s</xliff:g> gere esta definição."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Saiba mais"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Retomar app"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Ativar as apps de trabalho?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Obtenha acesso às suas apps de trabalho e notificações"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Ativar"</string> <string name="app_blocked_title" msgid="7353262160455028160">"A app não está disponível"</string> <string name="app_blocked_message" msgid="542972921087873023">"De momento, a app <xliff:g id="APP_NAME">%1$s</xliff:g> não está disponível."</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 022e1cad8374..bf58bd00a8c2 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -1025,8 +1025,7 @@ <string name="text_copied" msgid="2531420577879738860">"Text copiat în clipboard."</string> <string name="copied" msgid="4675902854553014676">"Copiat"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat date din <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat din clipboard"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat textul copiat"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat o imagine copiată"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat conținutul copiat"</string> @@ -1280,14 +1279,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Se pregătește <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Se pornesc aplicațiile."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Se finalizează pornirea."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Dezactivați ecranul?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Ați apăsat butonul de pornire în timpul configurării amprentei.\n\nDe obicei, această acțiune dezactivează ecranul."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Dezactivați"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Anulați"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"Rulează <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Atingeți pentru a reveni la joc"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Alegeți jocul"</string> @@ -1740,8 +1735,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizați comanda rapidă"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversarea culorilor"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Corecția culorii"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modul cu o mână"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Luminozitate redusă suplimentar"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S-au apăsat lung tastele de volum. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S-au apăsat lung tastele de volum. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> @@ -1898,10 +1892,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizat de administratorul dvs."</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Șters de administratorul dvs."</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Economisirea bateriei activează tema întunecată și restricționează sau dezactivează activitatea în fundal, unele efecte vizuale, alte funcții și câteva conexiuni de rețea.\n\n"<annotation id="url">"Aflați mai multe"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Economisirea bateriei activează tema întunecată și restricționează sau dezactivează activitatea în fundal, unele efecte vizuale, alte funcții și câteva conexiuni la rețea."</string> <string name="data_saver_description" msgid="4995164271550590517">"Pentru a contribui la reducerea utilizării de date, Economizorul de date împiedică unele aplicații să trimită sau să primească date în fundal. O aplicație pe care o folosiți poate accesa datele, însă mai rar. Aceasta poate însemna, de exemplu, că imaginile se afișează numai după ce le atingeți."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Activați Economizorul de date?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Activați"</string> @@ -2013,10 +2005,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Momentan, aplicația <xliff:g id="APP_NAME_0">%1$s</xliff:g> nu este disponibilă. Aceasta este gestionată de <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Aflați mai multe"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Anulați întreruperea aplicației"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Activați aplicațiile pentru lucru?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Obțineți acces la aplicațiile pentru lucru și notificări"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Activați"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Aplicația nu este disponibilă"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu este disponibilă momentan."</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 86a0c0d9a8f7..c338e88be1c3 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1028,8 +1028,7 @@ <string name="text_copied" msgid="2531420577879738860">"Текст скопирован в буфер обмена."</string> <string name="copied" msgid="4675902854553014676">"Скопировано"</string> <string name="pasted_from_app" msgid="5627698450808256545">"Данные из приложения \"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>\" вставлены в приложение \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\"."</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"Приложение \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\" вставило данные из буфера обмена."</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: скопированный текст вставлен"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: скопированное изображение вставлено"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: скопированный контент вставлен"</string> @@ -1300,14 +1299,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Подготовка приложения \"<xliff:g id="APPNAME">%1$s</xliff:g>\"..."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск приложений."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Окончание загрузки..."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Отключить экран?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Во время настройки отпечатка пальца вы нажали кнопку питания.\n\nОбычно это действие приводит к отключению экрана."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Отключить"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Отмена"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"Приложение <xliff:g id="APP">%1$s</xliff:g> запущено"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Нажмите, чтобы вернуться в игру."</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Выберите игру"</string> @@ -1762,8 +1757,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Использовать быстрое включение"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Инверсия цветов"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Коррекция цвета"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим управления одной рукой"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дополнительное уменьшение яркости"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" включена."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" отключена."</string> @@ -1921,10 +1915,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Обновлено администратором"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Удалено администратором"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"В режиме энергосбережения включается тёмная тема, ограничиваются или отключаются фоновые процессы, некоторые визуальные эффекты, определенные функции и ряд сетевых подключений.\n\n"<annotation id="url">"Подробнее…"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"В режиме энергосбережения включается тёмная тема, ограничиваются или отключаются фоновые процессы, некоторые визуальные эффекты, определенные функции и ряд сетевых подключений."</string> <string name="data_saver_description" msgid="4995164271550590517">"В режиме экономии трафика фоновая передача данных для некоторых приложений отключена. Приложение, которым вы пользуетесь, может получать и отправлять данные, но реже, чем обычно. Например, изображения могут не загружаться, пока вы не нажмете на них."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Включить экономию трафика?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Включить"</string> @@ -2045,10 +2037,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Приложение \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" недоступно. Его работу ограничивает приложение \"<xliff:g id="APP_NAME_1">%2$s</xliff:g>\"."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Подробнее"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Возобновить работу приложения"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Включить рабочие приложения?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Вы получите доступ к рабочим приложениям и уведомлениям"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Включить"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Приложение недоступно"</string> <string name="app_blocked_message" msgid="542972921087873023">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" сейчас недоступно."</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 5d38e46b81d3..51c4077ee955 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"කෙටිමඟ භාවිතා කරන්න"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"වර්ණ අපවර්තනය"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"වර්ණ නිවැරදි කිරීම"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"තනි අත් ප්රකාරය"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"තවත් අඳුරු"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාත්මකයි."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාවිරහිතයි."</string> @@ -1870,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"ඔබගේ පරිපාලක මඟින් යාවත්කාලීන කර ඇත"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"ඔබගේ පරිපාලක මඟින් මකා දමා ඇත"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"හරි"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"බැටරි සුරැකුම අඳුරු තේමාව ක්රියාත්මක කර පසුබිම් ක්රියාකාරකම්, සමහර දෘශ්ය ප්රයෝග, යම් විශේෂාංග සහ සමහර ජාල සම්බන්ධතා සීමා හෝ ක්රියාවිරහිත කරයි.\n\n"<annotation id="url">"තව දැන ගන්න"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"බැටරි සුරැකුම අඳුරු තේමාව ක්රියාත්මක කර පසුබිම් ක්රියාකාරකම්, සමහර දෘශ්ය ප්රයෝග, යම් විශේෂාංග සහ සමහර ජාල සම්බන්ධතා සීමා හෝ ක්රියාවිරහිත කරයි."</string> <string name="data_saver_description" msgid="4995164271550590517">"දත්ත භාවිතය අඩු කිරීමට උදවු වීමට, දත්ත සුරැකුම සමහර යෙදුම් පසුබිමින් දත්ත යැවීම සහ ලබා ගැනීම වළක්වයි. ඔබ දැනට භාවිත කරන යෙදුමකට දත්ත වෙත පිවිසීමට හැකිය, නමුත් එසේ කරන්නේ කලාතුරකින් විය හැකිය. මෙයින් අදහස් වන්නේ, උදාහරණයක් ලෙස, එම රූප ඔබ ඒවාට තට්ටු කරන තෙක් සංදර්ශනය නොවන බවය."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"දත්ත සුරැකුම ක්රියාත්මක කරන්නද?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ක්රියාත්මක කරන්න"</string> @@ -1976,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> මේ අවස්ථාවේදී ලබා ගත නොහැකිය. මෙය <xliff:g id="APP_NAME_1">%2$s</xliff:g> මගින් කළමනාකරණය කෙරේ."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"තව දැන ගන්න"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"යෙදුම විරාම කිරීම ඉවත් කරන්න"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"කාර්යාල යෙදු. ක්රියා. කරන්නද?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"ඔබගේ කාර්යාල යෙදුම් සහ දැනුම්දීම් වෙත ප්රවේශය ලබා ගන්න"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"ක්රියාත්මක කරන්න"</string> <string name="app_blocked_title" msgid="7353262160455028160">"යෙදුම ලබා ගත නොහැකිය"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> මේ දැන් ලබා ගත නොහැකිය."</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index ac5483a34843..95974b436668 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -1757,8 +1757,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Použiť skratku"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzia farieb"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Úprava farieb"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Režim jednej ruky"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Veľmi tmavé"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vypnutá."</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 7dce14894c39..b18afc30e64e 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -1028,8 +1028,7 @@ <string name="text_copied" msgid="2531420577879738860">"Besedilo, kopirano v odložišče."</string> <string name="copied" msgid="4675902854553014676">"Kopirano"</string> <string name="pasted_from_app" msgid="5627698450808256545">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je prilepila iz aplikacije <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>."</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je prilepila iz odložišča."</string> <string name="pasted_text" msgid="4298871641549173733">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je prilepila besedilo iz odložišča."</string> <string name="pasted_image" msgid="4729097394781491022">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je prilepila sliko iz odložišča."</string> <string name="pasted_content" msgid="646276353060777131">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je prilepila vsebino iz odložišča."</string> @@ -1300,14 +1299,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Pripravljanje aplikacije <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Zagon aplikacij."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Dokončevanje zagona."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Želite izklopiti zaslon?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Pri nastavljanju prstnega odtisa ste pritisnili gumb za vklop.\n\nS tem običajno izklopite zaslon."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Izklopi"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Prekliči"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> se izvaja"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Dotaknite se za vrnitev v igro"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Izberite igro"</string> @@ -1762,8 +1757,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Uporabi bližnjico"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija barv"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Popravljanje barv"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enoročni način"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Zelo zatemnjeno"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vklopljena."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je izklopljena."</string> @@ -1921,10 +1915,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Posodobil skrbnik"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisal skrbnik"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"V redu"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Funkcija varčevanja z energijo baterije vklopi temno temo ter omeji ali izklopi dejavnost v ozadju, nekatere vizualne učinke, določene funkcije in nekatere omrežne povezave.\n\n"<annotation id="url">"Več o tem"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Funkcija varčevanja z energijo baterije vklopi temno temo ter omeji ali izklopi dejavnost v ozadju, nekatere vizualne učinke, določene funkcije in nekatere omrežne povezave."</string> <string name="data_saver_description" msgid="4995164271550590517">"Zaradi zmanjševanja prenesene količine podatkov funkcija varčevanja s podatki nekaterim aplikacijam preprečuje, da bi v ozadju pošiljale ali prejemale podatke. Aplikacija, ki jo trenutno uporabljate, lahko prenaša podatke, vendar to morda počne manj pogosto. To na primer pomeni, da se slike ne prikažejo, dokler se jih ne dotaknete."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Vklop varčevanja s podatki?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Vklopi"</string> @@ -2045,10 +2037,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> trenutno ni na voljo. To upravlja aplikacija <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Več o tem"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Prekliči začasno zaustavitev aplikacije"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Vklop delovnih aplikacij?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Omogočanje dostopa do delovnih aplikacij in obvestil za delovni profil"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Vklop"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija ni na voljo"</string> <string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno ni na voljo."</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index d895f74a0e0b..49ec75c0e3b3 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Përdor shkurtoren"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Anasjellja e ngjyrës"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Korrigjimi i ngjyrës"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modaliteti i përdorimit me një dorë"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Shumë më i zbehtë"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tastet e volumit të mbajtura shtypur. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> i aktivizuar."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tastet e volumit të mbajtura shtypur. U çaktivizua \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 35129455625f..44644fd903b7 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -1892,10 +1892,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирао је администратор"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Избрисао је администратор"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Потврди"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Уштеда батерије укључује Тамну тему и ограничава или искључује активности у позадини, неке визуелне ефекте, одређене функције и мрежне везе.\n\n"<annotation id="url">"Сазнајте више"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Уштеда батерије укључује тамну тему и ограничава или искључује активности у позадини, неке визуелне ефекте, одређене функције и неке мрежне везе."</string> <string name="data_saver_description" msgid="4995164271550590517">"Да би се смањила потрошња података, Уштеда података спречава неке апликације да шаљу или примају податке у позадини. Апликација коју тренутно користите може да приступа подацима, али ће то чинити ређе. На пример, слике се неће приказивати док их не додирнете."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Желите да укључите Уштеду података?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Укључи"</string> @@ -2007,10 +2005,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Апликација <xliff:g id="APP_NAME_0">%1$s</xliff:g> тренутно није доступна. <xliff:g id="APP_NAME_1">%2$s</xliff:g> управља доступношћу."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Сазнајте више"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Опозови паузирање апликације"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Укључити пословне апликације?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Приступајте пословним апликацијама и обавештењима"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Укључи"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Апликација није доступна"</string> <string name="app_blocked_message" msgid="542972921087873023">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> тренутно није доступна."</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 71fcb7da984d..760a5c08ff99 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1713,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Använd kortkommandot"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverterade färger"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Färgkorrigering"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhandsläge"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extradimmat"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har aktiverats."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har inaktiverats."</string> @@ -1870,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Administratören uppdaterade paketet"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Administratören raderade paketet"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"I batterisparläget aktiveras mörkt tema medan bakgrundsaktivitet, vissa visuella effekter och funktioner samt vissa nätverksanslutningar begränsas eller inaktiveras.\n\n"<annotation id="url">"Läs mer"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"I batterisparläget aktiveras mörkt tema medan bakgrundsaktivitet, vissa visuella effekter och funktioner samt vissa nätverksanslutningar begränsas eller inaktiveras."</string> <string name="data_saver_description" msgid="4995164271550590517">"Med Databesparing kan du minska dataanvändningen genom att hindra en del appar från att skicka eller ta emot data i bakgrunden. Appar som du använder kan komma åt data, men det sker kanske inte lika ofta. Detta innebär t.ex. att bilder inte visas förrän du trycker på dem."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Vill du aktivera Databesparing?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivera"</string> @@ -1976,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> är inte tillgänglig just nu. Detta hanteras av <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Läs mer"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Återuppta app"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Vill du aktivera jobbappar?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Få åtkomst till jobbappar och aviseringar"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivera"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Appen är inte tillgänglig"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> är inte tillgängligt just nu."</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index cf224927cad1..20f0543b2cd5 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Maandishi yamenakiliwa kwenye ubao wa kunakili."</string> <string name="copied" msgid="4675902854553014676">"Umenakili"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> imebandika kutoka <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> imebandika kutoka ubao wako wa kunakili"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> imebandika maandishi uliyonakili"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> imebandika picha uliyonakili"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> imebandika maudhui uliyonakili"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Inaandaa <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Programu zinaanza"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Inamaliza kuwasha."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ungependa kuzima skrini?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Ulipokuwa ukiweka alama ya kidole chako, ulibonyeza Kitufe cha kuwasha/kuzima.\n\nKwa kawaida, hatua hii huzima skrini yako."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Zima"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Ghairi"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> inaendelea"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Gusa ili urudi kwenye mchezo"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Chagua mchezo"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Tumia Njia ya Mkato"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Ugeuzaji rangi"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Usahihishaji wa rangi"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Hali ya kutumia kwa mkono mmoja"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Kipunguza mwangaza zaidi"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Vitufe vya sauti vilivyoshikiliwa. Umewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Vitufe vya sauti vimeshikiliwa. Umezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Imesasishwa na msimamizi wako"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Imefutwa na msimamizi wako"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Sawa"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Kiokoa Betri huwasha Mandhari meusi na kudhibiti au kuzima shughuli za chinichini, baadhi ya madoido yanayoonekana, vipengele fulani na baadhi ya miunganisho ya mtandao.\n\n"<annotation id="url">"Pata maelezo zaidi"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Kiokoa Betri huwasha Mandhari meusi na kudhibiti au kuzima shughuli za chinichini, baadhi ya madoido yanayoonekana, vipengele fulani na baadhi ya miunganisho ya mtandao."</string> <string name="data_saver_description" msgid="4995164271550590517">"Ili kusaidia kupunguza matumizi ya data, Kiokoa Data huzuia baadhi ya programu kupokea na kutuma data chinichini. Programu ambayo unatumia sasa inaweza kufikia data, lakini si kila wakati. Kwa mfano, haitaonyesha picha hadi utakapozifungua."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Ungependa Kuwasha Kiokoa Data?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Washa"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> haipatikani kwa sasa. Inasimamiwa na <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Pata maelezo zaidi"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Acha kusimamisha programu"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Utawasha programu za kazini?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Pata uwezo wa kufikia arifa na programu zako za kazini"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Washa"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Programu haipatikani"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> haipatikani hivi sasa."</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 7d853eece02f..2473bca4f3a9 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"உரை கிளிப்போர்டிற்கு நகலெடுக்கப்பட்டது."</string> <string name="copied" msgid="4675902854553014676">"நகலெடுக்கப்பட்டது"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> ஆப்ஸிலிருந்து <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ஒட்டப்பட்டது"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"கிளிப்போர்டில் இருந்து <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ஒட்டப்பட்டது"</string> <string name="pasted_text" msgid="4298871641549173733">"நீங்கள் நகலெடுத்த உரையை <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ஆப்ஸ் ஒட்டியது"</string> <string name="pasted_image" msgid="4729097394781491022">"நீங்கள் நகலெடுத்த படத்தை <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ஆப்ஸ் ஒட்டியது"</string> <string name="pasted_content" msgid="646276353060777131">"நீங்கள் நகலெடுத்த உள்ளடக்கத்தை <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ஆப்ஸ் ஒட்டியது"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>ஐத் தயார்செய்கிறது."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ஆப்ஸ் தொடங்கப்படுகின்றன."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"துவக்குதலை முடிக்கிறது."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"திரையை ஆஃப் செய்யவா?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"கைரேகையை அமைக்கும்போது பவர் பட்டனை அழுத்திவிட்டீர்கள்.\n\nஇது திரையை ஆஃப் செய்துவிடும்."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ஆஃப் செய்"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ரத்துசெய்"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> இயங்குகிறது"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"கேமிற்குச் செல்ல, தட்டவும்"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"கேமைத் தேர்வுசெய்க"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ஷார்ட்கட்டைப் பயன்படுத்து"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"நிற நேரெதிர் மாற்றம்"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"நிறத் திருத்தம்"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ஒற்றைக் கைப் பயன்முறை"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"மிகக் குறைவான வெளிச்சம்"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆன் செய்யப்பட்டது."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆஃப் செய்யப்பட்டது."</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"உங்கள் நிர்வாகி புதுப்பித்துள்ளார்"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"உங்கள் நிர்வாகி நீக்கியுள்ளார்"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"சரி"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"பேட்டரி சேமிப்பான் டார்க் தீமினை ஆன் செய்து பின்னணிச் செயல்பாடு, சில விஷுவல் எஃபெக்ட்கள், குறிப்பிட்ட அம்சங்கள், சில நெட்வொர்க் இணைப்புகள் ஆகியவற்றைக் கட்டுப்படுத்தும் அல்லது ஆஃப் செய்யும்.\n\n"<annotation id="url">"மேலும் அறிக"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"பேட்டரி சேமிப்பான் டார்க் தீமினை ஆன் செய்து பின்னணிச் செயல்பாடு, சில விஷுவல் எஃபெக்ட்கள், குறிப்பிட்ட அம்சங்கள், சில நெட்வொர்க் இணைப்புகள் ஆகியவற்றைக் கட்டுப்படுத்தும் அல்லது ஆஃப் செய்யும்."</string> <string name="data_saver_description" msgid="4995164271550590517">"டேட்டா உபயோகத்தைக் குறைப்பதற்கு உதவ, பின்புலத்தில் டேட்டாவை அனுப்புவது அல்லது பெறுவதிலிருந்து சில ஆப்ஸை டேட்டா சேமிப்பான் தடுக்கும். தற்போது பயன்படுத்தும் ஆப்ஸானது எப்போதாவது டேட்டாவை அணுகலாம். எடுத்துக்காட்டாக, படங்களை நீங்கள் தட்டும் வரை அவை காட்டப்படாது."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"டேட்டா சேமிப்பானை இயக்கவா?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"இயக்கு"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"இப்போது <xliff:g id="APP_NAME_0">%1$s</xliff:g> ஆப்ஸை உபயோகிக்க இயலாது. இதை <xliff:g id="APP_NAME_1">%2$s</xliff:g> நிர்வகிக்கிறது."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"மேலும் அறிக"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ஆப்ஸ் இயக்கு"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"பணி ஆப்ஸை இயக்கவா?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"உங்கள் பணி ஆப்ஸுக்கும் அறிவிப்புகளுக்குமான அணுகலைப் பெறுங்கள்"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"இயக்கு"</string> <string name="app_blocked_title" msgid="7353262160455028160">"இந்த ஆப்ஸ் இப்போது கிடைப்பதில்லை"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் இப்போது கிடைப்பதில்லை."</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 13b3e2cd9d9e..523a1e459a52 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"వచనం క్లిప్బోర్డ్కు కాపీ చేయబడింది."</string> <string name="copied" msgid="4675902854553014676">"కాపీ చేయబడింది"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> నుండి పేస్ట్ చేయబడింది"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"మీ క్లిప్బోర్డ్ నుండి <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> పేస్ట్ చేయబడింది"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> మీరు కాపీ చేసిన టెక్స్ట్ను పేస్ట్ చేసింది"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> మీరు కాపీ చేసిన ఇమేజ్ను పేస్ట్ చేసింది"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> మీరు కాపీ చేసిన కంటెంట్ను పేస్ట్ చేసింది"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>ని సిద్ధం చేస్తోంది."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"అనువర్తనాలను ప్రారంభిస్తోంది."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"బూట్ను ముగిస్తోంది."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"స్క్రీన్ను ఆఫ్ చేయాలా?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"మీ వేలిముద్రను సెటప్ చేస్తున్నప్పుడు, మీరు పవర్ బటన్ను నొక్కారు.\n\nఇది సాధారణంగా మీ స్క్రీన్ను ఆఫ్ చేస్తుంది."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ఆఫ్ చేయండి"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"రద్దు చేయండి"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> అమలవుతోంది"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"గేమ్కి తిరిగి రావడానికి నొక్కండి"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"గేమ్ను ఎంచుకోండి"</string> @@ -1566,8 +1561,8 @@ <string name="keyboardview_keycode_enter" msgid="168054869339091055">"Enter"</string> <string name="activitychooserview_choose_application" msgid="3500574466367891463">"యాప్ను ఎంచుకోండి"</string> <string name="activitychooserview_choose_application_error" msgid="6937782107559241734">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>ని ప్రారంభించడం సాధ్యపడలేదు"</string> - <string name="shareactionprovider_share_with" msgid="2753089758467748982">"వీటితో భాగస్వామ్యం చేయండి"</string> - <string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>తో భాగస్వామ్యం చేయండి"</string> + <string name="shareactionprovider_share_with" msgid="2753089758467748982">"వీటితో షేర్ చేయండి"</string> + <string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>తో షేర్ చేయండి"</string> <string name="content_description_sliding_handle" msgid="982510275422590757">"స్లైడింగ్ హ్యాండిల్. తాకి, ఆపై నొక్కి ఉంచండి."</string> <string name="description_target_unlock_tablet" msgid="7431571180065859551">"అన్లాక్ చేయడానికి స్వైప్ చేయండి."</string> <string name="action_bar_home_description" msgid="1501655419158631974">"హోమ్కు నావిగేట్ చేయండి"</string> @@ -1611,7 +1606,7 @@ <string name="sha1_fingerprint" msgid="2339915142825390774">"SHA-1 వేలిముద్ర:"</string> <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"అన్నీ చూడండి"</string> <string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"కార్యాచరణను ఎంచుకోండి"</string> - <string name="share_action_provider_share_with" msgid="1904096863622941880">"వీటితో భాగస్వామ్యం చేయండి"</string> + <string name="share_action_provider_share_with" msgid="1904096863622941880">"వీటితో షేర్ చేయండి"</string> <string name="sending" msgid="206925243621664438">"పంపుతోంది..."</string> <string name="launchBrowserDefault" msgid="6328349989932924119">"బ్రౌజర్ను ప్రారంభించాలా?"</string> <string name="SetupCallDefault" msgid="5581740063237175247">"కాల్ను ఆమోదించాలా?"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"సత్వరమార్గాన్ని ఉపయోగించు"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"కలర్ మార్పిడి"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"కలర్ సరిచేయడం"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"వన్-హ్యాండెడ్ మోడ్"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"కాంతిని మరింత డిమ్ చేయడం"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆన్ చేయబడింది"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆఫ్ చేయబడింది"</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"మీ నిర్వాహకులు నవీకరించారు"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"మీ నిర్వాహకులు తొలగించారు"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"సరే"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"బ్యాటరీ సేవర్ ముదురు రంగు రూపాన్ని ఆన్ చేసి, బ్యాక్గ్రౌండ్ యాక్టివిటీ, కొన్ని విజువల్ ఎఫెక్ట్లు, నిర్దిష్ట ఫీచర్లు, ఇంకా కొన్ని నెట్వర్క్ కనెక్షన్లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది.\n\n"<annotation id="url">"మరింత తెలుసుకోండి"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"బ్యాటరీ సేవర్ ముదురు రంగు రూపాన్ని ఆన్ చేసి, బ్యాక్గ్రౌండ్ యాక్టివిటీ, కొన్ని విజువల్ ఎఫెక్ట్లు, నిర్దిష్ట ఫీచర్లు, ఇంకా కొన్ని నెట్వర్క్ కనెక్షన్లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది."</string> <string name="data_saver_description" msgid="4995164271550590517">"డేటా వినియోగాన్ని తగ్గించడంలో డేటా సేవర్ సహాయకరంగా ఉంటుంది. బ్యాక్గ్రౌండ్లో కొన్ని యాప్లు డేటాను పంపకుండా లేదా స్వీకరించకుండా నిరోధిస్తుంది. మీరు ప్రస్తుతం ఉపయోగిస్తోన్న యాప్, డేటాను యాక్సెస్ చేయగలదు. కానీ తక్కువ సార్లు మాత్రమే అలా చేయవచ్చు. ఉదాహరణకు, మీరు నొక్కే వరకు ఫోటోలు ప్రదర్శించబడవు."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"డేటా సేవర్ను ఆన్ చేయాలా?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"ఆన్ చేయి"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ప్రస్తుతం అందుబాటులో లేదు. ఇది <xliff:g id="APP_NAME_1">%2$s</xliff:g> ద్వారా నిర్వహించబడుతుంది."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"మరింత తెలుసుకోండి"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"యాప్పై వున్న పాజ్ను తొలగించండి"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"వర్క్ యాప్లను ఆన్ చేయాలా?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"మీ వర్క్ యాప్లు, నోటిఫికేషన్లకు యాక్సెస్ను పొందండి"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"ఆన్ చేయి"</string> <string name="app_blocked_title" msgid="7353262160455028160">"యాప్ అందుబాటులో లేదు"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రస్తుతం అందుబాటులో లేదు."</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 663fc848acc4..cc616f09ac63 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"คัดลอกข้อความไปยังคลิปบอร์ด"</string> <string name="copied" msgid="4675902854553014676">"คัดลอกแล้ว"</string> <string name="pasted_from_app" msgid="5627698450808256545">"วาง <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> จาก <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> แล้ว"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"วาง <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> จากคลิปบอร์ดแล้ว"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> วางข้อความที่คุณคัดลอก"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> วางรูปภาพที่คุณคัดลอก"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> วางเนื้อหาที่คุณคัดลอก"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"กำลังเตรียม <xliff:g id="APPNAME">%1$s</xliff:g>"</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"กำลังเริ่มต้นแอปพลิเคชัน"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"เสร็จสิ้นการบูต"</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"ปิดหน้าจอใช่ไหม"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"ขณะตั้งค่าลายนิ้วมือคุณจะต้องกดปุ่มเปิด/ปิด\n\nซึ่งโดยปกติจะเป็นการปิดหน้าจอด้วย"</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"ปิด"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"ยกเลิก"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> กำลังทำงาน"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"แตะเพื่อกลับไปที่เกม"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"เลือกเกม"</string> @@ -1874,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"อัปเดตโดยผู้ดูแลระบบ"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"ลบโดยผู้ดูแลระบบ"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ตกลง"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"โหมดประหยัดแบตเตอรี่จะเปิดธีมมืดและจำกัดหรือปิดกิจกรรมในเบื้องหลัง เอฟเฟกต์ภาพบางอย่าง ฟีเจอร์บางส่วน และการเชื่อมต่อบางเครือข่าย\n\n"<annotation id="url">"ดูข้อมูลเพิ่มเติม"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"โหมดประหยัดแบตเตอรี่จะเปิดธีมมืดและจำกัดหรือปิดกิจกรรมในเบื้องหลัง เอฟเฟกต์ภาพบางอย่าง ฟีเจอร์บางส่วน และการเชื่อมต่อบางเครือข่าย"</string> <string name="data_saver_description" msgid="4995164271550590517">"เพื่อช่วยลดปริมาณการใช้อินเทอร์เน็ต โปรแกรมประหยัดอินเทอร์เน็ตจะช่วยป้องกันไม่ให้บางแอปส่งหรือรับข้อมูลโดยการใช้อินเทอร์เน็ตอยู่เบื้องหลัง แอปที่คุณกำลังใช้งานสามารถเข้าถึงอินเทอร์เน็ตได้ แต่อาจไม่บ่อยเท่าเดิม ตัวอย่างเช่น ภาพต่างๆ จะไม่แสดงจนกว่าคุณจะแตะที่ภาพเหล่านั้น"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"เปิดการประหยัดอินเทอร์เน็ตไหม"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"เปิด"</string> @@ -1980,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"เปิด <xliff:g id="APP_NAME_0">%1$s</xliff:g> ไม่ได้ในขณะนี้ แอปนี้จัดการโดย <xliff:g id="APP_NAME_1">%2$s</xliff:g>"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"ดูข้อมูลเพิ่มเติม"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ยกเลิกการหยุดแอปชั่วคราว"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"เปิดแอปงานใช่ไหม"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"รับสิทธิ์เข้าถึงแอปงานและการแจ้งเตือนต่างๆ"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"เปิด"</string> <string name="app_blocked_title" msgid="7353262160455028160">"แอปไม่พร้อมใช้งาน"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ไม่พร้อมใช้งานในขณะนี้"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index e26ce1c94ef9..8ba11b9b46ce 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Nakopya ang teksto sa clipboard."</string> <string name="copied" msgid="4675902854553014676">"Nakopya"</string> <string name="pasted_from_app" msgid="5627698450808256545">"Na-paste ang <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> mula sa <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"Na-paste ang <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> mula sa iyong clipboard"</string> <string name="pasted_text" msgid="4298871641549173733">"Nag-paste ang <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ng text na nakopya mo"</string> <string name="pasted_image" msgid="4729097394781491022">"Nag-paste ang <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ng larawang nakopya mo"</string> <string name="pasted_content" msgid="646276353060777131">"Nag-paste ang <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ng content na nakopya mo"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Ihinahanda ang <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Sinisimulan ang apps."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Pagtatapos ng pag-boot."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"I-off ang screen?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Habang sine-set up ang iyong fingerprint, pinindot mo ang Power button.\n\nKaraniwan nitong ino-off ang iyong screen."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"I-off"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Kanselahin"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"Tumatakbo ang <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Mag-tap upang bumalik sa laro"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Pumili ng laro"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gamitin ang Shortcut"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Pag-invert ng Kulay"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Pagwawasto ng Kulay"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-Hand mode"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pinindot nang matagal ang volume keys. Na-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pinindot nang matagal ang volume keys. Na-off ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Na-update ng iyong admin"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Na-delete ng iyong admin"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Ino-on ng Pantipid ng Baterya ang Madilim na tema at nililimitahan o ino-off nito ang aktibidad sa background, ilang visual effect, ilang partikular na feature, at ilang koneksyon sa network.\n\n"<annotation id="url">"Matuto pa"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Ino-on ng Pantipid ng Baterya ang Madilim na tema at nililimitahan o ino-off nito ang aktibidad sa background, ilang visual effect, ilang partikular na feature, at ilang koneksyon sa network."</string> <string name="data_saver_description" msgid="4995164271550590517">"Upang makatulong na mabawasan ang paggamit ng data, pinipigilan ng Data Saver ang ilang app na magpadala o makatanggap ng data sa background. Maaaring mag-access ng data ang isang app na ginagamit mo sa kasalukuyan, ngunit mas bihira na nito magagawa iyon. Halimbawa, maaaring hindi lumabas ang mga larawan hangga\'t hindi mo nata-tap ang mga ito."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"I-on ang Data Saver?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"I-on"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Hindi available ang <xliff:g id="APP_NAME_0">%1$s</xliff:g> sa ngayon. Pinamamahalaan ito ng <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Matuto pa"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"I-unpause ang app"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"I-on ang app para sa trabaho?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Makakuha ng access sa iyong mga app para sa trabaho at notification"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"I-on"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Hindi available ang app"</string> <string name="app_blocked_message" msgid="542972921087873023">"Hindi available sa ngayon ang <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 9c4a4d4b9679..91430db16e3e 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Metin panoya kopyalandı."</string> <string name="copied" msgid="4675902854553014676">"Kopyalandı"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> uygulaması <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> kaynağından yapıştırdı"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>, panonuzdakini yapıştırdı"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>, kopyaladığınız metni yapıştırdı"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>, kopyaladığınız resmi yapıştırdı"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>, kopyaladığınız içeriği yapıştırdı"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> hazırlanıyor."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Uygulamalar başlatılıyor"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Açılış tamamlanıyor."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Ekran kapatılsın mı?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Parmak izinizi ayarlarken Güç düğmesine bastınız.\n\nBu hareket genellikle ekranınızın kapanmasına neden olur."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Kapat"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"İptal"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> çalışıyor"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Oyuna geri dönmek için dokunun"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Oyun seçin"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kısayolu Kullan"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Rengi Ters Çevirme"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Renk Düzeltme"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Tek El modu"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra loş"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> açıldı."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kapatıldı."</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Yöneticiniz tarafından güncellendi"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Yöneticiniz tarafından silindi"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"Tamam"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Pil Tasarrufu, Koyu temayı açıp arka plan etkinliğini, bazı görsel efektleri, belirli özellikleri ve bazı ağ bağlantılarını sınırlandırır veya kapatır.\n\n"<annotation id="url">"Daha fazla bilgi"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Pil Tasarrufu, Koyu temayı açıp arka plan etkinliğini, bazı görsel efektleri, belirli özellikleri ve bazı ağ bağlantılarını sınırlandırır veya kapatır."</string> <string name="data_saver_description" msgid="4995164271550590517">"Veri kullanımını azaltmaya yardımcı olması için Veri Tasarrufu, bazı uygulamaların arka planda veri göndermesini veya almasını engeller. Kullanmakta olduğunuz bir uygulama veri bağlantısına erişebilir, ancak bunu daha seyrek yapabilir. Bu durumda örneğin, siz resimlere dokunmadan resimler görüntülenmez."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Veri Tasarrufu açılsın mı?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aç"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> uygulaması şu anda kullanılamıyor. Uygulamanın kullanım durumu <xliff:g id="APP_NAME_1">%2$s</xliff:g> tarafından yönetiliyor."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Daha fazla bilgi"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Uygulamanın duraklatmasını kaldır"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"İş uygulamaları açılsın mı?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"İş uygulamalarınıza ve bildirimlerinize erişin"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Aç"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Uygulama kullanılamıyor"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulaması şu anda kullanılamıyor."</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index bfac2642471b..0af700d86728 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -1028,8 +1028,7 @@ <string name="text_copied" msgid="2531420577879738860">"Текст скопійов. в буф. обм."</string> <string name="copied" msgid="4675902854553014676">"Скопійовано"</string> <string name="pasted_from_app" msgid="5627698450808256545">"Дані з додатка <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> вставлено в <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"Дані з буфера обміну вставлено в додатку <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string> <string name="pasted_text" msgid="4298871641549173733">"Скопійований текст вставлено в додатку <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string> <string name="pasted_image" msgid="4729097394781491022">"Скопійоване зображення вставлено в додатку <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string> <string name="pasted_content" msgid="646276353060777131">"Скопійований контент вставлено в додатку <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string> @@ -1300,14 +1299,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Підготовка додатка <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск програм."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Завершення завантаження."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Вимкнути екран?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Налаштовуючи відбиток пальця, ви натиснули кнопку живлення.\n\nЗазвичай після цього вимикається екран."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Вимкнути"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Скасувати"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"Працює <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Торкніться, щоб повернутися в гру"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Виберіть гру"</string> @@ -1762,8 +1757,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Використовувати ярлик"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія кольорів"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Корекція кольорів"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим керування однією рукою"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Додаткове зменшення яскравості"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> увімкнено."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> вимкнено."</string> @@ -1921,10 +1915,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Оновлено адміністратором"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Видалено адміністратором"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ОК"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"У режимі енергозбереження вмикається Темна тема й обмежуються чи вимикаються дії у фоновому режимі, а також деякі візуальні ефекти, функції та з’єднання з мережами.\n\n"<annotation id="url">"Докладніше"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"У режимі енергозбереження вмикається Темна тема й обмежуються чи вимикаються дії у фоновому режимі, а також деякі візуальні ефекти, функції та з’єднання з мережами."</string> <string name="data_saver_description" msgid="4995164271550590517">"Щоб зменшити використання трафіку, функція \"Заощадження трафіку\" не дозволяє деяким додаткам надсилати чи отримувати дані у фоновому режимі. Поточний додаток зможе отримувати доступ до таких даних, але рідше. Наприклад, зображення не відображатиметься, доки ви не торкнетеся його."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Увімкнути заощадження трафіку?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Увімкнути"</string> @@ -2045,10 +2037,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"Додаток <xliff:g id="APP_NAME_0">%1$s</xliff:g> зараз недоступний. Керує додаток <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Докладніше"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Відновити доступ до додатка"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Увімкнути робочі додатки?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Отримайте доступ до своїх робочих додатків і сповіщень"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Увімкнути"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Додаток недоступний"</string> <string name="app_blocked_message" msgid="542972921087873023">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> зараз недоступний."</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index fedb557fe6f6..11441b38c888 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"متن کو کلپ بورڈ پر کاپی کیا گیا۔"</string> <string name="copied" msgid="4675902854553014676">"کاپی ہو گیا"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> سے <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> میں پیسٹ کیا گیا"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> نے آپ کے کلپ بورڈ سے پپیسٹ کر دیا"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> نے آپ کا کاپی کردہ ٹیکسٹ پیسٹ کر دیا"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> نے آپ کی کاپی کردہ ایک تصویر پیسٹ کر دی"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> نے آپ کا کاپی کردہ مواد پیسٹ کر دیا"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> تیار ہو رہی ہے۔"</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ایپس شروع ہو رہی ہیں۔"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"بوٹ مکمل ہو رہا ہے۔"</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"اسکرین آف کریں؟"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"اپنی فنگر پرنٹ سیٹ اپ کرنے کے دوران آپ نے پاور بٹن دبایا۔\n\n یہ عام طور پر آپ کی اسکرین کو آف کرتی ہے۔"</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"آف کریں"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"منسوخ کریں"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> چل رہی ہے"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"گیم پر واپس جانے کے لیے تھپتھپائیں"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"گیم کا انتخاب کریں"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"شارٹ کٹ استعمال کریں"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"رنگوں کی تقلیب"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"رنگ کی تصحیح"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ایک ہاتھ کی وضع"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"اضافی دھندلا"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آن ہے۔"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آف ہے۔"</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"آپ کے منتظم کے ذریعے اپ ڈیٹ کیا گیا"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"آپ کے منتظم کے ذریعے حذف کیا گیا"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"ٹھیک ہے"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"بیٹری سیور گہری تھیم کو آن کرتی ہے اور پس منظر کی سرگرمی، کچھ بصری اثرات، مخصوص خصوصیات اور کچھ نیٹ ورک کنکشنز کو محدود یا آف کرتی ہے۔\n\n"<annotation id="url">"مزید جانیں"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"بیٹری سیور گہری تھیم کو آن کرتی ہے اور پس منظر کی سرگرمی، کچھ بصری اثرات، مخصوص خصوصیات اور کچھ نیٹ ورک کنکشنز کو محدود یا آف کرتی ہے۔"</string> <string name="data_saver_description" msgid="4995164271550590517">"ڈیٹا کے استعمال کو کم کرنے میں مدد کیلئے، ڈیٹا سیور پس منظر میں کچھ ایپس کو ڈیٹا بھیجنے یا موصول کرنے سے روکتی ہے۔ آپ جو ایپ فی الحال استعمال کر رہے ہیں وہ ڈیٹا تک رسائی کر سکتی ہے مگر ہو سکتا ہے ایسا اکثر نہ ہو۔ اس کا مطلب مثال کے طور پر یہ ہو سکتا ہے کہ تصاویر تھپتھپانے تک ظاہر نہ ہوں۔"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"ڈیٹا سیور آن کریں؟"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"آن کریں"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ابھی دستیاب نہیں ہے۔ یہ <xliff:g id="APP_NAME_1">%2$s</xliff:g> کے زیر انتظام ہے۔"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"مزید جانیں"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ایپ کو غیر موقوف کریں"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"ورک ایپس آن کریں؟"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"اپنی ورک ایپس اور اطلاعات تک رسائی حاصل کریں"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"آن کریں"</string> <string name="app_blocked_title" msgid="7353262160455028160">"ایپ دستیاب نہیں ہے"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ابھی دستیاب نہیں ہے۔"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 9ddb3a6f3160..72063f43cbde 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Đã sao chép văn bản vào bảng nhớ tạm thời."</string> <string name="copied" msgid="4675902854553014676">"Đã sao chép"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> đã dán dữ liệu từ <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> đã dán dữ liệu từ bảng nhớ tạm của bạn"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> đã dán văn bản mà bạn sao chép"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> đã dán một hình ảnh mà bạn sao chép"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> đã dán nội dung mà bạn sao chép"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Đang chuẩn bị <xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Khởi động ứng dụng."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Hoàn tất khởi động."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Tắt màn hình?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Trong khi thiết lập vân tay, bạn đã nhấn vào nút Nguồn.\n\nThông thường, thao tác này sẽ tắt màn hình."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Tắt"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Hủy"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> đang hoạt động"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Nhấn để quay lại trò chơi"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Chọn trò chơi"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sử dụng phím tắt"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Đảo màu"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Chỉnh màu"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Chế độ một tay"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Siêu tối"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã bật."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã tắt."</string> @@ -1875,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Do quản trị viên của bạn cập nhật"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Do quản trị viên của bạn xóa"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"Trình tiết kiệm pin sẽ bật Giao diện tối, đồng thời hạn chế hoặc tắt hoạt động chạy trong nền, một số hiệu ứng hình ảnh, các tính năng nhất định và một số kết nối mạng.\n\n"<annotation id="url">"Tìm hiểu thêm"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"Trình tiết kiệm pin sẽ bật Giao diện tối, đồng thời hạn chế hoặc tắt hoạt động chạy trong nền, một số hiệu ứng hình ảnh, các tính năng nhất định, và một số kết nối mạng."</string> <string name="data_saver_description" msgid="4995164271550590517">"Để giúp giảm mức sử dụng dữ liệu, Trình tiết kiệm dữ liệu sẽ chặn một số ứng dụng gửi hoặc nhận dữ liệu trong nền. Ứng dụng mà bạn hiện sử dụng có thể dùng dữ liệu nhưng tần suất sẽ giảm. Ví dụ: hình ảnh sẽ không hiển thị cho đến khi bạn nhấn vào hình ảnh đó."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Bật Trình tiết kiệm dữ liệu?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Bật"</string> @@ -1981,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> hiện không sử dụng được. Chính sách này do <xliff:g id="APP_NAME_1">%2$s</xliff:g> quản lý."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Tìm hiểu thêm"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Mở lại ứng dụng"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"Bật các ứng dụng công việc?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"Bạn sẽ có quyền truy cập vào các ứng dụng công việc và thông báo"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"Bật"</string> <string name="app_blocked_title" msgid="7353262160455028160">"Ứng dụng này không dùng được"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> hiện không dùng được."</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 50324d82c0c8..51735b942825 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"文本已复制到剪贴板。"</string> <string name="copied" msgid="4675902854553014676">"已复制"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>已粘贴从<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>复制的内容"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"已将剪贴板中的内容粘贴到<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string> <string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>已粘贴您复制的文字"</string> <string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>已粘贴您复制的图片"</string> <string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>已粘贴您复制的内容"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"正在准备升级<xliff:g id="APPNAME">%1$s</xliff:g>。"</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在启动应用。"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"即将完成启动。"</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"要关闭屏幕吗?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"您在设置指纹时按了电源按钮。\n\n此操作通常会关闭屏幕。"</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"关闭"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"取消"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g>正在运行"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"点按即可返回游戏"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"选择游戏"</string> @@ -1874,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"已由您的管理员更新"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"已由您的管理员删除"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"确定"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"在省电模式下,系统会启用深色主题,并限制或关闭后台活动、某些视觉效果、特定功能和部分网络连接。\n\n"<annotation id="url">"了解详情"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"在省电模式下,系统会启用深色主题,并限制或关闭后台活动、某些视觉效果、特定功能和部分网络连接。"</string> <string name="data_saver_description" msgid="4995164271550590517">"为了减少流量消耗,流量节省程序会阻止某些应用在后台收发数据。您当前使用的应用可以收发数据,但频率可能会降低。举例而言,这可能意味着图片只有在您点按之后才会显示。"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"要开启流量节省程序吗?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"开启"</string> @@ -1980,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>目前无法使用。该应用是由<xliff:g id="APP_NAME_1">%2$s</xliff:g>所管理。"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"了解详情"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"取消暂停应用"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"要开启工作应用访问权限吗?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"获取工作应用和通知的访问权限"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"开启"</string> <string name="app_blocked_title" msgid="7353262160455028160">"应用无法使用"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g>目前无法使用。"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 993b1bc4d1e7..4fb57cf3bd2c 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"文字已複製到剪貼簿。"</string> <string name="copied" msgid="4675902854553014676">"已複製"</string> <string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> 已貼上從 <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> 複製的資料"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"已將剪貼簿內容貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string> <string name="pasted_text" msgid="4298871641549173733">"您複製的文字已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string> <string name="pasted_image" msgid="4729097394781491022">"您複製的圖片已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string> <string name="pasted_content" msgid="646276353060777131">"您複製的內容已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"正在準備 <xliff:g id="APPNAME">%1$s</xliff:g>。"</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在啟動應用程式。"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"啟動完成。"</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"要關閉螢幕嗎?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"設定指紋時需要按下開關按鈕。\n\n此操作通常會關閉螢幕。"</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"關閉"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"取消"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"正在執行 <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"輕按即可返回遊戲"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"選擇遊戲"</string> @@ -1874,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"已由您的管理員更新"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"已由您的管理員刪除"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"好"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"「省電模式」會開啟深色主題背景,並限制或關閉背景活動、部分視覺效果、特定功能和部分網絡連線。\n\n"<annotation id="url">"瞭解詳情"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"「省電模式」會開啟深色主題背景,並限制或關閉背景活動、部分視覺效果、特定功能和部分網絡連線。"</string> <string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。您正在使用的應用程式可存取資料,但次數可能會減少。例如,圖片可能需要輕按才會顯示。"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟「數據節省模式」嗎?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string> @@ -1980,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"目前無法使用 <xliff:g id="APP_NAME_0">%1$s</xliff:g>。此應用程式是由「<xliff:g id="APP_NAME_1">%2$s</xliff:g>」管理。"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"瞭解詳情"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"取消暫停應用程式"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"要開啟工作應用程式存取權嗎?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"開啟工作應用程式和通知的存取權"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"開啟"</string> <string name="app_blocked_title" msgid="7353262160455028160">"無法使用應用程式"</string> <string name="app_blocked_message" msgid="542972921087873023">"目前無法使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index a45f981de5c4..51a8ef2f3761 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"文字已複製到剪貼簿。"</string> <string name="copied" msgid="4675902854553014676">"已複製"</string> <string name="pasted_from_app" msgid="5627698450808256545">"「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」已貼上從「<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>」複製的資料"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"已將剪貼簿內容貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string> <string name="pasted_text" msgid="4298871641549173733">"你複製的文字已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string> <string name="pasted_image" msgid="4729097394781491022">"你複製的圖片已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string> <string name="pasted_content" msgid="646276353060777131">"你複製的內容已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"正在準備升級「<xliff:g id="APPNAME">%1$s</xliff:g>」。"</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在啟動應用程式。"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"啟動完成。"</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"要關閉螢幕嗎?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"設定指紋時必須按下電源鍵。\n\n這項操作通常會將螢幕關閉。"</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"關閉"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"取消"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> 執行中"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"輕觸即可返回遊戲"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"選擇遊戲"</string> @@ -1874,10 +1869,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"已由你的管理員更新"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"已由你的管理員刪除"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"確定"</string> - <!-- no translation found for battery_saver_description_with_learn_more (4877297130366222145) --> - <skip /> - <!-- no translation found for battery_saver_description (8518809702138617167) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="4877297130366222145">"省電模式會開啟深色主題,並限制或關閉背景活動、某些視覺效果、特定功能和部分網路連線。\n\n"<annotation id="url">"瞭解詳情"</annotation></string> + <string name="battery_saver_description" msgid="8518809702138617167">"省電模式會開啟深色主題,並限制或關閉背景活動、某些視覺效果、特定功能和部分網路連線。"</string> <string name="data_saver_description" msgid="4995164271550590517">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。你目前使用的應用程式可以存取資料,但存取頻率可能不如平時高。舉例來說,圖片可能不會自動顯示,在你輕觸後才會顯示。"</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"要開啟數據節省模式嗎?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"開啟"</string> @@ -1980,10 +1973,8 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"目前無法使用「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」。這項設定是由「<xliff:g id="APP_NAME_1">%2$s</xliff:g>」管理。"</string> <string name="app_suspended_more_details" msgid="211260942831587014">"瞭解詳情"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"取消暫停應用程式"</string> - <!-- no translation found for work_mode_off_title (961171256005852058) --> - <skip /> - <!-- no translation found for work_mode_off_message (7319580997683623309) --> - <skip /> + <string name="work_mode_off_title" msgid="961171256005852058">"要開啟工作應用程式存取權嗎?"</string> + <string name="work_mode_off_message" msgid="7319580997683623309">"開啟工作應用程式和通知的存取權"</string> <string name="work_mode_turn_on" msgid="3662561662475962285">"開啟"</string> <string name="app_blocked_title" msgid="7353262160455028160">"應用程式無法使用"</string> <string name="app_blocked_message" msgid="542972921087873023">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」目前無法使用。"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index b983f1115c09..424551a2d4e4 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -1022,8 +1022,7 @@ <string name="text_copied" msgid="2531420577879738860">"Umbhalo ukopishwe ebhodini lokunamathisela."</string> <string name="copied" msgid="4675902854553014676">"Kukopishiwe"</string> <string name="pasted_from_app" msgid="5627698450808256545">"I-<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> inamathiselwe kusuka ku-<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string> - <!-- no translation found for pasted_from_clipboard (7355790625710831847) --> - <skip /> + <string name="pasted_from_clipboard" msgid="7355790625710831847">"I-<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> inamathiselwe ebhodini lakho lokunamathisela"</string> <string name="pasted_text" msgid="4298871641549173733">"U-<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> unamathisele umbhalo owukopishile"</string> <string name="pasted_image" msgid="4729097394781491022">"U-<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> unamathisele isithombe osikopishile"</string> <string name="pasted_content" msgid="646276353060777131">"U-<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> unamathisele okuqukethwe okukopishile"</string> @@ -1260,14 +1259,10 @@ <string name="android_preparing_apk" msgid="589736917792300956">"Ukulungisela i-<xliff:g id="APPNAME">%1$s</xliff:g>."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Qalisa izinhlelo zokusebenza."</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Qedela ukuqala kabusha."</string> - <!-- no translation found for fp_enrollment_powerbutton_intent_title (3385634173366119903) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_message (6582149052513682522) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_positive_button (5963520983910436791) --> - <skip /> - <!-- no translation found for fp_enrollment_powerbutton_intent_negative_button (6465764183480190748) --> - <skip /> + <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"Vala isikrini?"</string> + <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"Lapho usetha izigxivizo zeminwe yakho, ucindezele Inkinobho yamandla.\n\nLokhu kuvame ukuvala isikrini sakho."</string> + <string name="fp_enrollment_powerbutton_intent_positive_button" msgid="5963520983910436791">"Vala"</string> + <string name="fp_enrollment_powerbutton_intent_negative_button" msgid="6465764183480190748">"Khansela"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> iyasebenza"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Thepha ukuze ubuyele emuva kwigeyimu"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Khetha igeyimu"</string> @@ -1718,8 +1713,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sebenzisa isinqamuleli"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Ukuguqulwa kombala"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Ukulungiswa kombala"</string> - <!-- no translation found for one_handed_mode_feature_name (2334330034828094891) --> - <skip /> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Imodi yesandla esisodwa"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ukufiphaza okwengeziwe"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivuliwe."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivaliwe."</string> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index c3b35c81cb66..c4838b83347c 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -206,6 +206,12 @@ <!-- A tag used to store the margin end for this view when the right icon is gone --> <item type="id" name="tag_margin_end_when_icon_visible" /> + <!-- A tag used on the notification @id/left_icon to indicate that this view should be pupulated with the drawable from @id/right_icon when visible. --> + <item type="id" name="tag_uses_right_icon_drawable" /> + + <!-- A tag used on notification @id/right_icon to indicate that this view should remain visible even when the @id/left_icon is shown. --> + <item type="id" name="tag_keep_when_showing_left_icon" /> + <!-- Marks the "copy to clipboard" button in the ChooserActivity --> <item type="id" name="chooser_copy_button" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index f7a99309ec5a..641b2ad9bfc4 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3064,6 +3064,7 @@ <public name="hotwordDetectionService" /> <public name="previewLayout" /> <public name="clipToOutline" /> + <public name="__removed3" /> <public name="knownCerts" /> <public name="windowBackgroundBlurRadius"/> <public name="windowSplashScreenBackground"/> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index d8620a706fe2..d440173ef199 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3167,6 +3167,8 @@ <java-symbol type="dimen" name="notification_action_disabled_alpha" /> <java-symbol type="id" name="tag_margin_end_when_icon_visible" /> <java-symbol type="id" name="tag_margin_end_when_icon_gone" /> + <java-symbol type="id" name="tag_uses_right_icon_drawable" /> + <java-symbol type="id" name="tag_keep_when_showing_left_icon" /> <!-- Override Wake Key Behavior When Screen is Off --> <java-symbol type="bool" name="config_wakeOnDpadKeyPress" /> diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java deleted file mode 100644 index 677f4bd9e46d..000000000000 --- a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java +++ /dev/null @@ -1,186 +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 static org.testng.Assert.expectThrows; - -import android.util.ArrayMap; - -import org.junit.Test; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class SetSchemaRequestTest { - - private static Collection<String> getSchemaTypesFromSetSchemaRequest(SetSchemaRequest request) { - HashSet<String> schemaTypes = new HashSet<>(); - for (AppSearchSchema schema : request.getSchemas()) { - schemaTypes.add(schema.getSchemaType()); - } - return schemaTypes; - } - - @Test - public void testInvalidSchemaReferences_fromDisplayedBySystem() { - IllegalArgumentException expected = - expectThrows( - IllegalArgumentException.class, - () -> - new SetSchemaRequest.Builder() - .setSchemaTypeDisplayedBySystem("InvalidSchema", false) - .build()); - assertThat(expected).hasMessageThat().contains("referenced, but were not added"); - } - - @Test - public void testInvalidSchemaReferences_fromPackageVisibility() { - IllegalArgumentException expected = - expectThrows( - IllegalArgumentException.class, - () -> - new SetSchemaRequest.Builder() - .setSchemaTypeVisibilityForPackage( - "InvalidSchema", - /*visible=*/ true, - new PackageIdentifier( - "com.foo.package", - /*sha256Certificate=*/ new byte[] {})) - .build()); - assertThat(expected).hasMessageThat().contains("referenced, but were not added"); - } - - @Test - public void testSetSchemaTypeDisplayedBySystem_displayed() { - AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build(); - - // By default, the schema is displayed. - SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build(); - assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty(); - - request = - new SetSchemaRequest.Builder() - .addSchemas(schema) - .setSchemaTypeDisplayedBySystem("Schema", true) - .build(); - assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty(); - } - - @Test - public void testSetSchemaTypeDisplayedBySystem_notDisplayed() { - AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build(); - SetSchemaRequest request = - new SetSchemaRequest.Builder() - .addSchemas(schema) - .setSchemaTypeDisplayedBySystem("Schema", false) - .build(); - assertThat(request.getSchemasNotDisplayedBySystem()).containsExactly("Schema"); - } - - @Test - public void testSchemaTypeVisibilityForPackage_visible() { - AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build(); - - // By default, the schema is not visible. - SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build(); - assertThat(request.getSchemasVisibleToPackages()).isEmpty(); - - PackageIdentifier packageIdentifier = - new PackageIdentifier("com.package.foo", new byte[] {100}); - Map<String, Set<PackageIdentifier>> expectedVisibleToPackagesMap = new ArrayMap<>(); - expectedVisibleToPackagesMap.put("Schema", Collections.singleton(packageIdentifier)); - - request = - new SetSchemaRequest.Builder() - .addSchemas(schema) - .setSchemaTypeVisibilityForPackage( - "Schema", /*visible=*/ true, packageIdentifier) - .build(); - assertThat(request.getSchemasVisibleToPackages()) - .containsExactlyEntriesIn(expectedVisibleToPackagesMap); - } - - @Test - public void testSchemaTypeVisibilityForPackage_notVisible() { - AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build(); - - SetSchemaRequest request = - new SetSchemaRequest.Builder() - .addSchemas(schema) - .setSchemaTypeVisibilityForPackage( - "Schema", - /*visible=*/ false, - new PackageIdentifier( - "com.package.foo", /*sha256Certificate=*/ new byte[] {})) - .build(); - assertThat(request.getSchemasVisibleToPackages()).isEmpty(); - } - - @Test - public void testSchemaTypeVisibilityForPackage_deduped() throws Exception { - AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build(); - - PackageIdentifier packageIdentifier = - new PackageIdentifier("com.package.foo", new byte[] {100}); - Map<String, Set<PackageIdentifier>> expectedVisibleToPackagesMap = new ArrayMap<>(); - expectedVisibleToPackagesMap.put("Schema", Collections.singleton(packageIdentifier)); - - SetSchemaRequest request = - new SetSchemaRequest.Builder() - .addSchemas(schema) - // Set it visible for "Schema" - .setSchemaTypeVisibilityForPackage( - "Schema", /*visible=*/ true, packageIdentifier) - // Set it visible for "Schema" again, which should be a no-op - .setSchemaTypeVisibilityForPackage( - "Schema", /*visible=*/ true, packageIdentifier) - .build(); - assertThat(request.getSchemasVisibleToPackages()) - .containsExactlyEntriesIn(expectedVisibleToPackagesMap); - } - - @Test - public void testSchemaTypeVisibilityForPackage_removed() throws Exception { - AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build(); - - SetSchemaRequest request = - new SetSchemaRequest.Builder() - .addSchemas(schema) - // First set it as visible - .setSchemaTypeVisibilityForPackage( - "Schema", - /*visible=*/ true, - new PackageIdentifier( - "com.package.foo", /*sha256Certificate=*/ new byte[] {100})) - // Then make it not visible - .setSchemaTypeVisibilityForPackage( - "Schema", - /*visible=*/ false, - new PackageIdentifier( - "com.package.foo", /*sha256Certificate=*/ new byte[] {100})) - .build(); - - // Nothing should be visible. - assertThat(request.getSchemasVisibleToPackages()).isEmpty(); - } -} diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 281ce2bd3ab6..df0c64c810c6 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -61,6 +61,7 @@ import android.view.DisplayCutout; import android.view.Surface; import android.view.autofill.AutofillId; import android.view.translation.TranslationSpec; +import android.view.translation.UiTranslationSpec; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -693,8 +694,8 @@ public class TransactionParcelTests { @Override public void updateUiTranslationState(IBinder activityToken, int state, - TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds) { - + TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds, + UiTranslationSpec uiTranslationSpec) { } } } diff --git a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java index 4a5528d2ca36..79f7a5c9df18 100644 --- a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java @@ -40,6 +40,7 @@ public class AmbientDisplayPowerCalculatorTest { @Test public void testMeasuredEnergyBasedModel() { + mStatsRule.initMeasuredEnergyStatsLocked(); BatteryStatsImpl stats = mStatsRule.getBatteryStats(); stats.updateDisplayMeasuredEnergyStatsLocked(300_000_000, Display.STATE_ON, 0); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java index f168b3c160cb..464412f17722 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java @@ -506,6 +506,7 @@ public class BatteryStatsNoteTest extends TestCase { public void testUpdateDisplayMeasuredEnergyStatsLocked() { final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + bi.initMeasuredEnergyStats(); clocks.realtime = 0; int screen = Display.STATE_OFF; @@ -590,6 +591,7 @@ public class BatteryStatsNoteTest extends TestCase { public void testUpdateCustomMeasuredEnergyStatsLocked_neverCalled() { final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + bi.initMeasuredEnergyStats(); bi.setOnBatteryInternal(true); final int uid1 = 11500; @@ -603,6 +605,7 @@ public class BatteryStatsNoteTest extends TestCase { public void testUpdateCustomMeasuredEnergyStatsLocked() { final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + bi.initMeasuredEnergyStats(); final int bucketA = 0; // Custom bucket 0 final int bucketB = 1; // Custom bucket 1 diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java index d36f06ab683a..7f0449b75c18 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java @@ -45,7 +45,7 @@ public class BatteryUsageStatsProviderTest { private static final long MINUTE_IN_MS = 60 * 1000; @Rule - public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345); @Test public void test_getBatteryUsageStats() { @@ -62,6 +62,8 @@ public class BatteryUsageStatsProviderTest { batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_CACHED_EMPTY, 40 * MINUTE_IN_MS, 40 * MINUTE_IN_MS); + mStatsRule.setCurrentTime(54321); + Context context = InstrumentationRegistry.getContext(); BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats); @@ -75,6 +77,9 @@ public class BatteryUsageStatsProviderTest { .isEqualTo(20 * MINUTE_IN_MS); assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND)) .isEqualTo(10 * MINUTE_IN_MS); + + assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(12345); + assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(54321); } @Test diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java index 1a6408ff7eb3..961e85950b29 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java @@ -51,19 +51,20 @@ public class BatteryUsageStatsRule implements TestRule { private final PowerProfile mPowerProfile; private final MockClocks mMockClocks = new MockClocks(); - private final MockBatteryStatsImpl mBatteryStats = new MockBatteryStatsImpl(mMockClocks) { - @Override - public boolean hasBluetoothActivityReporting() { - return true; - } - }; + private final MockBatteryStatsImpl mBatteryStats; private BatteryUsageStats mBatteryUsageStats; private boolean mScreenOn; public BatteryUsageStatsRule() { + this(0); + } + + public BatteryUsageStatsRule(long currentTime) { Context context = InstrumentationRegistry.getContext(); mPowerProfile = spy(new PowerProfile(context, true /* forTest */)); + mMockClocks.currentTime = currentTime; + mBatteryStats = new MockBatteryStatsImpl(mMockClocks); mBatteryStats.setPowerProfile(mPowerProfile); } @@ -110,10 +111,17 @@ public class BatteryUsageStatsRule implements TestRule { /** Call only after setting the power profile information. */ public BatteryUsageStatsRule initMeasuredEnergyStatsLocked() { + return initMeasuredEnergyStatsLocked(new String[0]); + } + + /** Call only after setting the power profile information. */ + public BatteryUsageStatsRule initMeasuredEnergyStatsLocked( + String[] customPowerComponentNames) { final boolean[] supportedStandardBuckets = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS]; Arrays.fill(supportedStandardBuckets, true); - mBatteryStats.initMeasuredEnergyStatsLocked(supportedStandardBuckets, new String[0]); + mBatteryStats.initMeasuredEnergyStatsLocked(supportedStandardBuckets, + customPowerComponentNames); mBatteryStats.informThatAllExternalStatsAreFlushed(); return this; } @@ -161,6 +169,10 @@ public class BatteryUsageStatsRule implements TestRule { mMockClocks.uptime = uptimeMs; } + public void setCurrentTime(long currentTimeMs) { + mMockClocks.currentTime = currentTimeMs; + } + BatteryUsageStats apply(PowerCalculator... calculators) { return apply(new BatteryUsageStatsQuery.Builder().includePowerModels().build(), calculators); 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 c8564acfc656..380b4ae7e748 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java @@ -44,13 +44,13 @@ public class BatteryUsageStatsTest { @Test public void testBuilder() { - BatteryUsageStats batteryUsageStats = buildBatteryUsageStats(); + BatteryUsageStats batteryUsageStats = buildBatteryUsageStats().build(); validateBatteryUsageStats(batteryUsageStats); } @Test public void testParcelability() { - final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats(); + final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats().build(); final Parcel outParcel = Parcel.obtain(); outParcel.writeParcelable(outBatteryUsageStats, 0); final byte[] bytes = outParcel.marshall(); @@ -65,9 +65,17 @@ public class BatteryUsageStatsTest { validateBatteryUsageStats(inBatteryUsageStats); } + + @Test + public void testDefaultSessionDuration() { + final BatteryUsageStats stats = + buildBatteryUsageStats().setStatsDuration(10000).build(); + assertThat(stats.getStatsDuration()).isEqualTo(10000); + } + @Test public void testDump() { - final BatteryUsageStats stats = buildBatteryUsageStats(); + final BatteryUsageStats stats = buildBatteryUsageStats().build(); final StringWriter out = new StringWriter(); try (PrintWriter pw = new PrintWriter(out)) { stats.dump(pw, " "); @@ -93,7 +101,7 @@ public class BatteryUsageStatsTest { assertThat(allNames).hasSize(BatteryConsumer.POWER_COMPONENT_COUNT); } - private BatteryUsageStats buildBatteryUsageStats() { + private BatteryUsageStats.Builder buildBatteryUsageStats() { final MockClocks clocks = new MockClocks(); final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks); final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000); @@ -103,7 +111,8 @@ public class BatteryUsageStatsTest { .setBatteryCapacity(4000) .setDischargePercentage(20) .setDischargedPowerRange(1000, 2000) - .setStatsStartTimestamp(1000); + .setStatsStartTimestamp(1000) + .setStatsEndTimestamp(3000); builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid) .setPackageWithHighestDrain("foo") .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000) @@ -152,7 +161,7 @@ public class BatteryUsageStatsTest { .setUsageDurationForCustomComponentMillis( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 40); - return builder.build(); + return builder; } public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) { @@ -162,6 +171,8 @@ public class BatteryUsageStatsTest { assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(1000); assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(2000); assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(1000); + assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(3000); + assertThat(batteryUsageStats.getStatsDuration()).isEqualTo(2000); final List<UidBatteryConsumer> uidBatteryConsumers = batteryUsageStats.getUidBatteryConsumers(); diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java index 2de621c8fa6f..5c8479406c03 100644 --- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.UidTraffic; import android.os.BatteryConsumer; +import android.os.BatteryStats; import android.os.BatteryUsageStatsQuery; import android.os.Process; @@ -43,111 +44,89 @@ public class BluetoothPowerCalculatorTest { .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE, 10.0) .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX, 50.0) .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX, 100.0) - .initMeasuredEnergyStatsLocked(); + .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE, 3700); @Test public void testTimerBasedModel() { - setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID) - .getOrCreateBluetoothControllerActivityLocked(), - 1000, 2000, 3000, 0); + setupBluetoothEnergyInfo(0, BatteryStats.POWER_DATA_UNAVAILABLE); - setDurationsAndPower(mStatsRule.getUidStats(APP_UID) - .getOrCreateBluetoothControllerActivityLocked(), - 4000, 5000, 6000, 0); + BluetoothPowerCalculator calculator = + new BluetoothPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator); + + assertCalculatedPower(0.08216, 0.18169, 0.26388, 0.26386, + BatteryConsumer.POWER_MODEL_POWER_PROFILE); + } - setDurationsAndPower((BatteryStatsImpl.ControllerActivityCounterImpl) - mStatsRule.getBatteryStats().getBluetoothControllerActivity(), - 6000, 8000, 10000, 0); + @Test + public void testReportedEnergyBasedModel() { + setupBluetoothEnergyInfo(4000000, BatteryStats.POWER_DATA_UNAVAILABLE); BluetoothPowerCalculator calculator = new BluetoothPowerCalculator(mStatsRule.getPowerProfile()); - mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator); + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().includePowerModels().build(), + calculator); - assertBluetoothPowerAndDuration( - mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), - 0.11388, 6000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); - assertBluetoothPowerAndDuration( - mStatsRule.getUidBatteryConsumer(APP_UID), - 0.24722, 15000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); - assertBluetoothPowerAndDuration( - mStatsRule.getDeviceBatteryConsumer(), - 0.40555, 24000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); - assertBluetoothPowerAndDuration( - mStatsRule.getAppsBatteryConsumer(), - 0.36111, 21000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + assertCalculatedPower(0.08216, 0.18169, 0.30030, 0.26386, + BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test - public void testReportedPowerBasedModel() { - setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID) - .getOrCreateBluetoothControllerActivityLocked(), - 1000, 2000, 3000, 360000); + public void testMeasuredEnergyBasedModel() { + mStatsRule.initMeasuredEnergyStatsLocked(); + setupBluetoothEnergyInfo(0, 1200000); - setDurationsAndPower(mStatsRule.getUidStats(APP_UID) - .getOrCreateBluetoothControllerActivityLocked(), - 4000, 5000, 6000, 720000); + final BluetoothPowerCalculator calculator = + new BluetoothPowerCalculator(mStatsRule.getPowerProfile()); - setDurationsAndPower((BatteryStatsImpl.ControllerActivityCounterImpl) - mStatsRule.getBatteryStats().getBluetoothControllerActivity(), - 6000, 8000, 10000, 1260000); + mStatsRule.apply(calculator); + + assertCalculatedPower(0.10378, 0.22950, 0.33333, 0.33329, + BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); + } + + @Test + public void testIgnoreMeasuredEnergyBasedModel() { + mStatsRule.initMeasuredEnergyStatsLocked(); + setupBluetoothEnergyInfo(4000000, 1200000); BluetoothPowerCalculator calculator = new BluetoothPowerCalculator(mStatsRule.getPowerProfile()); mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator); - assertBluetoothPowerAndDuration( - mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), - 0.1, 6000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); - assertBluetoothPowerAndDuration( - mStatsRule.getUidBatteryConsumer(APP_UID), - 0.2, 15000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); - assertBluetoothPowerAndDuration( - mStatsRule.getDeviceBatteryConsumer(), - 0.35, 24000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); - assertBluetoothPowerAndDuration( - mStatsRule.getAppsBatteryConsumer(), - 0.3, 21000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + assertCalculatedPower(0.08216, 0.18169, 0.26388, 0.26386, + BatteryConsumer.POWER_MODEL_POWER_PROFILE); } - @Test - public void testMeasuredEnergyBasedModel() { + private void setupBluetoothEnergyInfo(long reportedEnergyUc, long consumedEnergyUc) { final BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000, - BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 7000, 5000, 0, 100000); + BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 7000, 5000, 0, + reportedEnergyUc); info.setUidTraffic(new UidTraffic[]{ new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000), new UidTraffic(APP_UID, 3000, 4000) }); - mStatsRule.getBatteryStats().updateBluetoothStateLocked(info, 1200000, 1000, 1000); - - final BluetoothPowerCalculator calculator = - new BluetoothPowerCalculator(mStatsRule.getPowerProfile()); - - mStatsRule.apply(new BatteryUsageStatsQuery.Builder().includePowerModels().build(), - calculator); + mStatsRule.getBatteryStats().updateBluetoothStateLocked(info, + consumedEnergyUc, 1000, 1000); + } + private void assertCalculatedPower(double bluetoothUidPowerMah, double appPowerMah, + double devicePowerMah, double allAppsPowerMah, int powerModelPowerProfile) { assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), - 0.10378, 3583, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); + bluetoothUidPowerMah, 3583, powerModelPowerProfile); assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(APP_UID), - 0.22950, 8416, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); + appPowerMah, 8416, powerModelPowerProfile); assertBluetoothPowerAndDuration( mStatsRule.getDeviceBatteryConsumer(), - 0.33333, 12000, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); + devicePowerMah, 12000, powerModelPowerProfile); assertBluetoothPowerAndDuration( mStatsRule.getAppsBatteryConsumer(), - 0.33329, 11999, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); - } - - private void setDurationsAndPower( - BatteryStatsImpl.ControllerActivityCounterImpl controllerActivity, int idleDurationMs, - int rxDurationMs, int txDurationMs, long powerMaMs) { - controllerActivity.getIdleTimeCounter().addCountLocked(idleDurationMs); - controllerActivity.getRxTimeCounter().addCountLocked(rxDurationMs); - controllerActivity.getTxTimeCounters()[0].addCountLocked(txDurationMs); - controllerActivity.getPowerCounter().addCountLocked(powerMaMs); + allAppsPowerMah, 11999, powerModelPowerProfile); } private void assertBluetoothPowerAndDuration(@Nullable BatteryConsumer batteryConsumer, @@ -162,7 +141,6 @@ public class BluetoothPowerCalculatorTest { long usageDurationMillis = batteryConsumer.getUsageDurationMillis( BatteryConsumer.POWER_COMPONENT_BLUETOOTH); - assertThat(usageDurationMillis).isEqualTo(durationMs); } } diff --git a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java index f8c2bc6c3d80..5334f07d28f5 100644 --- a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java @@ -38,7 +38,8 @@ public class CustomMeasuredPowerCalculatorTest { private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; @Rule - public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .initMeasuredEnergyStatsLocked(new String[]{"CUSTOM_COMPONENT1", "CUSTOM_COMPONENT2"}); @Test public void testMeasuredEnergyCopiedIntoBatteryConsumers() { diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index 7a7d9f541328..80def71ce812 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -48,13 +48,6 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { setExternalStatsSyncLocked(new DummyExternalStatsSync()); informThatAllExternalStatsAreFlushed(); - final boolean[] supportedStandardBuckets = - new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS]; - Arrays.fill(supportedStandardBuckets, true); - final String[] customBucketNames = {"FOO", "BAR"}; - mGlobalMeasuredEnergyStats = - new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); - // A no-op handler. mHandler = new Handler(Looper.getMainLooper()) { }; @@ -64,6 +57,15 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { this(new MockClocks()); } + public void initMeasuredEnergyStats() { + final boolean[] supportedStandardBuckets = + new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS]; + Arrays.fill(supportedStandardBuckets, true); + final String[] customBucketNames = {"FOO", "BAR"}; + mGlobalMeasuredEnergyStats = + new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); + } + public TimeBase getOnBatteryTimeBase() { return mOnBatteryTimeBase; } diff --git a/core/tests/coretests/src/com/android/internal/os/MockClocks.java b/core/tests/coretests/src/com/android/internal/os/MockClocks.java index d0fa756c9193..c26505e57346 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockClocks.java +++ b/core/tests/coretests/src/com/android/internal/os/MockClocks.java @@ -21,6 +21,8 @@ public class MockClocks implements BatteryStatsImpl.Clocks { public long realtime; /** Uptime in ms */ public long uptime; + /** Current time in ms */ + public long currentTime; @Override public long elapsedRealtime() { @@ -31,4 +33,9 @@ public class MockClocks implements BatteryStatsImpl.Clocks { public long uptimeMillis() { return uptime; } + + @Override + public long currentTimeMillis() { + return currentTime; + } } diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java index 4c29c204618a..c695fc9eb87d 100644 --- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java @@ -47,6 +47,7 @@ public class ScreenPowerCalculatorTest { @Test public void testMeasuredEnergyBasedModel() { + mStatsRule.initMeasuredEnergyStatsLocked(); BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0); diff --git a/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java index 82830f23fec3..a7f4fb303b3d 100644 --- a/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java @@ -79,8 +79,8 @@ public class WakelockPowerCalculatorTest { assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK)) .isWithin(PRECISION).of(0.6); - BatteryConsumer appConsumer = mStatsRule.getDeviceBatteryConsumer(); + BatteryConsumer appConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK)) - .isWithin(PRECISION).of(0.6); + .isWithin(PRECISION).of(0.1); } } diff --git a/data/etc/car/com.android.car.bugreport.xml b/data/etc/car/com.android.car.bugreport.xml index 2ff98357f8bf..ec9128e8acbc 100644 --- a/data/etc/car/com.android.car.bugreport.xml +++ b/data/etc/car/com.android.car.bugreport.xml @@ -22,5 +22,6 @@ <permission name="android.permission.MANAGE_USERS"/> <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/> <permission name="android.car.permission.CAR_DRIVING_STATE"/> + <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/> </privapp-permissions> </permissions> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 9b9511b15082..3c9086dde021 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -211,12 +211,6 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/TaskOrganizerController.java" }, - "-1890326172": { - "message": "no-history finish of %s on new resume", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-1884933373": { "message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s", "level": "INFO", @@ -1429,6 +1423,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-484194149": { + "message": "no-history finish of %s on new resume", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" + }, "-481924678": { "message": "handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w.isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d", "level": "DEBUG", diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java index d00492cedb38..6c0981afa2c9 100644 --- a/graphics/java/android/graphics/drawable/RippleShader.java +++ b/graphics/java/android/graphics/drawable/RippleShader.java @@ -43,10 +43,10 @@ final class RippleShader extends RuntimeShader { + "uniform shader in_shader;\n"; private static final String SHADER_LIB = "float triangleNoise(vec2 n) {\n" - + " n = fract(n * vec2(5.3987, 5.4421));\n" - + " n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));\n" - + " float xy = n.x * n.y;\n" - + " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n" + + " n = fract(n * vec2(5.3987, 5.4421));\n" + + " n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));\n" + + " float xy = n.x * n.y;\n" + + " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n" + "}" + "const float PI = 3.1415926535897932384626;\n" + "\n" @@ -110,14 +110,16 @@ final class RippleShader extends RuntimeShader { + " vec2 uv = p * in_resolutionScale;\n" + " vec2 densityUv = uv - mod(uv, in_noiseScale);\n" + " float turbulence = turbulence(uv, in_turbulencePhase);\n" - + " float sparkle = sparkles(densityUv, in_noisePhase) * ring * alpha " + + " float sparkleAlpha = sparkles(densityUv, in_noisePhase) * ring * alpha " + "* turbulence;\n" + " float fade = min(fadeIn, 1. - fadeOutRipple);\n" - + " float circleAlpha = softCircle(p, center, in_maxRadius * scaleIn, 0.2) * fade;\n" - + " vec3 color = mix(in_color.rgb, in_sparkleColor.rgb, sparkle);\n" + + " float waveAlpha = softCircle(p, center, in_maxRadius * scaleIn, 0.2) * fade " + + "* in_color.a;\n" + + " vec4 waveColor = vec4(in_color.rgb * waveAlpha, waveAlpha);\n" + + " vec4 sparkleColor = vec4(in_sparkleColor.rgb * in_sparkleColor.a, " + + "in_sparkleColor.a);\n" + " float mask = in_hasMask == 1. ? sample(in_shader, p).a > 0. ? 1. : 0. : 1.;\n" - + " float a = (in_color.a * circleAlpha + in_sparkleColor.a * sparkle) * mask;\n" - + " return vec4(color * a, a);\n" + + " return mix(waveColor, sparkleColor, sparkleAlpha) * mask;\n" + "}"; private static final String SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN; private static final double PI_ROTATE_RIGHT = Math.PI * 0.0078125; diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java index 9c01a4be381f..df47f73eb04a 100644 --- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java +++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java @@ -134,7 +134,11 @@ public class FontCustomizationParser { throw new IllegalArgumentException("customizationType must be specified"); } if (customizationType.equals("new-named-family")) { - out.add(FontListParser.readFamily(parser, fontDir, updatableFontMap, false)); + FontFamily fontFamily = FontListParser.readFamily( + parser, fontDir, updatableFontMap, false); + if (fontFamily != null) { + out.add(fontFamily); + } } else { throw new IllegalArgumentException("Unknown customizationType=" + customizationType); } diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 255f9e659c36..1d392d2c27b5 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -262,8 +262,14 @@ public final class SystemFonts { */ @VisibleForTesting public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig) { + return buildSystemFallback(fontConfig, new ArrayMap<>()); + } + + /** @hide */ + @VisibleForTesting + public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig, + ArrayMap<String, ByteBuffer> outBufferCache) { final Map<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>(); final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies(); final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>(); @@ -273,7 +279,7 @@ public final class SystemFonts { if (familyName == null) { continue; } - appendNamedFamily(xmlFamily, bufferCache, fallbackListMap); + appendNamedFamily(xmlFamily, outBufferCache, fallbackListMap); } // Then, add fallback fonts to the each fallback map. @@ -282,7 +288,7 @@ public final class SystemFonts { // The first family (usually the sans-serif family) is always placed immediately // after the primary family in the fallback. if (i == 0 || xmlFamily.getName() == null) { - pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache); + pushFamilyToFallback(xmlFamily, fallbackListMap, outBufferCache); } } diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java index df579bba9dc2..1034847b761b 100644 --- a/keystore/java/android/security/KeyStore2.java +++ b/keystore/java/android/security/KeyStore2.java @@ -19,6 +19,7 @@ package android.security; import android.annotation.NonNull; import android.compat.annotation.ChangeId; import android.compat.annotation.Disabled; +import android.os.Binder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; @@ -140,6 +141,7 @@ public class KeyStore2 { if (mBinder == null || retryLookup) { mBinder = IKeystoreService.Stub.asInterface(ServiceManager .getService(KEYSTORE2_SERVICE_NAME)); + Binder.allowBlocking(mBinder.asBinder()); } return mBinder; } diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java index a6552dddc630..e6c1ea827118 100644 --- a/keystore/java/android/security/KeyStoreOperation.java +++ b/keystore/java/android/security/KeyStoreOperation.java @@ -18,6 +18,7 @@ package android.security; import android.annotation.NonNull; import android.hardware.security.keymint.KeyParameter; +import android.os.Binder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.security.keymaster.KeymasterDefs; @@ -39,6 +40,7 @@ public class KeyStoreOperation { Long challenge, KeyParameter[] parameters ) { + Binder.allowBlocking(operation.asBinder()); this.mOperation = operation; this.mChallenge = challenge; this.mParameters = parameters; diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java index d188b6525579..b85dd742cc49 100644 --- a/keystore/java/android/security/KeyStoreSecurityLevel.java +++ b/keystore/java/android/security/KeyStoreSecurityLevel.java @@ -19,6 +19,7 @@ package android.security; import android.annotation.NonNull; import android.app.compat.CompatChanges; import android.hardware.security.keymint.KeyParameter; +import android.os.Binder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.security.keystore.BackendBusyException; @@ -45,6 +46,7 @@ public class KeyStoreSecurityLevel { private final IKeystoreSecurityLevel mSecurityLevel; public KeyStoreSecurityLevel(IKeystoreSecurityLevel securityLevel) { + Binder.allowBlocking(securityLevel.asBinder()); this.mSecurityLevel = securityLevel; } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java index fe05989c3846..97592b44ba2e 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java @@ -252,7 +252,9 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { blockModes, userAuthenticationRequired, (int) userAuthenticationValidityDurationSeconds, - keymasterHwEnforcedUserAuthenticators, + userAuthenticationRequirementEnforcedBySecureHardware + ? keymasterHwEnforcedUserAuthenticators + : keymasterSwEnforcedUserAuthenticators, userAuthenticationRequirementEnforcedBySecureHardware, userAuthenticationValidWhileOnBody, trustedUserPresenceRequired, diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 3e2fb94f0387..f3cfcf18dec1 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -41,6 +41,8 @@ import android.system.keystore2.KeyMetadata; import android.system.keystore2.ResponseCode; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -974,7 +976,6 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { } private Set<String> getUniqueAliases() { - try { final KeyDescriptor[] keys = mKeyStore.list( getTargetDomain(), @@ -987,7 +988,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { return aliases; } catch (android.security.KeyStoreException e) { Log.e(TAG, "Failed to list keystore entries.", e); - return null; + return new HashSet<>(); } } @@ -1099,6 +1100,17 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { return caAlias; } + /** + * Used by Tests to initialize with a fake KeyStore2. + * @hide + * @param keystore + */ + @VisibleForTesting + public void initForTesting(KeyStore2 keystore) { + mKeyStore = keystore; + mNamespace = KeyProperties.NAMESPACE_APPLICATION; + } + @Override public void engineStore(OutputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { diff --git a/keystore/tests/Android.bp b/keystore/tests/Android.bp index 2315a8568c64..7de45233494b 100644 --- a/keystore/tests/Android.bp +++ b/keystore/tests/Android.bp @@ -28,6 +28,7 @@ android_test { static_libs: [ "androidx.test.rules", "hamcrest-library", + "mockito-target-minus-junit4", ], platform_apis: true, libs: ["android.test.runner"], diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java index b7d72fce6eba..2ae61ab3b38d 100644 --- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java +++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java @@ -43,7 +43,6 @@ public final class ParcelableKeyGenParameterSpecTest { static final String ALIAS = "keystore-alias"; static final String ANOTHER_ALIAS = "another-keystore-alias"; static final int KEY_PURPOSES = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY; - static final int UID = 1230; static final int KEYSIZE = 2048; static final X500Principal SUBJECT = new X500Principal("CN=subject"); static final BigInteger SERIAL = new BigInteger("1234567890"); @@ -61,7 +60,7 @@ public final class ParcelableKeyGenParameterSpecTest { public static KeyGenParameterSpec configureDefaultSpec() { return new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES) - .setUid(UID) + .setNamespace(KeyProperties.NAMESPACE_WIFI) .setKeySize(KEYSIZE) .setCertificateSubject(SUBJECT) .setCertificateSerialNumber(SERIAL) @@ -88,10 +87,11 @@ public final class ParcelableKeyGenParameterSpecTest { .build(); } - public static void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) { + public static void validateSpecValues(KeyGenParameterSpec spec, + @KeyProperties.Namespace int namespace, String alias) { assertThat(spec.getKeystoreAlias(), is(alias)); assertThat(spec.getPurposes(), is(KEY_PURPOSES)); - assertThat(spec.getUid(), is(uid)); + assertThat(spec.getNamespace(), is(namespace)); assertThat(spec.getKeySize(), is(KEYSIZE)); assertThat(spec.getCertificateSubject(), is(SUBJECT)); assertThat(spec.getCertificateSerialNumber(), is(SERIAL)); @@ -134,7 +134,7 @@ public final class ParcelableKeyGenParameterSpecTest { Parcel parcel = parcelForReading(spec); ParcelableKeyGenParameterSpec fromParcel = ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel); - validateSpecValues(fromParcel.getSpec(), UID, ALIAS); + validateSpecValues(fromParcel.getSpec(), KeyProperties.NAMESPACE_WIFI, ALIAS); assertThat(parcel.dataAvail(), is(0)); } diff --git a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java index b2edfd05d13f..ddbb1d8c097c 100644 --- a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java +++ b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java @@ -21,8 +21,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import android.security.ParcelableKeyGenParameterSpecTest; -import android.security.keystore.KeyGenParameterSpec; -import android.security.keystore.KeyProperties; import androidx.test.runner.AndroidJUnit4; @@ -41,7 +39,7 @@ public final class KeyGenParameterSpecTest { KeyGenParameterSpec copiedSpec = new KeyGenParameterSpec.Builder(spec).build(); ParcelableKeyGenParameterSpecTest.validateSpecValues( - copiedSpec, spec.getUid(), spec.getKeystoreAlias()); + copiedSpec, spec.getNamespace(), spec.getKeystoreAlias()); } @Test diff --git a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.java new file mode 100644 index 000000000000..1bd3069f483a --- /dev/null +++ b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreSpiTest.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 android.security.keystore2; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.security.KeyStore2; +import android.security.KeyStoreException; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class AndroidKeyStoreSpiTest { + + @Mock + private KeyStore2 mKeystore2; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testEngineAliasesReturnsEmptySetOnKeyStoreError() throws Exception { + when(mKeystore2.list(anyInt(), anyLong())) + .thenThrow(new KeyStoreException(6, "Some Error")); + AndroidKeyStoreSpi spi = new AndroidKeyStoreSpi(); + spi.initForTesting(mKeystore2); + + assertThat("Empty collection expected", !spi.engineAliases().hasMoreElements()); + + verify(mKeystore2).list(anyInt(), anyLong()); + } + +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index ca05ff47d507..17e0d1bc9018 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -208,6 +208,24 @@ public class PipAnimationController { } /** + * A handler class that could register itself to apply the transaction instead of the + * animation controller doing it. For example, the menu controller can be one such handler. + */ + public static class PipTransactionHandler { + + /** + * Called when the animation controller is about to apply a transaction. Allow a registered + * handler to apply the transaction instead. + * + * @return true if handled by the handler, false otherwise. + */ + public boolean handlePipTransaction(SurfaceControl leash, SurfaceControl.Transaction tx, + Rect destinationBounds) { + return false; + } + } + + /** * Animator for PiP transition animation which supports both alpha and bounds animation. * @param <T> Type of property to animate, either alpha (float) or bounds (Rect) */ @@ -225,6 +243,7 @@ public class PipAnimationController { private T mEndValue; private float mStartingAngle; private PipAnimationCallback mPipAnimationCallback; + private PipTransactionHandler mPipTransactionHandler; private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; private PipSurfaceTransactionHelper mSurfaceTransactionHelper; @@ -293,6 +312,20 @@ public class PipAnimationController { mPipAnimationCallback = callback; return this; } + + PipTransitionAnimator<T> setPipTransactionHandler(PipTransactionHandler handler) { + mPipTransactionHandler = handler; + return this; + } + + boolean handlePipTransaction(SurfaceControl leash, SurfaceControl.Transaction tx, + Rect destinationBounds) { + if (mPipTransactionHandler != null) { + return mPipTransactionHandler.handlePipTransaction(leash, tx, destinationBounds); + } + return false; + } + @VisibleForTesting @TransitionDirection public int getTransitionDirection() { return mTransitionDirection; @@ -499,7 +532,9 @@ public class PipAnimationController { getSurfaceTransactionHelper().scaleAndCrop(tx, leash, initialSourceValue, bounds, insets); } - tx.apply(); + if (!handlePipTransaction(leash, tx, bounds)) { + tx.apply(); + } } private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java index 319d57a63320..48a15d8686b4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java @@ -20,7 +20,6 @@ import android.content.Context; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; -import android.os.SystemProperties; import android.view.SurfaceControl; import com.android.wm.shell.R; @@ -44,10 +43,7 @@ public class PipSurfaceTransactionHelper { * @param context the current context */ public void onDensityOrFontScaleChanged(Context context) { - final boolean enableCornerRadius = - SystemProperties.getBoolean("debug.sf.enable_hole_punch_pip", false); - mCornerRadius = enableCornerRadius - ? context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius) : 0; + mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius); } /** 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 4ce6c9e35e9e..0633330dc830 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 @@ -200,6 +200,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } }; + private final PipAnimationController.PipTransactionHandler mPipTransactionHandler = + new PipAnimationController.PipTransactionHandler() { + @Override + public boolean handlePipTransaction(SurfaceControl leash, + SurfaceControl.Transaction tx, Rect destinationBounds) { + if (mPipMenuController.isMenuVisible()) { + mPipMenuController.movePipMenu(leash, tx, destinationBounds); + return true; + } + return false; + } + }; + private ActivityManager.RunningTaskInfo mTaskInfo; // To handle the edge case that onTaskInfoChanged callback is received during the entering // PiP transition, where we do not want to intercept the transition but still want to apply the @@ -433,8 +446,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // removePipImmediately is expected when the following animation finishes. ValueAnimator animator = mPipAnimationController - .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(), 1f, 0f) + .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(), + 1f /* alphaStart */, 0f /* alphaEnd */) .setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK) + .setPipTransactionHandler(mPipTransactionHandler) .setPipAnimationCallback(mPipAnimationCallback); animator.setDuration(mExitAnimationDuration); animator.setInterpolator(Interpolators.ALPHA_OUT); @@ -573,6 +588,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, .getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setPipAnimationCallback(mPipAnimationCallback) + .setPipTransactionHandler(mPipTransactionHandler) .setDuration(durationMs) .start(); // mState is set right after the animation is kicked off to block any resize @@ -749,6 +765,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipAnimationController .getAnimator(mTaskInfo, mLeash, mPipBoundsState.getBounds(), alphaStart, alphaEnd) .setTransitionDirection(TRANSITION_DIRECTION_SAME) + .setPipTransactionHandler(mPipTransactionHandler) .setDuration(show ? mEnterAnimationDuration : mExitAnimationDuration) .start(); mHasFadeOut = !show; @@ -1226,6 +1243,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, sourceHintRect, direction, startingAngle, rotationDelta); animator.setTransitionDirection(direction) .setPipAnimationCallback(mPipAnimationCallback) + .setPipTransactionHandler(mPipTransactionHandler) .setDuration(durationMs) .start(); if (rotationDelta != Surface.ROTATION_0 && direction == TRANSITION_DIRECTION_TO_PIP) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 9b6909b2bd51..4759550c35c0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -111,14 +111,14 @@ public class PipTransition extends PipTransitionController { final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( taskInfo.pictureInPictureParams, currentBounds); - animator = mPipAnimationController.getAnimator(taskInfo, leash, - currentBounds, currentBounds, destinationBounds, sourceHintRect, - TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, Surface.ROTATION_0); + animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds, + currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, + 0 /* startingAngle */, Surface.ROTATION_0); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { t.setAlpha(leash, 0f); t.apply(); - animator = mPipAnimationController.getAnimator(taskInfo, leash, - destinationBounds, 0f, 1f); + animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds, + 0f, 1f); mOneShotAnimationType = ANIM_TYPE_BOUNDS; } else { throw new RuntimeException("Unrecognized animation type: " diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index 9cf0b721cc48..052653e0cc81 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -70,12 +70,19 @@ public class PhonePipMenuController implements PipMenuController { */ public interface Listener { /** - * Called when the PIP menu visibility changes. + * Called when the PIP menu visibility change has started. * - * @param menuState the current state of the menu + * @param menuState the new, about-to-change state of the menu * @param resize whether or not to resize the PiP with the state change */ - void onPipMenuStateChanged(int menuState, boolean resize, Runnable callback); + void onPipMenuStateChangeStart(int menuState, boolean resize, Runnable callback); + + /** + * Called when the PIP menu state has finished changing/animating. + * + * @param menuState the new state of the menu. + */ + void onPipMenuStateChangeFinish(int menuState); /** * Called when the PIP requested to be expanded. @@ -485,15 +492,15 @@ public class PhonePipMenuController implements PipMenuController { /** * Handles changes in menu visibility. */ - void onMenuStateChanged(int menuState, boolean resize, Runnable callback) { + void onMenuStateChangeStart(int menuState, boolean resize, Runnable callback) { if (DEBUG) { - Log.d(TAG, "onMenuStateChanged() mMenuState=" + mMenuState + Log.d(TAG, "onMenuStateChangeStart() mMenuState=" + mMenuState + " menuState=" + menuState + " resize=" + resize + " callers=\n" + Debug.getCallers(5, " ")); } if (menuState != mMenuState) { - mListeners.forEach(l -> l.onPipMenuStateChanged(menuState, resize, callback)); + mListeners.forEach(l -> l.onPipMenuStateChangeStart(menuState, resize, callback)); if (menuState == MENU_STATE_FULL) { // Once visible, start listening for media action changes. This call will trigger // the menu actions to be updated again. @@ -511,6 +518,12 @@ public class PhonePipMenuController implements PipMenuController { Log.e(TAG, "Unable to update focus as menu appears/disappears", e); } } + } + + void onMenuStateChangeFinish(int menuState) { + if (menuState != mMenuState) { + mListeners.forEach(l -> l.onPipMenuStateChangeFinish(menuState)); + } mMenuState = menuState; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 29a483bd7ae3..91e38872e3d0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -535,10 +535,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb private void onPipCornerRadiusChanged() { if (mPinnedStackAnimationRecentsCallback != null) { - final boolean enableCornerRadius = - SystemProperties.getBoolean("debug.sf.enable_hole_punch_pip", false); - final int cornerRadius = enableCornerRadius - ? mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius) : 0; + final int cornerRadius = + mContext.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius); try { mPinnedStackAnimationRecentsCallback.onPipCornerRadiusChanged(cornerRadius); } catch (RemoteException e) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index ecbf0f128b5c..7b17fe45a9a3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -45,7 +45,6 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.os.SystemProperties; import android.os.UserHandle; import android.util.Log; import android.util.Pair; @@ -152,11 +151,7 @@ public class PipMenuView extends FrameLayout { mAccessibilityManager = context.getSystemService(AccessibilityManager.class); inflate(context, R.layout.pip_menu, this); - final boolean enableCornerRadius = - SystemProperties.getBoolean("debug.sf.enable_hole_punch_pip", false); - mBackgroundDrawable = enableCornerRadius - ? mContext.getDrawable(R.drawable.pip_menu_background) - : new ColorDrawable(Color.BLACK); + mBackgroundDrawable = mContext.getDrawable(R.drawable.pip_menu_background); mBackgroundDrawable.setAlpha(0); mViewRoot = findViewById(R.id.background); mViewRoot.setBackground(mBackgroundDrawable); @@ -281,17 +276,18 @@ public class PipMenuView extends FrameLayout { } mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN); mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS); - if (allowMenuTimeout) { - mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { + mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + notifyMenuStateChangeFinish(menuState); + if (allowMenuTimeout) { repostDelayedHide(INITIAL_DISMISS_DELAY); } - }); - } + } + }); if (withDelay) { // starts the menu container animation after window expansion is completed - notifyMenuStateChange(menuState, resizeMenuOnShow, () -> { + notifyMenuStateChangeStart(menuState, resizeMenuOnShow, () -> { if (mMenuContainerAnimator == null) { return; } @@ -300,11 +296,11 @@ public class PipMenuView extends FrameLayout { mMenuContainerAnimator.start(); }); } else { - notifyMenuStateChange(menuState, resizeMenuOnShow, null); + notifyMenuStateChangeStart(menuState, resizeMenuOnShow, null); setVisibility(VISIBLE); mMenuContainerAnimator.start(); } - updateActionViews(stackBounds); + updateActionViews(menuState, stackBounds); } else { // If we are already visible, then just start the delayed dismiss and unregister any // existing input consumers from the previous drag @@ -357,7 +353,7 @@ public class PipMenuView extends FrameLayout { if (mMenuState != MENU_STATE_NONE) { cancelDelayedHide(); if (notifyMenuVisibility) { - notifyMenuStateChange(MENU_STATE_NONE, resize, null); + notifyMenuStateChangeStart(MENU_STATE_NONE, resize, null); } mMenuContainerAnimator = new AnimatorSet(); ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA, @@ -376,6 +372,9 @@ public class PipMenuView extends FrameLayout { @Override public void onAnimationEnd(Animator animation) { setVisibility(GONE); + if (notifyMenuVisibility) { + notifyMenuStateChangeFinish(MENU_STATE_NONE); + } if (animationFinishedRunnable != null) { animationFinishedRunnable.run(); } @@ -404,11 +403,11 @@ public class PipMenuView extends FrameLayout { mActions.clear(); mActions.addAll(actions); if (mMenuState == MENU_STATE_FULL) { - updateActionViews(stackBounds); + updateActionViews(mMenuState, stackBounds); } } - private void updateActionViews(Rect stackBounds) { + private void updateActionViews(int menuState, Rect stackBounds) { ViewGroup expandContainer = findViewById(R.id.expand_container); ViewGroup actionsContainer = findViewById(R.id.actions_container); actionsContainer.setOnTouchListener((v, ev) -> { @@ -417,13 +416,13 @@ public class PipMenuView extends FrameLayout { }); // Update the expand button only if it should show with the menu - expandContainer.setVisibility(mMenuState == MENU_STATE_FULL + expandContainer.setVisibility(menuState == MENU_STATE_FULL ? View.VISIBLE : View.INVISIBLE); FrameLayout.LayoutParams expandedLp = (FrameLayout.LayoutParams) expandContainer.getLayoutParams(); - if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE || mMenuState == MENU_STATE_NONE) { + if (mActions.isEmpty() || menuState == MENU_STATE_CLOSE || menuState == MENU_STATE_NONE) { actionsContainer.setVisibility(View.INVISIBLE); // Update the expand container margin to adjust the center of the expand button to @@ -493,9 +492,13 @@ public class PipMenuView extends FrameLayout { expandContainer.requestLayout(); } - private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) { + private void notifyMenuStateChangeStart(int menuState, boolean resize, Runnable callback) { + mController.onMenuStateChangeStart(menuState, resize, callback); + } + + private void notifyMenuStateChangeFinish(int menuState) { mMenuState = menuState; - mController.onMenuStateChanged(menuState, resize, callback); + mController.onMenuStateChangeFinish(menuState); } private void expandPip() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 8726ee76d29a..0878f54b7d27 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -419,13 +419,13 @@ public class PipResizeGestureHandler { // Reset the down to begin resizing from this point mDownPoint.set(mLastPoint); mDownSecondPoint.set(mLastSecondPoint); - } - if (mThresholdCrossed) { if (mPhonePipMenuController.isMenuVisible()) { mPhonePipMenuController.hideMenu(); } + } + if (mThresholdCrossed) { mAngle = mPinchResizingAlgorithm.calculateBoundsAndAngle(mDownPoint, mDownSecondPoint, mLastPoint, mLastSecondPoint, mMinSize, mMaxSize, mDownBounds, mLastResizeBounds); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 0a0798ef24c1..8f9dcef14c47 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -129,8 +129,13 @@ public class PipTouchHandler { */ private class PipMenuListener implements PhonePipMenuController.Listener { @Override - public void onPipMenuStateChanged(int menuState, boolean resize, Runnable callback) { - setMenuState(menuState, resize, callback); + public void onPipMenuStateChangeStart(int menuState, boolean resize, Runnable callback) { + PipTouchHandler.this.onPipMenuStateChangeStart(menuState, resize, callback); + } + + @Override + public void onPipMenuStateChangeFinish(int menuState) { + setMenuState(menuState); } @Override @@ -614,7 +619,7 @@ public class PipTouchHandler { } } - shouldDeliverToMenu |= !mPipBoundsState.isStashed(); + shouldDeliverToMenu &= !mPipBoundsState.isStashed(); // Deliver the event to PipMenuActivity to handle button click if the menu has shown. if (shouldDeliverToMenu) { @@ -646,9 +651,9 @@ public class PipTouchHandler { } /** - * Sets the menu visibility. + * Called when the PiP menu state is in the process of animating/changing from one to another. */ - private void setMenuState(int menuState, boolean resize, Runnable callback) { + private void onPipMenuStateChangeStart(int menuState, boolean resize, Runnable callback) { if (mMenuState == menuState && !resize) { return; } @@ -686,6 +691,9 @@ public class PipTouchHandler { mSavedSnapFraction = -1f; } } + } + + private void setMenuState(int menuState) { mMenuState = menuState; updateMovementBounds(); // If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip 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 a1e68329fdc0..26e8753ad3cf 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 @@ -103,7 +103,7 @@ public class StartingSurfaceDrawer { private Choreographer mChoreographer; private static final boolean DEBUG_ENABLE_REVEAL_ANIMATION = - SystemProperties.getBoolean("persist.debug.enable_reveal_animation", false); + SystemProperties.getBoolean("persist.debug.enable_reveal_animation", true); /** * @param splashScreenExecutor The thread used to control add and remove starting window. */ diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt index 1f9ff4ab5638..b5198bb46a38 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt @@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.apppairs import android.os.SystemClock import android.platform.test.annotations.Presubmit -import android.provider.Settings import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -27,6 +26,8 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.wm.shell.flicker.appPairsDividerIsInvisible import com.android.wm.shell.flicker.helpers.AppPairsHelper +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow import org.junit.After import org.junit.Before import org.junit.FixMethodOrder @@ -49,7 +50,6 @@ import org.junit.runners.Parameterized class AppPairsTestCannotPairNonResizeableApps( testSpec: FlickerTestParameter ) : AppPairsTransition(testSpec) { - var prevSupportNonResizableInMultiWindow = 0 override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { @@ -64,21 +64,15 @@ class AppPairsTestCannotPairNonResizeableApps( } @Before - fun setup() { - prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW) - if (prevSupportNonResizableInMultiWindow == 1) { - // Not support non-resizable in multi window - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) - } + override fun setup() { + super.setup() + setSupportsNonResizableMultiWindow(instrumentation, -1) } @After - fun teardown() { - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, - prevSupportNonResizableInMultiWindow) + override fun teardown() { + super.teardown() + resetMultiWindowConfig(instrumentation) } @FlakyTest diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt index 1e3595c17f48..f2a375bb94f8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt @@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.apppairs import android.os.SystemClock import android.platform.test.annotations.Presubmit -import android.provider.Settings import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -27,6 +26,8 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.wm.shell.flicker.appPairsDividerIsVisible import com.android.wm.shell.flicker.helpers.AppPairsHelper +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow import org.junit.After import org.junit.Before import org.junit.FixMethodOrder @@ -49,7 +50,6 @@ import org.junit.runners.Parameterized class AppPairsTestSupportPairNonResizeableApps( testSpec: FlickerTestParameter ) : AppPairsTransition(testSpec) { - var prevSupportNonResizableInMultiWindow = 0 override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { @@ -64,21 +64,15 @@ class AppPairsTestSupportPairNonResizeableApps( } @Before - fun setup() { - prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW) - if (prevSupportNonResizableInMultiWindow == 0) { - // Support non-resizable in multi window - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) - } + override fun setup() { + super.setup() + setSupportsNonResizableMultiWindow(instrumentation, 1) } @After - fun teardown() { - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, - prevSupportNonResizableInMultiWindow) + override fun teardown() { + super.teardown() + resetMultiWindowConfig(instrumentation) } @FlakyTest diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt index 741773e8f61a..1935bb97849c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt @@ -20,11 +20,9 @@ import android.app.Instrumentation import android.content.Context import android.platform.test.annotations.Presubmit import android.system.helpers.ActivityHelper -import android.util.Log import android.view.Surface import androidx.test.filters.FlakyTest import androidx.test.platform.app.InstrumentationRegistry -import com.android.compatibility.common.util.SystemUtil import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder @@ -41,10 +39,14 @@ import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.helpers.AppPairsHelper +import com.android.wm.shell.flicker.helpers.BaseAppHelper +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow import com.android.wm.shell.flicker.helpers.SplitScreenHelper import com.android.wm.shell.flicker.testapp.Components +import org.junit.After +import org.junit.Before import org.junit.Test -import java.io.IOException abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) { protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() @@ -62,6 +64,21 @@ abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) protected var primaryTaskId = "" protected var secondaryTaskId = "" protected var nonResizeableTaskId = "" + private var prevDevEnableNonResizableMultiWindow = 0 + + @Before + open fun setup() { + prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context) + if (prevDevEnableNonResizableMultiWindow != 0) { + // Turn off the development option + setDevEnableNonResizableMultiWindow(context, 0) + } + } + + @After + open fun teardown() { + setDevEnableNonResizableMultiWindow(context, prevDevEnableNonResizableMultiWindow) + } @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { @@ -117,11 +134,7 @@ abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) } internal fun executeShellCommand(cmd: String) { - try { - SystemUtil.runShellCommand(instrumentation, cmd) - } catch (e: IOException) { - Log.d("AppPairsTest", "executeShellCommand error! $e") - } + BaseAppHelper.executeShellCommand(instrumentation, cmd) } internal fun composePairsCommand( diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt index 006b569146e4..4fe69ad7fabe 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt @@ -21,11 +21,14 @@ import android.content.ComponentName import android.content.pm.PackageManager.FEATURE_LEANBACK import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY import android.support.test.launcherhelper.LauncherStrategyFactory +import android.util.Log import androidx.test.uiautomator.By import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until +import com.android.compatibility.common.util.SystemUtil import com.android.server.wm.flicker.helpers.StandardAppHelper import com.android.server.wm.traces.parser.toWindowName +import java.io.IOException abstract class BaseAppHelper( instrumentation: Instrumentation, @@ -56,5 +59,13 @@ abstract class BaseAppHelper( companion object { private const val APP_CLOSE_WAIT_TIME_MS = 3_000L + + fun executeShellCommand(instrumentation: Instrumentation, cmd: String) { + try { + SystemUtil.runShellCommand(instrumentation, cmd) + } catch (e: IOException) { + Log.e("BaseAppHelper", "executeShellCommand error! $e") + } + } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt new file mode 100644 index 000000000000..7f99e62b36b0 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.helpers + +import android.app.Instrumentation +import android.content.ComponentName +import android.content.Context +import android.provider.Settings + +class MultiWindowHelper( + instrumentation: Instrumentation, + activityLabel: String, + componentsInfo: ComponentName +) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) { + + companion object { + fun getDevEnableNonResizableMultiWindow(context: Context): Int = + Settings.Global.getInt(context.contentResolver, + Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW) + + fun setDevEnableNonResizableMultiWindow(context: Context, configValue: Int) = + Settings.Global.putInt(context.contentResolver, + Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, configValue) + + fun setSupportsNonResizableMultiWindow(instrumentation: Instrumentation, configValue: Int) = + executeShellCommand( + instrumentation, + createConfigSupportsNonResizableMultiWindowCommand(configValue)) + + fun resetMultiWindowConfig(instrumentation: Instrumentation) = + executeShellCommand(instrumentation, resetMultiWindowConfigCommand) + + private fun createConfigSupportsNonResizableMultiWindowCommand(configValue: Int): String = + "wm set-multi-window-config --supportsNonResizable $configValue" + + private const val resetMultiWindowConfigCommand: String = "wm reset-multi-window-config" + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt index 04f97c8a4c99..c18c122d974c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.platform.test.annotations.Presubmit -import android.provider.Settings import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -27,6 +26,8 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.canSplitScreen import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.dockedStackDividerIsInvisible +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.After import org.junit.Assert @@ -51,7 +52,6 @@ import org.junit.runners.Parameterized class EnterSplitScreenNotSupportNonResizable( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - var prevSupportNonResizableInMultiWindow = 0 override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> @@ -76,21 +76,15 @@ class EnterSplitScreenNotSupportNonResizable( splitScreenApp.defaultWindowName) @Before - fun setup() { - prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW) - if (prevSupportNonResizableInMultiWindow == 1) { - // Not support non-resizable in multi window - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) - } + override fun setup() { + super.setup() + setSupportsNonResizableMultiWindow(instrumentation, -1) } @After - fun teardown() { - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, - prevSupportNonResizableInMultiWindow) + override fun teardown() { + super.teardown() + resetMultiWindowConfig(instrumentation) } @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt index 2832bb4d15d4..d5b9a135ba8c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.platform.test.annotations.Presubmit -import android.provider.Settings import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -27,6 +26,8 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.dockedStackDividerIsVisible +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.After import org.junit.Before @@ -50,7 +51,6 @@ import org.junit.runners.Parameterized class EnterSplitScreenSupportNonResizable( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - var prevSupportNonResizableInMultiWindow = 0 override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> @@ -73,21 +73,15 @@ class EnterSplitScreenSupportNonResizable( splitScreenApp.defaultWindowName) @Before - fun setup() { - prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW) - if (prevSupportNonResizableInMultiWindow != 1) { - // Support non-resizable in multi window - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) - } + override fun setup() { + super.setup() + setSupportsNonResizableMultiWindow(instrumentation, 1) } @After - fun teardown() { - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, - prevSupportNonResizableInMultiWindow) + override fun teardown() { + super.teardown() + resetMultiWindowConfig(instrumentation) } @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt index 32afd190af2b..612018e39f13 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.platform.test.annotations.Presubmit -import android.provider.Settings import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -32,6 +31,8 @@ import com.android.server.wm.flicker.layerBecomesVisible import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.dockedStackDividerIsInvisible +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.After import org.junit.Before @@ -53,7 +54,6 @@ import org.junit.runners.Parameterized class LegacySplitScreenFromIntentNotSupportNonResizable( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - var prevSupportNonResizableInMultiWindow = 0 override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> @@ -77,21 +77,15 @@ class LegacySplitScreenFromIntentNotSupportNonResizable( WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) @Before - fun setup() { - prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW) - if (prevSupportNonResizableInMultiWindow == 1) { - // Not support non-resizable in multi window - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) - } + override fun setup() { + super.setup() + setSupportsNonResizableMultiWindow(instrumentation, -1) } @After - fun teardown() { - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, - prevSupportNonResizableInMultiWindow) + override fun teardown() { + super.teardown() + resetMultiWindowConfig(instrumentation) } @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt index af307580df3f..65062f9acc7d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.platform.test.annotations.Presubmit -import android.provider.Settings import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -30,6 +29,8 @@ import com.android.server.wm.flicker.layerBecomesVisible import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.dockedStackDividerIsVisible +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.After import org.junit.Before @@ -51,7 +52,6 @@ import org.junit.runners.Parameterized class LegacySplitScreenFromIntentSupportNonResizable( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - var prevSupportNonResizableInMultiWindow = 0 override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> @@ -75,21 +75,15 @@ class LegacySplitScreenFromIntentSupportNonResizable( WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) @Before - fun setup() { - prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW) - if (prevSupportNonResizableInMultiWindow == 0) { - // Support non-resizable in multi window - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) - } + override fun setup() { + super.setup() + setSupportsNonResizableMultiWindow(instrumentation, 1) } @After - fun teardown() { - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, - prevSupportNonResizableInMultiWindow) + override fun teardown() { + super.teardown() + resetMultiWindowConfig(instrumentation) } @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt index 8c6275821b97..37207875e372 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.platform.test.annotations.Presubmit -import android.provider.Settings import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -33,6 +32,8 @@ import com.android.server.wm.flicker.layerBecomesVisible import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.dockedStackDividerIsInvisible +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.After import org.junit.Before @@ -54,7 +55,6 @@ import org.junit.runners.Parameterized class LegacySplitScreenFromRecentNotSupportNonResizable( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - var prevSupportNonResizableInMultiWindow = 0 override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> @@ -78,21 +78,15 @@ class LegacySplitScreenFromRecentNotSupportNonResizable( WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) @Before - fun setup() { - prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW) - if (prevSupportNonResizableInMultiWindow == 1) { - // Not support non-resizable in multi window - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) - } + override fun setup() { + super.setup() + setSupportsNonResizableMultiWindow(instrumentation, -1) } @After - fun teardown() { - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, - prevSupportNonResizableInMultiWindow) + override fun teardown() { + super.teardown() + resetMultiWindowConfig(instrumentation) } @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt index 5b48f8a1df1d..61ebcd2af5de 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.platform.test.annotations.Presubmit -import android.provider.Settings import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -31,6 +30,8 @@ import com.android.server.wm.flicker.layerBecomesVisible import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.dockedStackDividerIsVisible +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.After import org.junit.Before @@ -52,7 +53,6 @@ import org.junit.runners.Parameterized class LegacySplitScreenFromRecentSupportNonResizable( testSpec: FlickerTestParameter ) : LegacySplitScreenTransition(testSpec) { - var prevSupportNonResizableInMultiWindow = 0 override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> @@ -76,21 +76,15 @@ class LegacySplitScreenFromRecentSupportNonResizable( WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) @Before - fun setup() { - prevSupportNonResizableInMultiWindow = Settings.Global.getInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW) - if (prevSupportNonResizableInMultiWindow == 0) { - // Support non-resizable in multi window - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) - } + override fun setup() { + super.setup() + setSupportsNonResizableMultiWindow(instrumentation, 1) } @After - fun teardown() { - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, - prevSupportNonResizableInMultiWindow) + override fun teardown() { + super.teardown() + resetMultiWindowConfig(instrumentation) } @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt index 8684ba52bd52..e8d4d1e9ada2 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt @@ -32,7 +32,11 @@ import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow +import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import org.junit.After +import org.junit.Before import org.junit.Test abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) { @@ -44,6 +48,21 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation) protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation) .launcherStrategy.supportedLauncherPackage + private var prevDevEnableNonResizableMultiWindow = 0 + + @Before + open fun setup() { + prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context) + if (prevDevEnableNonResizableMultiWindow != 0) { + // Turn off the development option + setDevEnableNonResizableMultiWindow(context, 0) + } + } + + @After + open fun teardown() { + setDevEnableNonResizableMultiWindow(context, prevDevEnableNonResizableMultiWindow) + } /** * List of windows that are ignored when verifying that visible elements appear on 2 diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 76366fce2aea..26d836328b54 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -88,7 +88,7 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path, if (IsFabricatedOverlay(overlay_path)) { // Fabricated overlays do not contain resource definitions. All of the overlay resource values // are defined inline in the idmap. - overlay_assets = EmptyAssetsProvider::Create(); + overlay_assets = EmptyAssetsProvider::Create(overlay_path); } else { // The overlay should be an APK. overlay_assets = ZipAssetsProvider::Create(overlay_path); diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 320106a20b69..3a0153fa3dd8 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -612,6 +612,11 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( result->entry = overlay_entry.GetInlineValue(); result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); result->cookie = id_map.cookie; + + if (UNLIKELY(logging_enabled)) { + last_resolution_.steps.push_back( + Resolution::Step{Resolution::Step::Type::OVERLAID_INLINE, String8(), result->cookie}); + } continue; } @@ -640,7 +645,6 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back( Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(), - overlay_result->package_name, overlay_result->cookie}); } } @@ -723,7 +727,6 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( if (UNLIKELY(logging_enabled)) { resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::SKIPPED, this_config.toString(), - &loaded_package->GetPackageName(), cookie}); } continue; @@ -741,7 +744,6 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( if (UNLIKELY(logging_enabled)) { resolution_steps.push_back(Resolution::Step{Resolution::Step::Type::NO_ENTRY, this_config.toString(), - &loaded_package->GetPackageName(), cookie}); } continue; @@ -756,7 +758,6 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back(Resolution::Step{resolution_type, this_config.toString(), - &loaded_package->GetPackageName(), cookie}); } @@ -839,18 +840,18 @@ std::string AssetManager2::GetLastResourceResolution() const { } std::stringstream log_stream; - log_stream << base::StringPrintf("Resolution for 0x%08x ", resid) - << resource_name_string - << "\n\tFor config -" - << configuration_.toString(); + log_stream << base::StringPrintf("Resolution for 0x%08x %s\n" + "\tFor config - %s", resid, resource_name_string.c_str(), + configuration_.toString().c_str()); for (const Resolution::Step& step : last_resolution_.steps) { const static std::unordered_map<Resolution::Step::Type, const char*> kStepStrings = { - {Resolution::Step::Type::INITIAL, "Found initial"}, - {Resolution::Step::Type::BETTER_MATCH, "Found better"}, - {Resolution::Step::Type::OVERLAID, "Overlaid"}, - {Resolution::Step::Type::SKIPPED, "Skipped"}, - {Resolution::Step::Type::NO_ENTRY, "No entry"} + {Resolution::Step::Type::INITIAL, "Found initial"}, + {Resolution::Step::Type::BETTER_MATCH, "Found better"}, + {Resolution::Step::Type::OVERLAID, "Overlaid"}, + {Resolution::Step::Type::OVERLAID_INLINE, "Overlaid inline"}, + {Resolution::Step::Type::SKIPPED, "Skipped"}, + {Resolution::Step::Type::NO_ENTRY, "No entry"} }; const auto prefix = kStepStrings.find(step.type); @@ -858,10 +859,9 @@ std::string AssetManager2::GetLastResourceResolution() const { continue; } - log_stream << "\n\t" << prefix->second << ": " << *step.package_name << " (" - << apk_assets_[step.cookie]->GetDebugName() << ")"; + log_stream << "\n\t" << prefix->second << ": " << apk_assets_[step.cookie]->GetDebugName(); if (!step.config_name.isEmpty()) { - log_stream << " -" << step.config_name; + log_stream << " - " << step.config_name; } } diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp index 0aaf0b359b60..6c7a25307247 100644 --- a/libs/androidfw/AssetsProvider.cpp +++ b/libs/androidfw/AssetsProvider.cpp @@ -386,8 +386,15 @@ bool MultiAssetsProvider::IsUpToDate() const { return primary_->IsUpToDate() && secondary_->IsUpToDate(); } +EmptyAssetsProvider::EmptyAssetsProvider(std::optional<std::string>&& path) : + path_(std::move(path)) {} + std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create() { - return std::make_unique<EmptyAssetsProvider>(); + return std::unique_ptr<EmptyAssetsProvider>(new EmptyAssetsProvider({})); +} + +std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create(const std::string& path) { + return std::unique_ptr<EmptyAssetsProvider>(new EmptyAssetsProvider(path)); } std::unique_ptr<Asset> EmptyAssetsProvider::OpenInternal(const std::string& /* path */, @@ -406,10 +413,16 @@ bool EmptyAssetsProvider::ForEachFile( } std::optional<std::string_view> EmptyAssetsProvider::GetPath() const { + if (path_.has_value()) { + return *path_; + } return {}; } const std::string& EmptyAssetsProvider::GetDebugName() const { + if (path_.has_value()) { + return *path_; + } const static std::string kEmpty = kEmptyDebugString; return kEmpty; } diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 10666adfdceb..df3abf63323a 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -458,6 +458,7 @@ class AssetManager2 { INITIAL, BETTER_MATCH, OVERLAID, + OVERLAID_INLINE, SKIPPED, NO_ENTRY, }; @@ -468,10 +469,6 @@ class AssetManager2 { // Built name of configuration for this step. String8 config_name; - // Marks the package name of the better resource found in this step. - const std::string* package_name; - - // ApkAssetsCookie cookie = kInvalidCookie; }; diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h index 63bbdcc9698d..ec51c65053bf 100644 --- a/libs/androidfw/include/androidfw/AssetsProvider.h +++ b/libs/androidfw/include/androidfw/AssetsProvider.h @@ -176,6 +176,7 @@ struct MultiAssetsProvider : public AssetsProvider { // Does not provide any assets. struct EmptyAssetsProvider : public AssetsProvider { static std::unique_ptr<AssetsProvider> Create(); + static std::unique_ptr<AssetsProvider> Create(const std::string& path); bool ForEachFile(const std::string& path, const std::function<void(const StringPiece&, FileType)>& f) const override; @@ -188,6 +189,10 @@ struct EmptyAssetsProvider : public AssetsProvider { protected: std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode, bool* file_exists) const override; + + private: + explicit EmptyAssetsProvider(std::optional<std::string>&& path); + std::optional<std::string> path_; }; } // namespace android diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index e1c0fab77884..3c4ee4e63a76 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -765,7 +765,8 @@ TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) { auto result = assetmanager.GetLastResourceResolution(); EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n" - "\tFor config -de\n\tFound initial: com.android.basic (basic/basic.apk)", result); + "\tFor config - de\n" + "\tFound initial: basic/basic.apk", result); } TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) { @@ -784,9 +785,9 @@ TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) { auto result = assetmanager.GetLastResourceResolution(); EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n" - "\tFor config -de\n" - "\tFound initial: com.android.basic (basic/basic.apk)\n" - "\tFound better: com.android.basic (basic/basic_de_fr.apk) -de", result); + "\tFor config - de\n" + "\tFound initial: basic/basic.apk\n" + "\tFound better: basic/basic_de_fr.apk - de", result); } TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp index 979678d1c4b6..d9b9e2456fff 100644 --- a/libs/hwui/WebViewFunctorManager.cpp +++ b/libs/hwui/WebViewFunctorManager.cpp @@ -221,8 +221,30 @@ WebViewFunctorManager& WebViewFunctorManager::instance() { return sInstance; } +static void validateCallbacks(const WebViewFunctorCallbacks& callbacks) { + // TODO: Should we do a stack peek to see if this is really webview? + LOG_ALWAYS_FATAL_IF(callbacks.onSync == nullptr, "onSync is null"); + LOG_ALWAYS_FATAL_IF(callbacks.onContextDestroyed == nullptr, "onContextDestroyed is null"); + LOG_ALWAYS_FATAL_IF(callbacks.onDestroyed == nullptr, "onDestroyed is null"); + LOG_ALWAYS_FATAL_IF(callbacks.removeOverlays == nullptr, "removeOverlays is null"); + switch (auto mode = WebViewFunctor_queryPlatformRenderMode()) { + case RenderMode::OpenGL_ES: + LOG_ALWAYS_FATAL_IF(callbacks.gles.draw == nullptr, "gles.draw is null"); + break; + case RenderMode::Vulkan: + LOG_ALWAYS_FATAL_IF(callbacks.vk.initialize == nullptr, "vk.initialize is null"); + LOG_ALWAYS_FATAL_IF(callbacks.vk.draw == nullptr, "vk.draw is null"); + LOG_ALWAYS_FATAL_IF(callbacks.vk.postDraw == nullptr, "vk.postDraw is null"); + break; + default: + LOG_ALWAYS_FATAL("unknown platform mode? %d", (int)mode); + break; + } +} + int WebViewFunctorManager::createFunctor(void* data, const WebViewFunctorCallbacks& callbacks, RenderMode functorMode) { + validateCallbacks(callbacks); auto object = std::make_unique<WebViewFunctor>(data, callbacks, functorMode); int id = object->id(); auto handle = object->createHandle(); diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp index 0b995bc295de..10427039c35a 100644 --- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp +++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.cpp @@ -86,12 +86,9 @@ void SkiaMemoryTracer::processElement() { } } - // if we don't have a resource name then we don't know how to label the - // data and should abort. + // if we don't have a pretty name then use the dumpName if (resourceName == nullptr) { - mCurrentElement.clear(); - mCurrentValues.clear(); - return; + resourceName = mCurrentElement.c_str(); } auto result = mResults.find(resourceName); @@ -157,6 +154,14 @@ void SkiaMemoryTracer::logOutput(String8& log) { } } +size_t SkiaMemoryTracer::total() { + processElement(); + if (!strcmp("bytes", mTotalSize.units)) { + return mTotalSize.value; + } + return 0; +} + void SkiaMemoryTracer::logTotals(String8& log) { TraceValue total = convertUnits(mTotalSize); TraceValue purgeable = convertUnits(mPurgeableSize); diff --git a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h index b393b0755a70..cba3b0422c6f 100644 --- a/libs/hwui/pipeline/skia/SkiaMemoryTracer.h +++ b/libs/hwui/pipeline/skia/SkiaMemoryTracer.h @@ -37,6 +37,7 @@ public: bool hasOutput(); void logOutput(String8& log); void logTotals(String8& log); + size_t total(); void dumpNumericValue(const char* dumpName, const char* valueName, const char* units, uint64_t value) override; diff --git a/libs/hwui/pipeline/skia/StretchMask.cpp b/libs/hwui/pipeline/skia/StretchMask.cpp index 2bbd8a4fa028..1c58c6a5d440 100644 --- a/libs/hwui/pipeline/skia/StretchMask.cpp +++ b/libs/hwui/pipeline/skia/StretchMask.cpp @@ -46,9 +46,16 @@ void StretchMask::draw(GrRecordingContext* context, if (mIsDirty) { SkCanvas* maskCanvas = mMaskSurface->getCanvas(); + // Make sure to apply target transformation to the mask canvas + // to ensure the replayed drawing commands generate the same result + auto previousMatrix = displayList->mParentMatrix; + displayList->mParentMatrix = maskCanvas->getTotalMatrix(); + maskCanvas->save(); maskCanvas->drawColor(0, SkBlendMode::kClear); TransformCanvas transformCanvas(maskCanvas, SkBlendMode::kSrcOver); displayList->draw(&transformCanvas); + maskCanvas->restore(); + displayList->mParentMatrix = previousMatrix; } sk_sp<SkImage> maskImage = mMaskSurface->makeImageSnapshot(); diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 5047be979e41..46e806081c0c 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -130,27 +130,43 @@ void CacheManager::trimStaleResources() { mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30)); } +void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) { + *cpuUsage = 0; + *gpuUsage = 0; + if (!mGrContext) { + return; + } + + skiapipeline::SkiaMemoryTracer cpuTracer("category", true); + SkGraphics::DumpMemoryStatistics(&cpuTracer); + *cpuUsage += cpuTracer.total(); + + skiapipeline::SkiaMemoryTracer gpuTracer("category", true); + mGrContext->dumpMemoryStatistics(&gpuTracer); + *gpuUsage += gpuTracer.total(); +} + void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) { if (!mGrContext) { log.appendFormat("No valid cache instance.\n"); return; } - log.appendFormat("Font Cache (CPU):\n"); - log.appendFormat(" Size: %.2f kB \n", SkGraphics::GetFontCacheUsed() / 1024.0f); - log.appendFormat(" Glyph Count: %d \n", SkGraphics::GetFontCacheCountUsed()); - std::vector<skiapipeline::ResourcePair> cpuResourceMap = { {"skia/sk_resource_cache/bitmap_", "Bitmaps"}, {"skia/sk_resource_cache/rrect-blur_", "Masks"}, {"skia/sk_resource_cache/rects-blur_", "Masks"}, {"skia/sk_resource_cache/tessellated", "Shadows"}, + {"skia/sk_glyph_cache", "Glyph Cache"}, }; skiapipeline::SkiaMemoryTracer cpuTracer(cpuResourceMap, false); SkGraphics::DumpMemoryStatistics(&cpuTracer); if (cpuTracer.hasOutput()) { log.appendFormat("CPU Caches:\n"); cpuTracer.logOutput(log); + log.appendFormat(" Glyph Count: %d \n", SkGraphics::GetFontCacheCountUsed()); + log.appendFormat("Total CPU memory usage:\n"); + cpuTracer.logTotals(log); } skiapipeline::SkiaMemoryTracer gpuTracer("category", true); diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index 0a6b8dc26cc3..713ea990ff23 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -47,6 +47,7 @@ public: void trimMemory(TrimMemoryMode mode); void trimStaleResources(); void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr); + void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage); size_t getCacheSize() const { return mMaxResourceBytes; } size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; } diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index ad325cf2cdcc..95aa29d9eb13 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -195,6 +195,17 @@ void RenderProxy::trimMemory(int level) { } } +void RenderProxy::purgeCaches() { + if (RenderThread::hasInstance()) { + RenderThread& thread = RenderThread::getInstance(); + thread.queue().post([&thread]() { + if (thread.getGrContext()) { + thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete); + } + }); + } +} + void RenderProxy::overrideProperty(const char* name, const char* value) { // expensive, but block here since name/value pointers owned by caller RenderThread::getInstance().queue().runSync( @@ -256,6 +267,13 @@ void RenderProxy::dumpGraphicsMemory(int fd, bool includeProfileData) { } } +void RenderProxy::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) { + if (RenderThread::hasInstance()) { + auto& thread = RenderThread::getInstance(); + thread.queue().runSync([&]() { thread.getMemoryUsage(cpuUsage, gpuUsage); }); + } +} + void RenderProxy::setProcessStatsBuffer(int fd) { auto& rt = RenderThread::getInstance(); rt.queue().post([&rt, fd = dup(fd)]() { diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 662b44505fa6..0681dc5e16be 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -98,6 +98,7 @@ public: void destroyHardwareResources(); static void trimMemory(int level); + static void purgeCaches(); static void overrideProperty(const char* name, const char* value); void fence(); @@ -110,6 +111,7 @@ public: void resetProfileInfo(); uint32_t frameTimePercentile(int p); static void dumpGraphicsMemory(int fd, bool includeProfileData = true); + static void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage); static void rotateProcessStatsBuffer(); static void setProcessStatsBuffer(int fd); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 308352d34174..0268bfd73e55 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -323,6 +323,10 @@ void RenderThread::dumpGraphicsMemory(int fd, bool includeProfileData) { dprintf(fd, "\nPipeline=%s\n%s\n", pipelineToString(), cachesOutput.string()); } +void RenderThread::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) { + mCacheManager->getMemoryUsage(cpuUsage, gpuUsage); +} + Readback& RenderThread::readback() { if (!mReadback) { mReadback = new Readback(*this); diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index afd5750e8fd4..5021085e3598 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -151,6 +151,7 @@ public: sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap); void dumpGraphicsMemory(int fd, bool includeProfileData); + void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage); void requireGlContext(); void requireVkContext(); diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h index 781884ac9d7d..6b0be53b7f9b 100644 --- a/libs/hwui/tests/common/TestScene.h +++ b/libs/hwui/tests/common/TestScene.h @@ -40,6 +40,7 @@ public: int reportFrametimeWeight = 0; bool renderOffscreen = true; bool reportGpuMemoryUsage = false; + bool reportGpuMemoryUsageVerbose = false; }; template <class T> diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index cf8fc82abb4a..5092675a8104 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -287,6 +287,7 @@ public: int sync = 0; int contextDestroyed = 0; int destroyed = 0; + int removeOverlays = 0; int glesDraw = 0; }; @@ -311,6 +312,12 @@ public: expectOnRenderThread("onDestroyed"); sMockFunctorCounts[functor].destroyed++; }, + .removeOverlays = + [](int functor, void* data, + void (*mergeTransaction)(ASurfaceTransaction*)) { + expectOnRenderThread("removeOverlays"); + sMockFunctorCounts[functor].removeOverlays++; + }, }; switch (mode) { case RenderMode::OpenGL_ES: diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index 9d3b732d75c3..b640b90ecd5e 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -28,6 +28,20 @@ #include <log/log.h> #include <ui/PixelFormat.h> +// These are unstable internal APIs in google-benchmark. We should just implement our own variant +// of these instead, but this was quicker. Disabled-by-default to avoid any breakages when +// google-benchmark updates if they change anything +#if 0 +#define USE_SKETCHY_INTERNAL_STATS +namespace benchmark { +std::vector<BenchmarkReporter::Run> ComputeStats( + const std::vector<BenchmarkReporter::Run> &reports); +double StatisticsMean(const std::vector<double>& v); +double StatisticsMedian(const std::vector<double>& v); +double StatisticsStdDev(const std::vector<double>& v); +} +#endif + using namespace android; using namespace android::uirenderer; using namespace android::uirenderer::renderthread; @@ -66,6 +80,7 @@ using BenchmarkResults = std::vector<benchmark::BenchmarkReporter::Run>; void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options& opts, double durationInS, int repetationIndex, BenchmarkResults* reports) { + using namespace benchmark; benchmark::BenchmarkReporter::Run report; report.repetitions = opts.repeatCount; report.repetition_index = repetationIndex; @@ -73,12 +88,22 @@ void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options report.iterations = static_cast<int64_t>(opts.frameCount); report.real_accumulated_time = durationInS; report.cpu_accumulated_time = durationInS; - report.counters["items_per_second"] = opts.frameCount / durationInS; + report.counters["FPS"] = opts.frameCount / durationInS; + if (opts.reportGpuMemoryUsage) { + size_t cpuUsage, gpuUsage; + RenderProxy::getMemoryUsage(&cpuUsage, &gpuUsage); + report.counters["Rendering RAM"] = Counter{static_cast<double>(cpuUsage + gpuUsage), + Counter::kDefaults, Counter::kIs1024}; + } reports->push_back(report); } static void doRun(const TestScene::Info& info, const TestScene::Options& opts, int repetitionIndex, BenchmarkResults* reports) { + if (opts.reportGpuMemoryUsage) { + // If we're reporting GPU memory usage we need to first start with a clean slate + RenderProxy::purgeCaches(); + } Properties::forceDrawFrame = true; TestContext testContext; testContext.setRenderOffscreen(opts.renderOffscreen); @@ -162,11 +187,6 @@ static void doRun(const TestScene::Info& info, const TestScene::Options& opts, i void run(const TestScene::Info& info, const TestScene::Options& opts, benchmark::BenchmarkReporter* reporter) { - if (opts.reportGpuMemoryUsage) { - // If we're reporting GPU memory usage we need to first start with a clean slate - // All repetitions of the same test will share a single memory usage report - RenderProxy::trimMemory(100); - } BenchmarkResults results; for (int i = 0; i < opts.repeatCount; i++) { doRun(info, opts, i, reporter ? &results : nullptr); @@ -174,10 +194,21 @@ void run(const TestScene::Info& info, const TestScene::Options& opts, if (reporter) { reporter->ReportRuns(results); if (results.size() > 1) { - // TODO: Report summary +#ifdef USE_SKETCHY_INTERNAL_STATS + std::vector<benchmark::internal::Statistics> stats; + stats.reserve(3); + stats.emplace_back("mean", benchmark::StatisticsMean); + stats.emplace_back("median", benchmark::StatisticsMedian); + stats.emplace_back("stddev", benchmark::StatisticsStdDev); + for (auto& it : results) { + it.statistics = &stats; + } + auto summary = benchmark::ComputeStats(results); + reporter->ReportRuns(summary); +#endif } } - if (opts.reportGpuMemoryUsage) { + if (opts.reportGpuMemoryUsageVerbose) { RenderProxy::dumpGraphicsMemory(STDOUT_FILENO, false); } } diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp index e9e962a78b0c..f3f32eb6897c 100644 --- a/libs/hwui/tests/macrobench/main.cpp +++ b/libs/hwui/tests/macrobench/main.cpp @@ -71,7 +71,7 @@ OPTIONS: --benchmark_format Set output format. Possible values are tabular, json, csv --renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk --skip-leak-check Skips the memory leak check - --report-gpu-memory Dumps the GPU memory usage after each test run + --report-gpu-memory[=verbose] Dumps the GPU memory usage after each test run )"); } @@ -142,7 +142,7 @@ static bool setBenchmarkFormat(const char* format) { } else if (!strcmp(format, "json")) { gBenchmarkReporter.reset(new benchmark::JSONReporter()); } else { - fprintf(stderr, "Unknown format '%s'", format); + fprintf(stderr, "Unknown format '%s'\n", format); return false; } return true; @@ -154,7 +154,7 @@ static bool setRenderer(const char* renderer) { } else if (!strcmp(renderer, "skiavk")) { Properties::overrideRenderPipelineType(RenderPipelineType::SkiaVulkan); } else { - fprintf(stderr, "Unknown format '%s'", renderer); + fprintf(stderr, "Unknown format '%s'\n", renderer); return false; } return true; @@ -191,7 +191,7 @@ static const struct option LONG_OPTIONS[] = { {"offscreen", no_argument, nullptr, LongOpts::Offscreen}, {"renderer", required_argument, nullptr, LongOpts::Renderer}, {"skip-leak-check", no_argument, nullptr, LongOpts::SkipLeakCheck}, - {"report-gpu-memory", no_argument, nullptr, LongOpts::ReportGpuMemory}, + {"report-gpu-memory", optional_argument, nullptr, LongOpts::ReportGpuMemory}, {0, 0, 0, 0}}; static const char* SHORT_OPTIONS = "c:r:h"; @@ -296,6 +296,14 @@ void parseOptions(int argc, char* argv[]) { case LongOpts::ReportGpuMemory: gOpts.reportGpuMemoryUsage = true; + if (optarg) { + if (!strcmp("verbose", optarg)) { + gOpts.reportGpuMemoryUsageVerbose = true; + } else { + fprintf(stderr, "Invalid report gpu memory option '%s'\n", optarg); + error = true; + } + } break; case 'h': @@ -313,7 +321,7 @@ void parseOptions(int argc, char* argv[]) { } if (error) { - fprintf(stderr, "Try 'hwuitest --help' for more information.\n"); + fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); exit(EXIT_FAILURE); } diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index 402cb5814366..10c874ec3f12 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -62,8 +62,10 @@ int main(int argc, char* argv[]) { gSigChain.insert(pair<int, struct sigaction>(sig, old_sa)); } - // Replace the default GLES driver + // Avoid talking to SF Properties::isolatedProcess = true; + // Default to GLES (Vulkan-aware tests will override this) + Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL); // Run the tests testing::InitGoogleTest(&argc, argv); diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types index 587367737be0..60f0e9ef49cf 100644 --- a/mime/java-res/android.mime.types +++ b/mime/java-res/android.mime.types @@ -88,6 +88,7 @@ ?audio/x-mpeg mp3 ?image/bmp bmp +?image/gif gif ?image/heic heic ?image/heic-sequence heics ?image/heif heif hif diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp index ea57ea070369..472e0a4347fc 100644 --- a/native/webview/plat_support/draw_functor.cpp +++ b/native/webview/plat_support/draw_functor.cpp @@ -192,44 +192,43 @@ void postDrawVk(int functor, void* data) { int CreateFunctor_v3(void* data, int version, AwDrawFnFunctorCallbacks* functor_callbacks) { - static bool callbacks_initialized = false; - static uirenderer::WebViewFunctorCallbacks webview_functor_callbacks = { - .onSync = &onSync, - .onContextDestroyed = &onContextDestroyed, - .onDestroyed = &onDestroyed, - .removeOverlays = &removeOverlays, - }; - if (!callbacks_initialized) { - switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) { - case uirenderer::RenderMode::OpenGL_ES: - webview_functor_callbacks.gles.draw = &draw_gl; - break; - case uirenderer::RenderMode::Vulkan: - webview_functor_callbacks.vk.initialize = &initializeVk; - webview_functor_callbacks.vk.draw = &drawVk; - webview_functor_callbacks.vk.postDraw = &postDrawVk; - break; - } - callbacks_initialized = true; - } - SupportData* support = new SupportData{ - .data = data, - }; + static uirenderer::WebViewFunctorCallbacks webview_functor_callbacks = [] { + uirenderer::WebViewFunctorCallbacks ret = { + .onSync = &onSync, + .onContextDestroyed = &onContextDestroyed, + .onDestroyed = &onDestroyed, + .removeOverlays = &removeOverlays, + }; + switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) { + case uirenderer::RenderMode::OpenGL_ES: + ret.gles.draw = &draw_gl; + break; + case uirenderer::RenderMode::Vulkan: + ret.vk.initialize = &initializeVk; + ret.vk.draw = &drawVk; + ret.vk.postDraw = &postDrawVk; + break; + } + return ret; + }(); + SupportData* support = new SupportData{ + .data = data, + }; - // These callbacks are available on all versions. - support->callbacks = { - .on_sync = functor_callbacks->on_sync, - .on_context_destroyed = functor_callbacks->on_context_destroyed, - .on_destroyed = functor_callbacks->on_destroyed, - .draw_gl = functor_callbacks->draw_gl, - .init_vk = functor_callbacks->init_vk, - .draw_vk = functor_callbacks->draw_vk, - .post_draw_vk = functor_callbacks->post_draw_vk, - }; + // These callbacks are available on all versions. + support->callbacks = { + .on_sync = functor_callbacks->on_sync, + .on_context_destroyed = functor_callbacks->on_context_destroyed, + .on_destroyed = functor_callbacks->on_destroyed, + .draw_gl = functor_callbacks->draw_gl, + .init_vk = functor_callbacks->init_vk, + .draw_vk = functor_callbacks->draw_vk, + .post_draw_vk = functor_callbacks->post_draw_vk, + }; - if (version >= 3) { - support->callbacks.remove_overlays = functor_callbacks->remove_overlays; - } + if (version >= 3) { + support->callbacks.remove_overlays = functor_callbacks->remove_overlays; + } int functor = uirenderer::WebViewFunctor_create( support, webview_functor_callbacks, diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp index bb93af90a9be..a75792c940c3 100644 --- a/packages/Connectivity/framework/Android.bp +++ b/packages/Connectivity/framework/Android.bp @@ -102,7 +102,7 @@ java_sdk_library { // Tests using hidden APIs "//cts/tests/netlegacy22.api", "//external/sl4a:__subpackages__", - "//frameworks/base/tests/net:__subpackages__", + "//frameworks/base/packages/Connectivity/tests:__subpackages__", "//frameworks/libs/net/common/testutils", "//frameworks/libs/net/common/tests:__subpackages__", "//frameworks/opt/telephony/tests/telephonytests", diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt index 78dff21b6c68..b219375aed70 100644 --- a/packages/Connectivity/framework/api/module-lib-current.txt +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -57,7 +57,7 @@ package android.net { method @Nullable public static android.net.ProxyInfo getGlobalProxy(@NonNull android.content.Context); method @NonNull public static java.time.Duration getMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); method public static boolean getMobileDataAlwaysOn(@NonNull android.content.Context, boolean); - method @Nullable public static String getMobileDataPreferredApps(@NonNull android.content.Context); + method @NonNull public static java.util.Set<java.lang.Integer> getMobileDataPreferredUids(@NonNull android.content.Context); method public static int getNetworkAvoidBadWifi(@NonNull android.content.Context); method @Nullable public static String getNetworkMeteredMultipathPreference(@NonNull android.content.Context); method public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, int); @@ -65,6 +65,7 @@ package android.net { method @NonNull public static String getPrivateDnsDefaultMode(@NonNull android.content.Context); method @Nullable public static String getPrivateDnsHostname(@NonNull android.content.Context); method public static int getPrivateDnsMode(@NonNull android.content.Context); + method @NonNull public static java.util.Set<java.lang.String> getRestrictedAllowedApps(@NonNull android.content.Context); method public static boolean getWifiAlwaysRequested(@NonNull android.content.Context, boolean); method @NonNull public static java.time.Duration getWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); method public static void setCaptivePortalHttpUrl(@NonNull android.content.Context, @Nullable String); @@ -76,7 +77,7 @@ package android.net { method public static void setGlobalProxy(@NonNull android.content.Context, @NonNull android.net.ProxyInfo); method public static void setMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); method public static void setMobileDataAlwaysOn(@NonNull android.content.Context, boolean); - method public static void setMobileDataPreferredApps(@NonNull android.content.Context, @Nullable String); + method public static void setMobileDataPreferredUids(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.Integer>); method public static void setNetworkAvoidBadWifi(@NonNull android.content.Context, int); method public static void setNetworkMeteredMultipathPreference(@NonNull android.content.Context, @NonNull String); method public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, @IntRange(from=0) int); @@ -84,6 +85,7 @@ package android.net { method public static void setPrivateDnsDefaultMode(@NonNull android.content.Context, @NonNull int); method public static void setPrivateDnsHostname(@NonNull android.content.Context, @Nullable String); method public static void setPrivateDnsMode(@NonNull android.content.Context, int); + method public static void setRestrictedAllowedApps(@NonNull android.content.Context, @NonNull java.util.Set<java.lang.String>); method public static void setWifiAlwaysRequested(@NonNull android.content.Context, boolean); method public static void setWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2 diff --git a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java index 31e1fb058187..07754e4af2ff 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java @@ -28,8 +28,11 @@ import android.annotation.SystemApi; import android.content.ContentResolver; import android.content.Context; import android.net.ConnectivityManager.MultipathPreference; +import android.os.Process; +import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; +import android.util.ArraySet; import android.util.Range; import com.android.net.module.util.ProxyUtils; @@ -38,6 +41,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.time.Duration; import java.util.List; +import java.util.Set; +import java.util.StringJoiner; +import java.util.regex.Pattern; /** * A manager class for connectivity module settings. @@ -330,12 +336,12 @@ public class ConnectivitySettingsManager { "network_metered_multipath_preference"; /** - * A list of apps that should go on cellular networks in preference even when higher-priority + * A list of uids that should go on cellular networks in preference even when higher-priority * networks are connected. * * @hide */ - public static final String MOBILE_DATA_PREFERRED_APPS = "mobile_data_preferred_apps"; + public static final String MOBILE_DATA_PREFERRED_UIDS = "mobile_data_preferred_uids"; /** * One of the private DNS modes that indicates the private DNS mode is off. @@ -369,6 +375,13 @@ public class ConnectivitySettingsManager { private static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME_STRING = "hostname"; /** + * A list of apps that should be granted netd system permission for using restricted networks. + * + * @hide + */ + public static final String RESTRICTED_ALLOWED_APPS = "restricted_allowed_apps"; + + /** * Get mobile data activity timeout from {@link Settings}. * * @param context The {@link Context} to query the setting. @@ -991,27 +1004,88 @@ public class ConnectivitySettingsManager { } /** - * Get the list of apps(from {@link Settings}) that should go on cellular networks in preference + * Get the list of uids(from {@link Settings}) that should go on cellular networks in preference * even when higher-priority networks are connected. * * @param context The {@link Context} to query the setting. - * @return A list of apps that should go on cellular networks in preference even when + * @return A list of uids that should go on cellular networks in preference even when * higher-priority networks are connected or null if no setting value. */ - @Nullable - public static String getMobileDataPreferredApps(@NonNull Context context) { - return Settings.Secure.getString(context.getContentResolver(), MOBILE_DATA_PREFERRED_APPS); + @NonNull + public static Set<Integer> getMobileDataPreferredUids(@NonNull Context context) { + final String uidList = Settings.Secure.getString( + context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS); + final Set<Integer> uids = new ArraySet<>(); + if (TextUtils.isEmpty(uidList)) { + return uids; + } + for (String uid : uidList.split(";")) { + uids.add(Integer.valueOf(uid)); + } + return uids; } /** - * Set the list of apps(to {@link Settings}) that should go on cellular networks in preference + * Set the list of uids(to {@link Settings}) that should go on cellular networks in preference * even when higher-priority networks are connected. * * @param context The {@link Context} to set the setting. - * @param list A list of apps that should go on cellular networks in preference even when + * @param uidList A list of uids that should go on cellular networks in preference even when * higher-priority networks are connected. */ - public static void setMobileDataPreferredApps(@NonNull Context context, @Nullable String list) { - Settings.Secure.putString(context.getContentResolver(), MOBILE_DATA_PREFERRED_APPS, list); + public static void setMobileDataPreferredUids(@NonNull Context context, + @NonNull Set<Integer> uidList) { + final StringJoiner joiner = new StringJoiner(";"); + for (Integer uid : uidList) { + if (uid < 0 || UserHandle.getAppId(uid) > Process.LAST_APPLICATION_UID) { + throw new IllegalArgumentException("Invalid uid"); + } + joiner.add(uid.toString()); + } + Settings.Secure.putString( + context.getContentResolver(), MOBILE_DATA_PREFERRED_UIDS, joiner.toString()); + } + + /** + * Get the list of apps(from {@link Settings}) that should be granted netd system permission for + * using restricted networks. + * + * @param context The {@link Context} to query the setting. + * @return A list of apps that should be granted netd system permission for using restricted + * networks or null if no setting value. + */ + @NonNull + public static Set<String> getRestrictedAllowedApps(@NonNull Context context) { + final String appList = Settings.Secure.getString( + context.getContentResolver(), RESTRICTED_ALLOWED_APPS); + if (TextUtils.isEmpty(appList)) { + return new ArraySet<>(); + } + return new ArraySet<>(appList.split(";")); + } + + /** + * Set the list of apps(from {@link Settings}) that should be granted netd system permission for + * using restricted networks. + * + * Note: Please refer to android developer guidelines for valid app(package name). + * https://developer.android.com/guide/topics/manifest/manifest-element.html#package + * + * @param context The {@link Context} to set the setting. + * @param list A list of apps that should be granted netd system permission for using + * restricted networks. + */ + public static void setRestrictedAllowedApps(@NonNull Context context, + @NonNull Set<String> list) { + final Pattern appPattern = Pattern.compile("[a-zA-Z_0-9]+([.][a-zA-Z_0-9]+)*"); + final StringJoiner joiner = new StringJoiner(";"); + for (String app : list) { + if (!appPattern.matcher(app).matches()) { + throw new IllegalArgumentException("Invalid app(package name)"); + } + joiner.add(app); + } + Settings.Secure.putString( + context.getContentResolver(), RESTRICTED_ALLOWED_APPS, joiner.toString()); } } diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java index 614dfc1ec081..2e4d8f82843f 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java @@ -139,19 +139,13 @@ public final class NetworkCapabilities implements Parcelable { */ private String mRequestorPackageName; - /** - * Indicates what fields should be redacted from this instance. - */ - private final @RedactionType long mRedactions; - public NetworkCapabilities() { - mRedactions = REDACT_ALL; clearAll(); mNetworkCapabilities = DEFAULT_CAPABILITIES; } public NetworkCapabilities(NetworkCapabilities nc) { - this(nc, REDACT_ALL); + this(nc, REDACT_NONE); } /** @@ -163,10 +157,12 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public NetworkCapabilities(@Nullable NetworkCapabilities nc, @RedactionType long redactions) { - mRedactions = redactions; if (nc != null) { set(nc); } + if (mTransportInfo != null) { + mTransportInfo = nc.mTransportInfo.makeCopy(redactions); + } } /** @@ -175,14 +171,6 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public void clearAll() { - // Ensures that the internal copies maintained by the connectivity stack does not set it to - // anything other than |REDACT_ALL|. - if (mRedactions != REDACT_ALL) { - // This is needed because the current redaction mechanism relies on redaction while - // parceling. - throw new UnsupportedOperationException( - "Cannot clear NetworkCapabilities when mRedactions is set"); - } mNetworkCapabilities = mTransportTypes = mForbiddenNetworkCapabilities = 0; mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; mNetworkSpecifier = null; @@ -211,7 +199,7 @@ public final class NetworkCapabilities implements Parcelable { mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps; mNetworkSpecifier = nc.mNetworkSpecifier; if (nc.getTransportInfo() != null) { - setTransportInfo(nc.getTransportInfo().makeCopy(mRedactions)); + setTransportInfo(nc.getTransportInfo()); } else { setTransportInfo(null); } diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp index 20ccf060118c..813a239bdaf2 100644 --- a/packages/Connectivity/service/Android.bp +++ b/packages/Connectivity/service/Android.bp @@ -54,7 +54,7 @@ java_library { sdk_version: "system_server_current", min_sdk_version: "30", srcs: [ - ":connectivity-service-srcs", + "src/**/*.java", ":framework-connectivity-shared-srcs", ":services-connectivity-shared-srcs", // TODO: move to net-utils-device-common, enable shrink optimization to avoid extra classes diff --git a/packages/Connectivity/service/lint-baseline.xml b/packages/Connectivity/service/lint-baseline.xml index 35ea2d378e93..95c169ce64a3 100644 --- a/packages/Connectivity/service/lint-baseline.xml +++ b/packages/Connectivity/service/lint-baseline.xml @@ -7,8 +7,8 @@ errorLine1=" if (tm.isDataCapable()) {" errorLine2=" ~~~~~~~~~~~~~"> <location - file="frameworks/base/services/core/java/com/android/server/ConnectivityService.java" - line="781" + file="frameworks/base/packages/Connectivity/service/src/com/android/server/ConnectivityService.java" + line="787" column="20"/> </issue> @@ -18,8 +18,8 @@ errorLine1=" mUserAllContext.sendStickyBroadcast(intent, options);" errorLine2=" ~~~~~~~~~~~~~~~~~~~"> <location - file="frameworks/base/services/core/java/com/android/server/ConnectivityService.java" - line="2633" + file="frameworks/base/packages/Connectivity/service/src/com/android/server/ConnectivityService.java" + line="2681" column="33"/> </issue> @@ -29,8 +29,8 @@ errorLine1=" final int callingVersion = pm.getTargetSdkVersion(callingPackageName);" errorLine2=" ~~~~~~~~~~~~~~~~~~~"> <location - file="frameworks/base/services/core/java/com/android/server/ConnectivityService.java" - line="5784" + file="frameworks/base/packages/Connectivity/service/src/com/android/server/ConnectivityService.java" + line="5851" column="43"/> </issue> diff --git a/services/core/java/com/android/server/ConnectivityService.java b/packages/Connectivity/service/src/com/android/server/ConnectivityService.java index ae14e37c8329..ed2fe822de17 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/packages/Connectivity/service/src/com/android/server/ConnectivityService.java @@ -133,6 +133,8 @@ import android.net.IpMemoryStore; import android.net.IpPrefix; import android.net.LinkProperties; import android.net.MatchAllNetworkSpecifier; +import android.net.NativeNetworkConfig; +import android.net.NativeNetworkType; import android.net.NattSocketKeepalive; import android.net.Network; import android.net.NetworkAgent; @@ -3821,36 +3823,43 @@ public class ConnectivityService extends IConnectivityManager.Stub nai.onNetworkDestroyed(); } - private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) { + private boolean createNativeNetwork(@NonNull NetworkAgentInfo nai) { try { // This should never fail. Specifying an already in use NetID will cause failure. - if (networkAgent.isVPN()) { - mNetd.networkCreateVpn(networkAgent.network.getNetId(), - (networkAgent.networkAgentConfig == null - || !networkAgent.networkAgentConfig.allowBypass)); + final NativeNetworkConfig config; + if (nai.isVPN()) { + if (getVpnType(nai) == VpnManager.TYPE_VPN_NONE) { + Log.wtf(TAG, "Unable to get VPN type from network " + nai.toShortString()); + return false; + } + config = new NativeNetworkConfig(nai.network.getNetId(), NativeNetworkType.VIRTUAL, + INetd.PERMISSION_NONE, + (nai.networkAgentConfig == null || !nai.networkAgentConfig.allowBypass), + getVpnType(nai)); } else { - mNetd.networkCreatePhysical(networkAgent.network.getNetId(), - getNetworkPermission(networkAgent.networkCapabilities)); - } - mDnsResolver.createNetworkCache(networkAgent.network.getNetId()); - mDnsManager.updateTransportsForNetwork(networkAgent.network.getNetId(), - networkAgent.networkCapabilities.getTransportTypes()); + config = new NativeNetworkConfig(nai.network.getNetId(), NativeNetworkType.PHYSICAL, + getNetworkPermission(nai.networkCapabilities), /*secure=*/ false, + VpnManager.TYPE_VPN_NONE); + } + mNetd.networkCreate(config); + mDnsResolver.createNetworkCache(nai.network.getNetId()); + mDnsManager.updateTransportsForNetwork(nai.network.getNetId(), + nai.networkCapabilities.getTransportTypes()); return true; } catch (RemoteException | ServiceSpecificException e) { - loge("Error creating network " + networkAgent.network.getNetId() + ": " - + e.getMessage()); + loge("Error creating network " + nai.toShortString() + ": " + e.getMessage()); return false; } } - private void destroyNativeNetwork(@NonNull NetworkAgentInfo networkAgent) { + private void destroyNativeNetwork(@NonNull NetworkAgentInfo nai) { try { - mNetd.networkDestroy(networkAgent.network.getNetId()); + mNetd.networkDestroy(nai.network.getNetId()); } catch (RemoteException | ServiceSpecificException e) { loge("Exception destroying network(networkDestroy): " + e); } try { - mDnsResolver.destroyNetworkCache(networkAgent.network.getNetId()); + mDnsResolver.destroyNetworkCache(nai.network.getNetId()); } catch (RemoteException | ServiceSpecificException e) { loge("Exception destroying network: " + e); } @@ -9134,7 +9143,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } private NetworkCapabilities getNetworkCapabilitiesWithoutUids(@NonNull NetworkCapabilities nc) { - final NetworkCapabilities sanitized = new NetworkCapabilities(nc); + final NetworkCapabilities sanitized = new NetworkCapabilities(nc, + NetworkCapabilities.REDACT_ALL); sanitized.setUids(null); sanitized.setAdministratorUids(new int[0]); sanitized.setOwnerUid(Process.INVALID_UID); diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/packages/Connectivity/service/src/com/android/server/ConnectivityServiceInitializer.java index 2465479aadd8..2465479aadd8 100644 --- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java +++ b/packages/Connectivity/service/src/com/android/server/ConnectivityServiceInitializer.java diff --git a/services/core/java/com/android/server/NetIdManager.java b/packages/Connectivity/service/src/com/android/server/NetIdManager.java index 61925c80a22b..61925c80a22b 100644 --- a/services/core/java/com/android/server/NetIdManager.java +++ b/packages/Connectivity/service/src/com/android/server/NetIdManager.java diff --git a/services/core/java/com/android/server/TestNetworkService.java b/packages/Connectivity/service/src/com/android/server/TestNetworkService.java index 09873f4db045..09873f4db045 100644 --- a/services/core/java/com/android/server/TestNetworkService.java +++ b/packages/Connectivity/service/src/com/android/server/TestNetworkService.java diff --git a/services/core/java/com/android/server/connectivity/AutodestructReference.java b/packages/Connectivity/service/src/com/android/server/connectivity/AutodestructReference.java index 009a43e58285..009a43e58285 100644 --- a/services/core/java/com/android/server/connectivity/AutodestructReference.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/AutodestructReference.java diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/packages/Connectivity/service/src/com/android/server/connectivity/DnsManager.java index 05b12bad5589..05b12bad5589 100644 --- a/services/core/java/com/android/server/connectivity/DnsManager.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/DnsManager.java diff --git a/services/core/java/com/android/server/connectivity/FullScore.java b/packages/Connectivity/service/src/com/android/server/connectivity/FullScore.java index fbfa7a18c9d8..fbfa7a18c9d8 100644 --- a/services/core/java/com/android/server/connectivity/FullScore.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/FullScore.java diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/packages/Connectivity/service/src/com/android/server/connectivity/KeepaliveTracker.java index acf39f05a541..acf39f05a541 100644 --- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/KeepaliveTracker.java diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/packages/Connectivity/service/src/com/android/server/connectivity/LingerMonitor.java index 032612c6f093..032612c6f093 100644 --- a/services/core/java/com/android/server/connectivity/LingerMonitor.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/LingerMonitor.java diff --git a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java b/packages/Connectivity/service/src/com/android/server/connectivity/MockableSystemProperties.java index a25b89ac039a..a25b89ac039a 100644 --- a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/MockableSystemProperties.java diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/packages/Connectivity/service/src/com/android/server/connectivity/Nat464Xlat.java index c66a280f2b02..c66a280f2b02 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/Nat464Xlat.java diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkAgentInfo.java index 18becd45f6d8..18becd45f6d8 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkAgentInfo.java diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkDiagnostics.java index 2e51be39bfae..2e51be39bfae 100644 --- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkDiagnostics.java diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkNotificationManager.java index 3dc79c51d8a9..3dc79c51d8a9 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkNotificationManager.java diff --git a/services/core/java/com/android/server/connectivity/NetworkOffer.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkOffer.java index 8285e7a8a3cb..8285e7a8a3cb 100644 --- a/services/core/java/com/android/server/connectivity/NetworkOffer.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkOffer.java diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkRanker.java index e8398373a4fc..e8398373a4fc 100644 --- a/services/core/java/com/android/server/connectivity/NetworkRanker.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/NetworkRanker.java diff --git a/services/core/java/com/android/server/connectivity/OsCompat.java b/packages/Connectivity/service/src/com/android/server/connectivity/OsCompat.java index 57e3dcdf0d73..57e3dcdf0d73 100644 --- a/services/core/java/com/android/server/connectivity/OsCompat.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/OsCompat.java diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/packages/Connectivity/service/src/com/android/server/connectivity/PermissionMonitor.java index 5886b1ad9a0f..5886b1ad9a0f 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/PermissionMonitor.java diff --git a/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java b/packages/Connectivity/service/src/com/android/server/connectivity/ProfileNetworkPreferences.java index dd2815d9e2e3..dd2815d9e2e3 100644 --- a/services/core/java/com/android/server/connectivity/ProfileNetworkPreferences.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/ProfileNetworkPreferences.java diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/packages/Connectivity/service/src/com/android/server/connectivity/ProxyTracker.java index bc0929c20659..bc0929c20659 100644 --- a/services/core/java/com/android/server/connectivity/ProxyTracker.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/ProxyTracker.java diff --git a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java b/packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackAgentConnection.java index 534dbe7699a7..534dbe7699a7 100644 --- a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackAgentConnection.java diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackTracker.java index b6ab47b276e3..b6ab47b276e3 100644 --- a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/QosCallbackTracker.java diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/packages/Connectivity/service/src/com/android/server/connectivity/TcpKeepaliveController.java index 73f34751731e..73f34751731e 100644 --- a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java +++ b/packages/Connectivity/service/src/com/android/server/connectivity/TcpKeepaliveController.java diff --git a/tests/net/OWNERS b/packages/Connectivity/tests/OWNERS index d3836d4c6c57..d3836d4c6c57 100644 --- a/tests/net/OWNERS +++ b/packages/Connectivity/tests/OWNERS diff --git a/tests/net/TEST_MAPPING b/packages/Connectivity/tests/TEST_MAPPING index 502f885ceb78..502f885ceb78 100644 --- a/tests/net/TEST_MAPPING +++ b/packages/Connectivity/tests/TEST_MAPPING diff --git a/tests/net/common/Android.bp b/packages/Connectivity/tests/common/Android.bp index 439665b67ab7..439665b67ab7 100644 --- a/tests/net/common/Android.bp +++ b/packages/Connectivity/tests/common/Android.bp diff --git a/tests/net/common/java/ParseExceptionTest.kt b/packages/Connectivity/tests/common/java/ParseExceptionTest.kt index b702d61a9fe1..b702d61a9fe1 100644 --- a/tests/net/common/java/ParseExceptionTest.kt +++ b/packages/Connectivity/tests/common/java/ParseExceptionTest.kt diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/packages/Connectivity/tests/common/java/android/net/CaptivePortalDataTest.kt index 18a93319b271..18a93319b271 100644 --- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/CaptivePortalDataTest.kt diff --git a/tests/net/common/java/android/net/CaptivePortalTest.java b/packages/Connectivity/tests/common/java/android/net/CaptivePortalTest.java index 15d3398d43c0..15d3398d43c0 100644 --- a/tests/net/common/java/android/net/CaptivePortalTest.java +++ b/packages/Connectivity/tests/common/java/android/net/CaptivePortalTest.java diff --git a/tests/net/common/java/android/net/DependenciesTest.java b/packages/Connectivity/tests/common/java/android/net/DependenciesTest.java index ac1c28a45462..ac1c28a45462 100644 --- a/tests/net/common/java/android/net/DependenciesTest.java +++ b/packages/Connectivity/tests/common/java/android/net/DependenciesTest.java diff --git a/tests/net/common/java/android/net/DhcpInfoTest.java b/packages/Connectivity/tests/common/java/android/net/DhcpInfoTest.java index ab4726bab573..ab4726bab573 100644 --- a/tests/net/common/java/android/net/DhcpInfoTest.java +++ b/packages/Connectivity/tests/common/java/android/net/DhcpInfoTest.java diff --git a/tests/net/common/java/android/net/IpPrefixTest.java b/packages/Connectivity/tests/common/java/android/net/IpPrefixTest.java index 50ecb428359e..50ecb428359e 100644 --- a/tests/net/common/java/android/net/IpPrefixTest.java +++ b/packages/Connectivity/tests/common/java/android/net/IpPrefixTest.java diff --git a/tests/net/common/java/android/net/KeepalivePacketDataTest.kt b/packages/Connectivity/tests/common/java/android/net/KeepalivePacketDataTest.kt index f464ec6cf0e5..f464ec6cf0e5 100644 --- a/tests/net/common/java/android/net/KeepalivePacketDataTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/KeepalivePacketDataTest.kt diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/packages/Connectivity/tests/common/java/android/net/LinkAddressTest.java index 2cf3cf9c11da..2cf3cf9c11da 100644 --- a/tests/net/common/java/android/net/LinkAddressTest.java +++ b/packages/Connectivity/tests/common/java/android/net/LinkAddressTest.java diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/packages/Connectivity/tests/common/java/android/net/LinkPropertiesTest.java index 550953d0612d..550953d0612d 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/packages/Connectivity/tests/common/java/android/net/LinkPropertiesTest.java diff --git a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt b/packages/Connectivity/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt index a5e44d59fcab..a5e44d59fcab 100644 --- a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/MatchAllNetworkSpecifierTest.kt diff --git a/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt b/packages/Connectivity/tests/common/java/android/net/NattKeepalivePacketDataTest.kt index 46f39dd016fd..46f39dd016fd 100644 --- a/tests/net/common/java/android/net/NattKeepalivePacketDataTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/NattKeepalivePacketDataTest.kt diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkAgentConfigTest.kt index 2b45b3d69ce9..2b45b3d69ce9 100644 --- a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/NetworkAgentConfigTest.kt diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java index 1c8a1bfeb417..9efdde4da0c0 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java @@ -341,7 +341,7 @@ public class NetworkCapabilitiesTest { private void testParcelSane(NetworkCapabilities cap) { if (isAtLeastS()) { - assertParcelSane(cap, 17); + assertParcelSane(cap, 16); } else if (isAtLeastR()) { assertParcelSane(cap, 15); } else { diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkProviderTest.kt index 7424157bea74..7424157bea74 100644 --- a/tests/net/common/java/android/net/NetworkProviderTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/NetworkProviderTest.kt diff --git a/tests/net/common/java/android/net/NetworkSpecifierTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkSpecifierTest.kt index f3409f53596f..f3409f53596f 100644 --- a/tests/net/common/java/android/net/NetworkSpecifierTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/NetworkSpecifierTest.kt diff --git a/tests/net/common/java/android/net/NetworkStackTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkStackTest.java index f8f9c72374ad..f8f9c72374ad 100644 --- a/tests/net/common/java/android/net/NetworkStackTest.java +++ b/packages/Connectivity/tests/common/java/android/net/NetworkStackTest.java diff --git a/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt b/packages/Connectivity/tests/common/java/android/net/NetworkStateSnapshotTest.kt index 0ca4d9551f39..0ca4d9551f39 100644 --- a/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/NetworkStateSnapshotTest.kt diff --git a/tests/net/common/java/android/net/NetworkTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkTest.java index 7423c733c6eb..7423c733c6eb 100644 --- a/tests/net/common/java/android/net/NetworkTest.java +++ b/packages/Connectivity/tests/common/java/android/net/NetworkTest.java diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/packages/Connectivity/tests/common/java/android/net/OemNetworkPreferencesTest.java index fd29a9539de8..fd29a9539de8 100644 --- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java +++ b/packages/Connectivity/tests/common/java/android/net/OemNetworkPreferencesTest.java diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/packages/Connectivity/tests/common/java/android/net/RouteInfoTest.java index 71689f919726..71689f919726 100644 --- a/tests/net/common/java/android/net/RouteInfoTest.java +++ b/packages/Connectivity/tests/common/java/android/net/RouteInfoTest.java diff --git a/tests/net/common/java/android/net/StaticIpConfigurationTest.java b/packages/Connectivity/tests/common/java/android/net/StaticIpConfigurationTest.java index b5f23bf19a3c..b5f23bf19a3c 100644 --- a/tests/net/common/java/android/net/StaticIpConfigurationTest.java +++ b/packages/Connectivity/tests/common/java/android/net/StaticIpConfigurationTest.java diff --git a/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt b/packages/Connectivity/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt index 7a18bb08faa8..7a18bb08faa8 100644 --- a/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/TcpKeepalivePacketDataTest.kt diff --git a/tests/net/common/java/android/net/UidRangeTest.java b/packages/Connectivity/tests/common/java/android/net/UidRangeTest.java index 1b1c95431d6f..1b1c95431d6f 100644 --- a/tests/net/common/java/android/net/UidRangeTest.java +++ b/packages/Connectivity/tests/common/java/android/net/UidRangeTest.java diff --git a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt b/packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt index f23ba26d0039..f23ba26d0039 100644 --- a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/UnderlyingNetworkInfoTest.kt diff --git a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java b/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java index d50406fd3a1c..d50406fd3a1c 100644 --- a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java +++ b/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java diff --git a/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/ApfProgramEventTest.kt index 0b7b74097cc6..0b7b74097cc6 100644 --- a/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/metrics/ApfProgramEventTest.kt diff --git a/tests/net/common/java/android/net/metrics/ApfStatsTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/ApfStatsTest.kt index 46a8c8e5b509..46a8c8e5b509 100644 --- a/tests/net/common/java/android/net/metrics/ApfStatsTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/metrics/ApfStatsTest.kt diff --git a/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/DhcpClientEventTest.kt index 8d7a9c405024..8d7a9c405024 100644 --- a/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/metrics/DhcpClientEventTest.kt diff --git a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/DhcpErrorEventTest.kt index 236f72eafbdc..236f72eafbdc 100644 --- a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/metrics/DhcpErrorEventTest.kt diff --git a/tests/net/common/java/android/net/metrics/IpConnectivityLogTest.java b/packages/Connectivity/tests/common/java/android/net/metrics/IpConnectivityLogTest.java index d4780d3a5d7b..d4780d3a5d7b 100644 --- a/tests/net/common/java/android/net/metrics/IpConnectivityLogTest.java +++ b/packages/Connectivity/tests/common/java/android/net/metrics/IpConnectivityLogTest.java diff --git a/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/IpManagerEventTest.kt index 64be50837fc9..64be50837fc9 100644 --- a/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/metrics/IpManagerEventTest.kt diff --git a/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt index 55b5e492dd47..55b5e492dd47 100644 --- a/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/metrics/IpReachabilityEventTest.kt diff --git a/tests/net/common/java/android/net/metrics/NetworkEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/NetworkEventTest.kt index 41430b03a1eb..41430b03a1eb 100644 --- a/tests/net/common/java/android/net/metrics/NetworkEventTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/metrics/NetworkEventTest.kt diff --git a/tests/net/common/java/android/net/metrics/RaEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/RaEventTest.kt index d9b720332fbe..d9b720332fbe 100644 --- a/tests/net/common/java/android/net/metrics/RaEventTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/metrics/RaEventTest.kt diff --git a/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt b/packages/Connectivity/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt index 51c0d41bf4d5..51c0d41bf4d5 100644 --- a/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/metrics/ValidationProbeEventTest.kt diff --git a/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt b/packages/Connectivity/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt index 7b22e45db90a..7b22e45db90a 100644 --- a/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/netstats/NetworkStatsApiTest.kt diff --git a/tests/net/common/java/android/net/util/SocketUtilsTest.kt b/packages/Connectivity/tests/common/java/android/net/util/SocketUtilsTest.kt index aaf97f36889b..aaf97f36889b 100644 --- a/tests/net/common/java/android/net/util/SocketUtilsTest.kt +++ b/packages/Connectivity/tests/common/java/android/net/util/SocketUtilsTest.kt diff --git a/tests/net/deflake/Android.bp b/packages/Connectivity/tests/deflake/Android.bp index 58ece37ef647..58ece37ef647 100644 --- a/tests/net/deflake/Android.bp +++ b/packages/Connectivity/tests/deflake/Android.bp diff --git a/tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt b/packages/Connectivity/tests/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt index 62855255fec2..62855255fec2 100644 --- a/tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt +++ b/packages/Connectivity/tests/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt diff --git a/tests/net/integration/Android.bp b/packages/Connectivity/tests/integration/Android.bp index 39c424e31f0e..39c424e31f0e 100644 --- a/tests/net/integration/Android.bp +++ b/packages/Connectivity/tests/integration/Android.bp diff --git a/tests/net/integration/AndroidManifest.xml b/packages/Connectivity/tests/integration/AndroidManifest.xml index 2e1368935759..2e1368935759 100644 --- a/tests/net/integration/AndroidManifest.xml +++ b/packages/Connectivity/tests/integration/AndroidManifest.xml diff --git a/tests/net/integration/res/values/config.xml b/packages/Connectivity/tests/integration/res/values/config.xml index 2c8046ffd781..2c8046ffd781 100644 --- a/tests/net/integration/res/values/config.xml +++ b/packages/Connectivity/tests/integration/res/values/config.xml diff --git a/tests/net/integration/src/android/net/TestNetworkStackClient.kt b/packages/Connectivity/tests/integration/src/android/net/TestNetworkStackClient.kt index 61ef5bdca487..61ef5bdca487 100644 --- a/tests/net/integration/src/android/net/TestNetworkStackClient.kt +++ b/packages/Connectivity/tests/integration/src/android/net/TestNetworkStackClient.kt diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt index e039ef072542..e039ef072542 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt +++ b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl index 9a2bcfea7641..9a2bcfea7641 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl +++ b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt index e2063138fef1..e2063138fef1 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt +++ b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/HttpResponse.kt diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl index efc58add9cf5..efc58add9cf5 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl +++ b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt index e807952cec11..e807952cec11 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt +++ b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt index eff66584d6c1..eff66584d6c1 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt +++ b/packages/Connectivity/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt diff --git a/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt b/packages/Connectivity/tests/integration/util/com/android/server/ConnectivityServiceTestUtils.kt index 165fd3728281..165fd3728281 100644 --- a/tests/net/integration/util/com/android/server/ConnectivityServiceTestUtils.kt +++ b/packages/Connectivity/tests/integration/util/com/android/server/ConnectivityServiceTestUtils.kt diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/packages/Connectivity/tests/integration/util/com/android/server/NetworkAgentWrapper.java index 40d068d7e324..17db17923f4d 100644 --- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java +++ b/packages/Connectivity/tests/integration/util/com/android/server/NetworkAgentWrapper.java @@ -381,4 +381,8 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { } } } + + public boolean isBypassableVpn() { + return mNetworkAgentConfig.isBypassableVpn(); + } } diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/packages/Connectivity/tests/integration/util/com/android/server/TestNetIdManager.kt index 938a694e8ba9..938a694e8ba9 100644 --- a/tests/net/integration/util/com/android/server/TestNetIdManager.kt +++ b/packages/Connectivity/tests/integration/util/com/android/server/TestNetIdManager.kt diff --git a/tests/net/smoketest/Android.bp b/packages/Connectivity/tests/smoketest/Android.bp index 1535f3ddcb38..1535f3ddcb38 100644 --- a/tests/net/smoketest/Android.bp +++ b/packages/Connectivity/tests/smoketest/Android.bp diff --git a/tests/net/smoketest/AndroidManifest.xml b/packages/Connectivity/tests/smoketest/AndroidManifest.xml index f1b9febb9f57..f1b9febb9f57 100644 --- a/tests/net/smoketest/AndroidManifest.xml +++ b/packages/Connectivity/tests/smoketest/AndroidManifest.xml diff --git a/tests/net/smoketest/AndroidTest.xml b/packages/Connectivity/tests/smoketest/AndroidTest.xml index ac366e4ac544..ac366e4ac544 100644 --- a/tests/net/smoketest/AndroidTest.xml +++ b/packages/Connectivity/tests/smoketest/AndroidTest.xml diff --git a/tests/net/smoketest/java/SmokeTest.java b/packages/Connectivity/tests/smoketest/java/SmokeTest.java index 7d6655fde15e..7d6655fde15e 100644 --- a/tests/net/smoketest/java/SmokeTest.java +++ b/packages/Connectivity/tests/smoketest/java/SmokeTest.java diff --git a/tests/net/Android.bp b/packages/Connectivity/tests/unit/Android.bp index 6f503ace037f..6f503ace037f 100644 --- a/tests/net/Android.bp +++ b/packages/Connectivity/tests/unit/Android.bp diff --git a/tests/net/AndroidManifest.xml b/packages/Connectivity/tests/unit/AndroidManifest.xml index 4c60ccf60615..4c60ccf60615 100644 --- a/tests/net/AndroidManifest.xml +++ b/packages/Connectivity/tests/unit/AndroidManifest.xml diff --git a/tests/net/AndroidTest.xml b/packages/Connectivity/tests/unit/AndroidTest.xml index 939ae493b280..939ae493b280 100644 --- a/tests/net/AndroidTest.xml +++ b/packages/Connectivity/tests/unit/AndroidTest.xml diff --git a/tests/net/jarjar-rules.txt b/packages/Connectivity/tests/unit/jarjar-rules.txt index ca8867206dda..ca8867206dda 100644 --- a/tests/net/jarjar-rules.txt +++ b/packages/Connectivity/tests/unit/jarjar-rules.txt diff --git a/tests/net/java/android/app/usage/NetworkStatsManagerTest.java b/packages/Connectivity/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java index 899295a019d2..899295a019d2 100644 --- a/tests/net/java/android/app/usage/NetworkStatsManagerTest.java +++ b/packages/Connectivity/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/ConnectivityDiagnosticsManagerTest.java index 06e9405a6a79..06e9405a6a79 100644 --- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/ConnectivityDiagnosticsManagerTest.java diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/ConnectivityManagerTest.java index 591e0cc3504e..591e0cc3504e 100644 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/ConnectivityManagerTest.java diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java index 1abd39a32bdf..1abd39a32bdf 100644 --- a/tests/net/java/android/net/Ikev2VpnProfileTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/packages/Connectivity/tests/unit/java/android/net/IpMemoryStoreTest.java index 0b13800bc5c9..0b13800bc5c9 100644 --- a/tests/net/java/android/net/IpMemoryStoreTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/IpMemoryStoreTest.java diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecAlgorithmTest.java index 5bd221477412..5bd221477412 100644 --- a/tests/net/java/android/net/IpSecAlgorithmTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/IpSecAlgorithmTest.java diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecConfigTest.java index 25e225ef303a..25e225ef303a 100644 --- a/tests/net/java/android/net/IpSecConfigTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/IpSecConfigTest.java diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecManagerTest.java index 730e2d56bd78..730e2d56bd78 100644 --- a/tests/net/java/android/net/IpSecManagerTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/IpSecManagerTest.java diff --git a/tests/net/java/android/net/IpSecTransformTest.java b/packages/Connectivity/tests/unit/java/android/net/IpSecTransformTest.java index 424f23dbbaf6..424f23dbbaf6 100644 --- a/tests/net/java/android/net/IpSecTransformTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/IpSecTransformTest.java diff --git a/tests/net/java/android/net/KeepalivePacketDataUtilTest.java b/packages/Connectivity/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java index fc739fbfac61..fc739fbfac61 100644 --- a/tests/net/java/android/net/KeepalivePacketDataUtilTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java diff --git a/tests/net/java/android/net/MacAddressTest.java b/packages/Connectivity/tests/unit/java/android/net/MacAddressTest.java index 6de31f6b4be1..6de31f6b4be1 100644 --- a/tests/net/java/android/net/MacAddressTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/MacAddressTest.java diff --git a/tests/net/java/android/net/NetworkIdentityTest.kt b/packages/Connectivity/tests/unit/java/android/net/NetworkIdentityTest.kt index eb2b85c14578..eb2b85c14578 100644 --- a/tests/net/java/android/net/NetworkIdentityTest.kt +++ b/packages/Connectivity/tests/unit/java/android/net/NetworkIdentityTest.kt diff --git a/tests/net/java/android/net/NetworkStatsHistoryTest.java b/packages/Connectivity/tests/unit/java/android/net/NetworkStatsHistoryTest.java index 13558cd51c28..13558cd51c28 100644 --- a/tests/net/java/android/net/NetworkStatsHistoryTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/NetworkStatsHistoryTest.java diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/packages/Connectivity/tests/unit/java/android/net/NetworkStatsTest.java index 23d5a7e5d5f8..23d5a7e5d5f8 100644 --- a/tests/net/java/android/net/NetworkStatsTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/NetworkStatsTest.java diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt index ab6b2f409867..ab6b2f409867 100644 --- a/tests/net/java/android/net/NetworkTemplateTest.kt +++ b/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt diff --git a/tests/net/java/android/net/NetworkUtilsTest.java b/packages/Connectivity/tests/unit/java/android/net/NetworkUtilsTest.java index 7748288aeb05..7748288aeb05 100644 --- a/tests/net/java/android/net/NetworkUtilsTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/NetworkUtilsTest.java diff --git a/tests/net/java/android/net/QosSocketFilterTest.java b/packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java index ad58960eaadd..ad58960eaadd 100644 --- a/tests/net/java/android/net/QosSocketFilterTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/QosSocketFilterTest.java diff --git a/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java b/packages/Connectivity/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java index 6714bb1abbe6..6714bb1abbe6 100644 --- a/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java diff --git a/tests/net/java/android/net/VpnManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/VpnManagerTest.java index c548e30761c9..3135062138ac 100644 --- a/tests/net/java/android/net/VpnManagerTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/VpnManagerTest.java @@ -28,11 +28,13 @@ import static org.mockito.Mockito.when; import android.content.ComponentName; import android.content.Intent; import android.test.mock.MockContext; +import android.util.SparseArray; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.net.VpnProfile; +import com.android.internal.util.MessageUtils; import org.junit.Before; import org.junit.Test; @@ -119,4 +121,18 @@ public class VpnManagerTest { .setAuthPsk(PSK_BYTES) .build(); } + + @Test + public void testVpnTypesEqual() throws Exception { + SparseArray<String> vmVpnTypes = MessageUtils.findMessageNames( + new Class[] { VpnManager.class }, new String[]{ "TYPE_VPN_" }); + SparseArray<String> nativeVpnType = MessageUtils.findMessageNames( + new Class[] { NativeVpnType.class }, new String[]{ "" }); + + // TYPE_VPN_NONE = -1 is only defined in VpnManager. + assertEquals(vmVpnTypes.size() - 1, nativeVpnType.size()); + for (int i = VpnManager.TYPE_VPN_SERVICE; i < vmVpnTypes.size(); i++) { + assertEquals(vmVpnTypes.get(i), "TYPE_VPN_" + nativeVpnType.get(i)); + } + } } diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/packages/Connectivity/tests/unit/java/android/net/VpnTransportInfoTest.java index ccaa5cf7e9f7..ccaa5cf7e9f7 100644 --- a/tests/net/java/android/net/VpnTransportInfoTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/VpnTransportInfoTest.java diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/packages/Connectivity/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java index 603c87519532..603c87519532 100644 --- a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java +++ b/packages/Connectivity/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java diff --git a/tests/net/java/android/net/nsd/NsdManagerTest.java b/packages/Connectivity/tests/unit/java/android/net/nsd/NsdManagerTest.java index b0a9b8a55322..b0a9b8a55322 100644 --- a/tests/net/java/android/net/nsd/NsdManagerTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/nsd/NsdManagerTest.java diff --git a/tests/net/java/android/net/nsd/NsdServiceInfoTest.java b/packages/Connectivity/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java index 94dfc7515c67..94dfc7515c67 100644 --- a/tests/net/java/android/net/nsd/NsdServiceInfoTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java diff --git a/tests/net/java/android/net/util/DnsUtilsTest.java b/packages/Connectivity/tests/unit/java/android/net/util/DnsUtilsTest.java index b626db8d89e4..b626db8d89e4 100644 --- a/tests/net/java/android/net/util/DnsUtilsTest.java +++ b/packages/Connectivity/tests/unit/java/android/net/util/DnsUtilsTest.java diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/packages/Connectivity/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt index 5006d5345f5f..5006d5345f5f 100644 --- a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt +++ b/packages/Connectivity/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/packages/Connectivity/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt index 25aa6266577e..25aa6266577e 100644 --- a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt +++ b/packages/Connectivity/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt diff --git a/tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java index 3cfecd552967..3cfecd552967 100644 --- a/tests/net/java/com/android/internal/net/NetworkUtilsInternalTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java index 46597d19ef1b..46597d19ef1b 100644 --- a/tests/net/java/com/android/internal/net/VpnProfileTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java diff --git a/tests/net/java/com/android/internal/util/BitUtilsTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/util/BitUtilsTest.java index d2fbdce9771a..d2fbdce9771a 100644 --- a/tests/net/java/com/android/internal/util/BitUtilsTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/internal/util/BitUtilsTest.java diff --git a/tests/net/java/com/android/internal/util/RingBufferTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/util/RingBufferTest.java index d06095a690cf..d06095a690cf 100644 --- a/tests/net/java/com/android/internal/util/RingBufferTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/internal/util/RingBufferTest.java diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java index 6702869f511e..63501d7662ed 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -19,6 +19,7 @@ package com.android.server; import static android.Manifest.permission.CHANGE_NETWORK_STATE; import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; import static android.Manifest.permission.DUMP; +import static android.Manifest.permission.LOCAL_MAC_ADDRESS; import static android.Manifest.permission.NETWORK_FACTORY; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.app.PendingIntent.FLAG_IMMUTABLE; @@ -212,6 +213,8 @@ import android.net.IpSecManager.UdpEncapsulationSocket; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.MatchAllNetworkSpecifier; +import android.net.NativeNetworkConfig; +import android.net.NativeNetworkType; import android.net.Network; import android.net.NetworkAgent; import android.net.NetworkAgentConfig; @@ -1240,6 +1243,8 @@ public class ConnectivityServiceTest { verify(mMockNetd, never()) .networkRemoveUidRanges(eq(mMockVpn.getNetwork().getNetId()), any()); mAgentRegistered = true; + verify(mMockNetd).networkCreate(nativeNetworkConfigVpn(getNetwork().netId, + !mMockNetworkAgent.isBypassableVpn(), mVpnType)); updateState(NetworkInfo.DetailedState.CONNECTED, "registerAgent"); mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities()); mNetworkAgent = mMockNetworkAgent.getNetworkAgent(); @@ -2829,6 +2834,16 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(callback); } + private NativeNetworkConfig nativeNetworkConfigPhysical(int netId, int permission) { + return new NativeNetworkConfig(netId, NativeNetworkType.PHYSICAL, permission, + /*secure=*/ false, VpnManager.TYPE_VPN_NONE); + } + + private NativeNetworkConfig nativeNetworkConfigVpn(int netId, boolean secure, int vpnType) { + return new NativeNetworkConfig(netId, NativeNetworkType.VIRTUAL, INetd.PERMISSION_NONE, + secure, vpnType); + } + @Test public void testNetworkAgentCallbacks() throws Exception { // Keeps track of the order of events that happen in this test. @@ -2850,8 +2865,8 @@ public class ConnectivityServiceTest { wifiNetwork.set(mWiFiNetworkAgent.getNetwork()); assertNotNull(wifiNetwork.get()); try { - verify(mMockNetd).networkCreatePhysical(wifiNetwork.get().getNetId(), - INetd.PERMISSION_NONE); + verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + wifiNetwork.get().getNetId(), INetd.PERMISSION_NONE)); } catch (RemoteException impossible) { fail(); } @@ -8405,7 +8420,8 @@ public class ConnectivityServiceTest { final int cellNetId = mCellNetworkAgent.getNetwork().netId; waitForIdle(); - verify(mMockNetd, times(1)).networkCreatePhysical(eq(cellNetId), anyInt()); + verify(mMockNetd, times(1)).networkCreate(nativeNetworkConfigPhysical(cellNetId, + INetd.PERMISSION_NONE)); assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute); verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId)); verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME); @@ -9470,9 +9486,9 @@ public class ConnectivityServiceTest { @Override public TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) { return new TestTransportInfo( - (redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0, - (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0, - (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0 + locationRedacted | (redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0, + localMacAddressRedacted | (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0, + settingsRedacted | (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0 ); } @@ -9495,8 +9511,26 @@ public class ConnectivityServiceTest { public int hashCode() { return Objects.hash(locationRedacted, localMacAddressRedacted, settingsRedacted); } + + @Override + public String toString() { + return String.format( + "TestTransportInfo{locationRedacted=%s macRedacted=%s settingsRedacted=%s}", + locationRedacted, localMacAddressRedacted, settingsRedacted); + } } + private TestTransportInfo getTestTransportInfo(NetworkCapabilities nc) { + return (TestTransportInfo) nc.getTransportInfo(); + } + + private TestTransportInfo getTestTransportInfo(TestNetworkAgentWrapper n) { + final NetworkCapabilities nc = mCm.getNetworkCapabilities(n.getNetwork()); + assertNotNull(nc); + return getTestTransportInfo(nc); + } + + private void verifyNetworkCallbackLocationDataInclusionUsingTransportInfoAndOwnerUidInNetCaps( @NonNull TestNetworkCallback wifiNetworkCallback, int actualOwnerUid, @NonNull TransportInfo actualTransportInfo, int expectedOwnerUid, @@ -9525,7 +9559,6 @@ public class ConnectivityServiceTest { wifiNetworkCallback.expectCapabilitiesThat(mWiFiNetworkAgent, nc -> Objects.equals(expectedOwnerUid, nc.getOwnerUid()) && Objects.equals(expectedTransportInfo, nc.getTransportInfo())); - } @Test @@ -9546,6 +9579,40 @@ public class ConnectivityServiceTest { wifiNetworkCallack, ownerUid, transportInfo, INVALID_UID, sanitizedTransportInfo); } + @Test + public void testTransportInfoRedactionInSynchronousCalls() throws Exception { + final NetworkCapabilities ncTemplate = new NetworkCapabilities() + .addTransportType(TRANSPORT_WIFI) + .setTransportInfo(new TestTransportInfo()); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(), + ncTemplate); + mWiFiNetworkAgent.connect(true /* validated; waits for callback */); + + // NETWORK_SETTINGS redaction is controlled by the NETWORK_SETTINGS permission + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted); + withPermission(NETWORK_SETTINGS, () -> { + assertFalse(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted); + }); + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).settingsRedacted); + + // LOCAL_MAC_ADDRESS redaction is controlled by the LOCAL_MAC_ADDRESS permission + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted); + withPermission(LOCAL_MAC_ADDRESS, () -> { + assertFalse(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted); + }); + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).localMacAddressRedacted); + + // Synchronous getNetworkCapabilities calls never return unredacted location-sensitive + // information. + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted); + setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted); + denyAllLocationPrivilegedPermissions(); + assertTrue(getTestTransportInfo(mWiFiNetworkAgent).locationRedacted); + } + private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType) throws Exception { final Set<UidRange> vpnRange = Collections.singleton(PRIMARY_UIDRANGE); @@ -9904,12 +9971,27 @@ public class ConnectivityServiceTest { // Connect the cell agent verify that it notifies TestNetworkCallback that it is available final TestNetworkCallback callback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(callback); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + + final NetworkCapabilities ncTemplate = new NetworkCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .setTransportInfo(new TestTransportInfo()); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, new LinkProperties(), + ncTemplate); mCellNetworkAgent.connect(true); callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); callback.assertNoCallback(); } + private boolean areConnDiagCapsRedacted(NetworkCapabilities nc) { + TestTransportInfo ti = (TestTransportInfo) nc.getTransportInfo(); + return nc.getUids() == null + && nc.getAdministratorUids().length == 0 + && nc.getOwnerUid() == Process.INVALID_UID + && getTestTransportInfo(nc).locationRedacted + && getTestTransportInfo(nc).localMacAddressRedacted + && getTestTransportInfo(nc).settingsRedacted; + } + @Test public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable() throws Exception { @@ -9920,12 +10002,7 @@ public class ConnectivityServiceTest { // Verify onConnectivityReport fired verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable( - argThat(report -> { - final NetworkCapabilities nc = report.getNetworkCapabilities(); - return nc.getUids() == null - && nc.getAdministratorUids().length == 0 - && nc.getOwnerUid() == Process.INVALID_UID; - })); + argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities()))); } @Test @@ -9941,12 +10018,7 @@ public class ConnectivityServiceTest { // Verify onDataStallSuspected fired verify(mConnectivityDiagnosticsCallback).onDataStallSuspected( - argThat(report -> { - final NetworkCapabilities nc = report.getNetworkCapabilities(); - return nc.getUids() == null - && nc.getAdministratorUids().length == 0 - && nc.getOwnerUid() == Process.INVALID_UID; - })); + argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities()))); } @Test @@ -12256,8 +12328,9 @@ public class ConnectivityServiceTest { mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId, - INetd.PERMISSION_NONE); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); + final TestOnCompleteListener listener = new TestOnCompleteListener(); mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, @@ -12284,8 +12357,8 @@ public class ConnectivityServiceTest { mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent); mSystemDefaultNetworkCallback.assertNoCallback(); mDefaultNetworkCallback.assertNoCallback(); - inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId, - INetd.PERMISSION_SYSTEM); + inOrder.verify(mMockNetd).networkCreate( + nativeNetworkConfigPhysical(workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM)); inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId, uidRangeFor(testHandle)); inOrder.verify(mMockNetd).networkRemoveUidRanges(mCellNetworkAgent.getNetwork().netId, @@ -12328,8 +12401,8 @@ public class ConnectivityServiceTest { mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); mProfileDefaultNetworkCallback.assertNoCallback(); - inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId, - INetd.PERMISSION_NONE); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); // When the agent disconnects, test that the app on the work profile falls back to the // default network. @@ -12359,8 +12432,8 @@ public class ConnectivityServiceTest { mProfileDefaultNetworkCallback.expectAvailableCallbacksUnvalidated(workAgent2); assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback); - inOrder.verify(mMockNetd).networkCreatePhysical(workAgent2.getNetwork().netId, - INetd.PERMISSION_SYSTEM); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + workAgent2.getNetwork().netId, INetd.PERMISSION_SYSTEM)); inOrder.verify(mMockNetd).networkAddUidRanges(workAgent2.getNetwork().netId, uidRangeFor(testHandle)); @@ -12405,8 +12478,8 @@ public class ConnectivityServiceTest { mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, r -> r.run(), listener); listener.expectOnComplete(); - inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId, - INetd.PERMISSION_NONE); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); inOrder.verify(mMockNetd).networkAddUidRanges(workAgent.getNetwork().netId, uidRangeFor(testHandle)); @@ -12458,10 +12531,10 @@ public class ConnectivityServiceTest { mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); mProfileDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); app4Cb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId, - INetd.PERMISSION_NONE); - inOrder.verify(mMockNetd).networkCreatePhysical(workAgent.getNetwork().netId, - INetd.PERMISSION_SYSTEM); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + workAgent.getNetwork().netId, INetd.PERMISSION_SYSTEM)); final TestOnCompleteListener listener = new TestOnCompleteListener(); mCm.setProfileNetworkPreference(testHandle2, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, @@ -12513,8 +12586,8 @@ public class ConnectivityServiceTest { mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE, r -> r.run(), listener); listener.expectOnComplete(); - inOrder.verify(mMockNetd).networkCreatePhysical(mCellNetworkAgent.getNetwork().netId, - INetd.PERMISSION_NONE); + inOrder.verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical( + mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE)); inOrder.verify(mMockNetd).networkAddUidRanges(mCellNetworkAgent.getNetwork().netId, uidRangeFor(testHandle)); diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java index cf2c9c783ac7..cf2c9c783ac7 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java diff --git a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java index 22a2c94fc194..22a2c94fc194 100644 --- a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceTest.java index 6232423b4f9e..6232423b4f9e 100644 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/IpSecServiceTest.java diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt index 5ec111954fcc..5ec111954fcc 100644 --- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt +++ b/packages/Connectivity/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt diff --git a/tests/net/java/com/android/server/NetIdManagerTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/NetIdManagerTest.kt index 6f5e740d344c..6f5e740d344c 100644 --- a/tests/net/java/com/android/server/NetIdManagerTest.kt +++ b/packages/Connectivity/tests/unit/java/com/android/server/NetIdManagerTest.kt diff --git a/tests/net/java/com/android/server/NetworkManagementServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/NetworkManagementServiceTest.java index 13516d75a50d..13516d75a50d 100644 --- a/tests/net/java/com/android/server/NetworkManagementServiceTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/NetworkManagementServiceTest.java diff --git a/tests/net/java/com/android/server/NsdServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/NsdServiceTest.java index a90fa6882c25..a90fa6882c25 100644 --- a/tests/net/java/com/android/server/NsdServiceTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/NsdServiceTest.java diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java index 0ffeec98cf90..0ffeec98cf90 100644 --- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java diff --git a/tests/net/java/com/android/server/connectivity/FullScoreTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt index 45b575a4365d..45b575a4365d 100644 --- a/tests/net/java/com/android/server/connectivity/FullScoreTest.kt +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java index 70495cced536..70495cced536 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java index 8b072c49de82..8b072c49de82 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java index 36e229d8aa73..36e229d8aa73 100644 --- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java diff --git a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MetricsTestUtil.java index 5064b9bd91b9..5064b9bd91b9 100644 --- a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MetricsTestUtil.java diff --git a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java index 38f6d7f3172d..38f6d7f3172d 100644 --- a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java index 9b2a638f8b39..9b2a638f8b39 100644 --- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java index 50aaaee24418..50aaaee24418 100644 --- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java index c353cea266bb..c353cea266bb 100644 --- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java diff --git a/tests/net/java/com/android/server/connectivity/NetworkOfferTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt index 409f8c309935..409f8c309935 100644 --- a/tests/net/java/com/android/server/connectivity/NetworkOfferTest.kt +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt index 551b94c7668e..551b94c7668e 100644 --- a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java index 02a58080fefd..02a58080fefd 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/VpnTest.java index b725b826b14f..b725b826b14f 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/connectivity/VpnTest.java diff --git a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java index 8b730af76951..8b730af76951 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsBaseTest.java index a058a466a4ff..a058a466a4ff 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsBaseTest.java diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java index 505ff9b6a34b..505ff9b6a34b 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java index f3ae9b051e7c..f3ae9b051e7c 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java index 9fa1c50423d9..9fa1c50423d9 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java index fd374bc9e68f..fd374bc9e68f 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java index 6d2c7dc39ffd..6d2c7dc39ffd 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java diff --git a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java index ebbc0ef62548..ebbc0ef62548 100644 --- a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java diff --git a/tests/net/jni/Android.bp b/packages/Connectivity/tests/unit/jni/Android.bp index 22a04f5c0945..22a04f5c0945 100644 --- a/tests/net/jni/Android.bp +++ b/packages/Connectivity/tests/unit/jni/Android.bp diff --git a/tests/net/jni/test_onload.cpp b/packages/Connectivity/tests/unit/jni/test_onload.cpp index 5194ddb0d882..5194ddb0d882 100644 --- a/tests/net/jni/test_onload.cpp +++ b/packages/Connectivity/tests/unit/jni/test_onload.cpp diff --git a/tests/net/res/raw/history_v1 b/packages/Connectivity/tests/unit/res/raw/history_v1 Binary files differindex de79491c032e..de79491c032e 100644 --- a/tests/net/res/raw/history_v1 +++ b/packages/Connectivity/tests/unit/res/raw/history_v1 diff --git a/tests/net/res/raw/net_dev_typical b/packages/Connectivity/tests/unit/res/raw/net_dev_typical index 290bf03eb9b4..290bf03eb9b4 100644 --- a/tests/net/res/raw/net_dev_typical +++ b/packages/Connectivity/tests/unit/res/raw/net_dev_typical diff --git a/tests/net/res/raw/netstats_uid_v4 b/packages/Connectivity/tests/unit/res/raw/netstats_uid_v4 Binary files differindex e75fc1ca5c2e..e75fc1ca5c2e 100644 --- a/tests/net/res/raw/netstats_uid_v4 +++ b/packages/Connectivity/tests/unit/res/raw/netstats_uid_v4 diff --git a/tests/net/res/raw/netstats_v1 b/packages/Connectivity/tests/unit/res/raw/netstats_v1 Binary files differindex e80860a6b959..e80860a6b959 100644 --- a/tests/net/res/raw/netstats_v1 +++ b/packages/Connectivity/tests/unit/res/raw/netstats_v1 diff --git a/tests/net/res/raw/xt_qtaguid_iface_fmt_typical b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_fmt_typical index 656d5bb82da4..656d5bb82da4 100644 --- a/tests/net/res/raw/xt_qtaguid_iface_fmt_typical +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_fmt_typical diff --git a/tests/net/res/raw/xt_qtaguid_iface_typical b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_typical index 610723aef2f2..610723aef2f2 100644 --- a/tests/net/res/raw/xt_qtaguid_iface_typical +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_iface_typical diff --git a/tests/net/res/raw/xt_qtaguid_typical b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_typical index c1b0d259955c..c1b0d259955c 100644 --- a/tests/net/res/raw/xt_qtaguid_typical +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_typical diff --git a/tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface index fc92715253ed..fc92715253ed 100644 --- a/tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_incorrect_iface diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying index 1ef18894b669..1ef18894b669 100644 --- a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression index 6d6bf550bbfa..6d6bf550bbfa 100644 --- a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_compression diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic index 2c2e5d2555f6..2c2e5d2555f6 100644 --- a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn index eb0513b10049..eb0513b10049 100644 --- a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_one_underlying_two_vpn diff --git a/tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self index afcdd7199026..afcdd7199026 100644 --- a/tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_rewrite_through_self diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication index d7c7eb9f4ae8..d7c7eb9f4ae8 100644 --- a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_duplication diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split index 38a3dce4a834..38a3dce4a834 100644 --- a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression index d35244b3b4f2..d35244b3b4f2 100644 --- a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_two_underlying_split_compression diff --git a/tests/net/res/raw/xt_qtaguid_vpn_with_clat b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_with_clat index 0d893d515ca2..0d893d515ca2 100644 --- a/tests/net/res/raw/xt_qtaguid_vpn_with_clat +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_vpn_with_clat diff --git a/tests/net/res/raw/xt_qtaguid_with_clat b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat index f04b32f08332..f04b32f08332 100644 --- a/tests/net/res/raw/xt_qtaguid_with_clat +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_after index 12d98ca29f57..12d98ca29f57 100644 --- a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_after +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_after diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_before index ce4bcc3a3b43..ce4bcc3a3b43 100644 --- a/tests/net/res/raw/xt_qtaguid_with_clat_100mb_download_before +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_100mb_download_before diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_simple b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_simple index a1d6d411bad8..a1d6d411bad8 100644 --- a/tests/net/res/raw/xt_qtaguid_with_clat_simple +++ b/packages/Connectivity/tests/unit/res/raw/xt_qtaguid_with_clat_simple diff --git a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm index 44d5a0c376f7..16141883df98 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm @@ -171,15 +171,15 @@ key P { } key LEFT_BRACKET { - label: '[' + label: ']' base, capslock: '\u062c' - shift: '<' + shift: '>' } key RIGHT_BRACKET { - label: ']' + label: '[' base, capslock: '\u062f' - shift: '>' + shift: '<' } key BACKSLASH { diff --git a/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm b/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm index cd3a4b959386..283cb4ef2081 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm @@ -89,14 +89,14 @@ key 8 { key 9 { label: '9' base: '9' - shift: '(' + shift: ')' shift+capslock: '\u05c2' } key 0 { label: '0' base: '0' - shift: ')' + shift: '(' shift+capslock: '\u05c1' } @@ -180,17 +180,17 @@ key P { } key LEFT_BRACKET { - label: '[' - base, capslock: '[' - shift: '{' -} - -key RIGHT_BRACKET { label: ']' base, capslock: ']' shift: '}' } +key RIGHT_BRACKET { + label: '[' + base, capslock: '[' + shift: '{' +} + ### ROW 3 key A { @@ -322,14 +322,14 @@ key M { key COMMA { label: ',' base: '\u05ea' - shift: '<' + shift: '>' capslock: ',' } key PERIOD { label: '.' base: '\u05e5' - shift: '>' + shift: '<' capslock: '.' } diff --git a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm index e7dd6c6d68c8..bfe78212b1eb 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_persian.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_persian.kcm @@ -292,14 +292,14 @@ key TAB { key COMMA { label, number: '\u0648' base: '\u0648' - shift: '<' + shift: '>' ctrl, alt, meta: none } key PERIOD { label, number: '.' base: '.' - shift: '>' + shift: '<' ctrl, alt, meta: none } @@ -440,14 +440,14 @@ key NUMPAD_9 { } key NUMPAD_LEFT_PAREN { - label, number: '(' - base: '(' + label, number: ')' + base: ')' ctrl, alt, meta: none } key NUMPAD_RIGHT_PAREN { - label, number: ')' - base: ')' + label, number: '(' + base: '(' ctrl, alt, meta: none } 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 83a838f54d28..e6ca2e0d10c8 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java @@ -35,7 +35,6 @@ import com.google.android.material.resources.TextAppearanceConfig; public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity { private CollapsingToolbarLayout mCollapsingToolbarLayout; - private Toolbar mToolbar; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -45,8 +44,8 @@ public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity { super.setContentView(R.layout.collapsing_toolbar_base_layout); mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar); - mToolbar = findViewById(R.id.action_bar); - setActionBar(mToolbar); + final Toolbar toolbar = findViewById(R.id.action_bar); + setActionBar(toolbar); // Enable title and home button by default final ActionBar actionBar = getActionBar(); @@ -97,16 +96,11 @@ public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity { @Override public boolean onNavigateUp() { if (!super.onNavigateUp()) { - finish(); + finishAfterTransition(); } return true; } - @Override - public Toolbar getToolbar() { - return mToolbar; - } - /** * Returns an instance of collapsing toolbar. */ 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 b4fe7ed1a8fb..4c45c5e3cb56 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java @@ -18,14 +18,11 @@ package com.android.settingslib.collapsingtoolbar; import android.app.ActivityOptions; import android.content.Intent; -import android.os.Build; import android.os.Bundle; import android.util.Log; -import android.view.MenuItem; import android.view.Window; import android.widget.Toolbar; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.os.BuildCompat; import androidx.fragment.app.FragmentActivity; @@ -40,117 +37,85 @@ public abstract class SettingsTransitionActivity extends FragmentActivity { private static final String TAG = "SettingsTransitionActivity"; private static final int DEFAULT_REQUEST = -1; + private Toolbar mToolbar; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { - if (BuildCompat.isAtLeastS()) { + 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 startActivity(Intent intent) { - if (!BuildCompat.isAtLeastS()) { + if (!isSettingsTransitionEnabled()) { super.startActivity(intent); return; } - final Toolbar toolbar = getToolbar(); - if (toolbar == null) { - Log.w(TAG, "Toolbar is null. Cannot apply settings transition!"); - super.startActivity(intent); - return; - } - super.startActivity(intent, getActivityOptionsBundle(toolbar)); + super.startActivity(intent, createActivityOptionsBundleForTransition(null)); } @Override public void startActivity(Intent intent, @Nullable Bundle options) { - if (!BuildCompat.isAtLeastS()) { - super.startActivity(intent, options); - return; - } - final Toolbar toolbar = getToolbar(); - if (toolbar == null) { - Log.w(TAG, "Toolbar is null. Cannot apply settings transition!"); + if (!isSettingsTransitionEnabled()) { super.startActivity(intent, options); return; } - if (options != null) { - super.startActivity(intent, getMergedBundleForTransition(options)); - return; - } - super.startActivity(intent, getActivityOptionsBundle(toolbar)); + + super.startActivity(intent, createActivityOptionsBundleForTransition(options)); } @Override public void startActivityForResult(Intent intent, int requestCode) { - if (!BuildCompat.isAtLeastS() || requestCode == DEFAULT_REQUEST) { + if (!isSettingsTransitionEnabled() || requestCode == DEFAULT_REQUEST) { super.startActivityForResult(intent, requestCode); return; } - final Toolbar toolbar = getToolbar(); - if (toolbar == null) { - Log.w(TAG, "Toolbar is null. Cannot apply settings transition!"); - super.startActivityForResult(intent, requestCode); - return; - } - super.startActivityForResult(intent, requestCode, getActivityOptionsBundle(toolbar)); + super.startActivityForResult(intent, requestCode, createActivityOptionsBundleForTransition( + null)); } @Override public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) { - if (!BuildCompat.isAtLeastS() || requestCode == DEFAULT_REQUEST) { - super.startActivityForResult(intent, requestCode, options); - return; - } - - final Toolbar toolbar = getToolbar(); - if (toolbar == null) { - Log.w(TAG, "Toolbar is null. Cannot apply settings transition!"); + if (!isSettingsTransitionEnabled() || requestCode == DEFAULT_REQUEST) { super.startActivityForResult(intent, requestCode, options); return; } - if (options != null) { - super.startActivityForResult(intent, requestCode, - getMergedBundleForTransition(options)); - return; - } - super.startActivityForResult(intent, requestCode, getActivityOptionsBundle(toolbar)); - } - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - final int id = item.getItemId(); - if (id == android.R.id.home) { - // Make the up button behave the same as the back button. - onBackPressed(); - return true; - } - return super.onOptionsItemSelected(item); + super.startActivityForResult(intent, requestCode, createActivityOptionsBundleForTransition( + options)); } - /** - * Subclasses should implement this method and return their {@link Toolbar}. - */ - public abstract Toolbar getToolbar(); - - private Bundle getActivityOptionsBundle(Toolbar toolbar) { - return ActivityOptions.makeSceneTransitionAnimation(this, toolbar, - "shared_element_view").toBundle(); + protected boolean isSettingsTransitionEnabled() { + return BuildCompat.isAtLeastS(); } - private Bundle getMergedBundleForTransition(@NonNull Bundle options) { - final Toolbar toolbar = getToolbar(); - final Bundle mergedBundle = new Bundle(); - mergedBundle.putAll(options); - final Bundle activityOptionsBundle = getActivityOptionsBundle(toolbar); - if (activityOptionsBundle != null) { - mergedBundle.putAll(activityOptionsBundle); + @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; } - return mergedBundle; + final Bundle mergedOptions = new Bundle(options); + mergedOptions.putAll(transitionOptions); + return mergedOptions; } } diff --git a/packages/SettingsLib/FooterPreference/Android.bp b/packages/SettingsLib/FooterPreference/Android.bp index 11f39e7bb210..0929706f27ba 100644 --- a/packages/SettingsLib/FooterPreference/Android.bp +++ b/packages/SettingsLib/FooterPreference/Android.bp @@ -20,4 +20,8 @@ android_library { ], sdk_version: "system_current", min_sdk_version: "21", + apex_available: [ + "//apex_available:platform", + "com.android.permission", + ], } 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 5ba1082e91ef..feb3b011fe63 100644 --- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java +++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java @@ -35,6 +35,7 @@ public class FooterPreference extends Preference { public static final String KEY_FOOTER = "footer_preference"; static final int ORDER_FOOTER = Integer.MAX_VALUE - 1; + private CharSequence mContentDescription; public FooterPreference(Context context, AttributeSet attrs) { super(context, attrs, R.attr.footerPreferenceStyle); @@ -52,6 +53,7 @@ public class FooterPreference extends Preference { title.setMovementMethod(new LinkMovementMethod()); title.setClickable(false); title.setLongClickable(false); + title.setContentDescription(mContentDescription); } @Override @@ -69,6 +71,26 @@ public class FooterPreference extends Preference { return getTitle(); } + /** + * To set content description of the {@link FooterPreference}. This can use for talkback + * environment if developer wants to have a customization content. + * + * @param contentDescription The resource id of the content description. + */ + public void setContentDescription(CharSequence contentDescription) { + if (!TextUtils.equals(mContentDescription, contentDescription)) { + mContentDescription = contentDescription; + notifyChanged(); + } + } + + /** + * Return the content description of footer preference. + */ + public CharSequence getContentDescription() { + return mContentDescription; + } + private void init() { setLayoutResource(R.layout.preference_footer); if (getIcon() == null) { @@ -87,6 +109,7 @@ public class FooterPreference extends Preference { private Context mContext; private String mKey; private CharSequence mTitle; + private CharSequence mContentDescription; public Builder(@NonNull Context context) { mContext = context; @@ -94,6 +117,7 @@ public class FooterPreference extends Preference { /** * To set the key value of the {@link FooterPreference}. + * * @param key The key value. */ public Builder setKey(@NonNull String key) { @@ -103,6 +127,7 @@ public class FooterPreference extends Preference { /** * To set the title of the {@link FooterPreference}. + * * @param title The title. */ public Builder setTitle(CharSequence title) { @@ -112,6 +137,7 @@ public class FooterPreference extends Preference { /** * To set the title of the {@link FooterPreference}. + * * @param titleResId The resource id of the title. */ public Builder setTitle(@StringRes int titleResId) { @@ -120,6 +146,28 @@ public class FooterPreference extends Preference { } /** + * To set content description of the {@link FooterPreference}. This can use for talkback + * environment if developer wants to have a customization content. + * + * @param contentDescription The resource id of the content description. + */ + public Builder setContentDescription(CharSequence contentDescription) { + mContentDescription = contentDescription; + return this; + } + + /** + * To set content description of the {@link FooterPreference}. This can use for talkback + * environment if developer wants to have a customization content. + * + * @param contentDescriptionResId The resource id of the content description. + */ + public Builder setContentDescription(@StringRes int contentDescriptionResId) { + mContentDescription = mContext.getText(contentDescriptionResId); + return this; + } + + /** * To generate the {@link FooterPreference}. */ public FooterPreference build() { @@ -132,6 +180,10 @@ public class FooterPreference extends Preference { if (!TextUtils.isEmpty(mKey)) { footerPreference.setKey(mKey); } + + if (!TextUtils.isEmpty(mContentDescription)) { + footerPreference.setContentDescription(mContentDescription); + } return footerPreference; } } diff --git a/packages/SettingsLib/RadioButtonPreference/Android.bp b/packages/SettingsLib/RadioButtonPreference/Android.bp index b309c01e0b7b..28ff71f22840 100644 --- a/packages/SettingsLib/RadioButtonPreference/Android.bp +++ b/packages/SettingsLib/RadioButtonPreference/Android.bp @@ -20,4 +20,8 @@ android_library { sdk_version: "system_current", min_sdk_version: "21", + apex_available: [ + "//apex_available:platform", + "com.android.permission", + ], } diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml index 403e417c1bf2..e92b6716976e 100644 --- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml +++ b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml @@ -29,9 +29,9 @@ android:id="@android:id/widget_frame" android:layout_width="wrap_content" android:layout_height="match_parent" + android:paddingHorizontal="20dp" android:gravity="center" android:minWidth="56dp" - android:layout_marginEnd="16dp" android:orientation="vertical"/> <LinearLayout diff --git a/packages/SettingsLib/TwoTargetPreference/Android.bp b/packages/SettingsLib/TwoTargetPreference/Android.bp index 078e8c3388ef..b32d5b40a13a 100644 --- a/packages/SettingsLib/TwoTargetPreference/Android.bp +++ b/packages/SettingsLib/TwoTargetPreference/Android.bp @@ -19,4 +19,8 @@ android_library { ], sdk_version: "system_current", min_sdk_version: "21", + apex_available: [ + "//apex_available:platform", + "com.android.permission", + ], } diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 8639d2154266..f3bd92bd76c0 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Laai tans stadig"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Laai tans draadloos"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Laai nie"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Ingeprop; kan nie op die oomblik laai nie"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Gelaai"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Beheer deur administrateur"</string> <string name="disabled" msgid="8017887509554714950">"Gedeaktiveer"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index a1cf08d5c691..69e2b4dbeb90 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -460,7 +460,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="8330015078868707899">"ተሰክቷል፣ አሁን ኃይል መሙላት አይቻልም"</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> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index e3c452d7ccef..6e9688771d81 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"تم التوصيل، ولكن يتعذّر الشحن الآن"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index e8d294dd9634..38f6b690552e 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"প্লাগ কৰি থোৱা হৈছে, এই মুহূৰ্তত চ্চাৰ্জ কৰিব নোৱাৰি"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 5d2c997f4aa5..75fa059fe781 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Asta doldurulur"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Simsiz şarj edilir"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Doldurulmur"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Cihaz hazırda batareya yığa bilmir"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Şarj edilib"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Admin tərəfindən nəzarət olunur"</string> <string name="disabled" msgid="8017887509554714950">"Deaktiv"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index aa722b193cf3..2308830c97e7 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sporo se puni"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Priključeno je, ali punjenje trenutno nije moguće"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontroliše administrator"</string> <string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 65dcb2f2a966..59a54c8e06e4 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"Падключана да сеткі сілкавання, зарадзіць зараз немагчыма"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 31b032529e20..20b70cc4a009 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -460,7 +460,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="8330015078868707899">"Включена в захранването, в момента не се зарежда"</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> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 8d5c99de6fb4..19664aa12c3d 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"প্লাগ-ইন করা হয়েছে কিন্তু এখনই চার্জ করা যাবে না"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 9242a7010db0..424d215a5863 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sporo punjenje"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Priključeno, trenutno se ne može puniti"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, ne puni se"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Pod kontrolom administratora"</string> <string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 7bb27abb17a2..010fa5e9c04a 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregant lentament"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carregant sense fil"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"No s\'està carregant"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"El dispositiu està endollat però en aquests moments no es pot carregar"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connectat; no s\'està carregant"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlat per l\'administrador"</string> <string name="disabled" msgid="8017887509554714950">"Desactivat"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 1a7e83be5268..1b63b9af977e 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Pomalé nabíjení"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bezdrátové nabíjení"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenabíjí se"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Zapojeno, ale nelze nabíjet"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Nabito"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Spravováno administrátorem"</string> <string name="disabled" msgid="8017887509554714950">"Deaktivováno"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 0554960d6475..a5ae7d1033ef 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Oplader langsomt"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Trådløs opladning"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Oplader ikke"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Enheden er tilsluttet en strømkilde. Det er ikke muligt at oplade på nuværende tidspunkt."</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Opladet"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolleret af administratoren"</string> <string name="disabled" msgid="8017887509554714950">"Deaktiveret"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 83f516f9a91b..ff68b1343efb 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Langsames Aufladen"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kabelloses Laden"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Wird nicht geladen"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Angeschlossen, kann derzeit nicht geladen werden"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Verbunden, wird nicht geladen"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Aufgeladen"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Durch den Administrator verwaltet"</string> <string name="disabled" msgid="8017887509554714950">"Deaktiviert"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 585d54e295c9..6136def2083b 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"Συνδέθηκε, δεν είναι δυνατή η φόρτιση αυτήν τη στιγμή"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 46e4b401ab8a..2fb00781b59a 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Plugged in, can\'t charge at the moment"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string> <string name="disabled" msgid="8017887509554714950">"Disabled"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 705e903603b1..450865b2f74d 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Plugged in, can\'t charge at the moment"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string> <string name="disabled" msgid="8017887509554714950">"Disabled"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 46e4b401ab8a..2fb00781b59a 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Plugged in, can\'t charge at the moment"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string> <string name="disabled" msgid="8017887509554714950">"Disabled"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 46e4b401ab8a..2fb00781b59a 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Plugged in, can\'t charge at the moment"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string> <string name="disabled" msgid="8017887509554714950">"Disabled"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 8e944ba0a777..525d4d2e2630 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Plugged in, can\'t charge right now"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlled by admin"</string> <string name="disabled" msgid="8017887509554714950">"Disabled"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 0a6e98b41c94..38af77a8f0af 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carga lenta"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carga inalámbrica"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"No se está cargando."</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Conectado. No se puede cargar en este momento"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Está conectado, pero no se está cargando"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada por el administrador"</string> <string name="disabled" msgid="8017887509554714950">"Inhabilitada"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 99b297f2b4bf..8634f186efe9 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Cargando lentamente"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Cargando sin cables"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"No se está cargando"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Enchufado, pero no se puede cargar en este momento"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado, no se carga"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada por el administrador"</string> <string name="disabled" msgid="8017887509554714950">"Inhabilitada"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 75e012858b2a..6dce3e2a8be3 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Aeglaselt laadimine"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Juhtmevaba laadimine"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei lae"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Vooluvõrgus, praegu ei saa laadida"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Laetud"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Juhib administraator"</string> <string name="disabled" msgid="8017887509554714950">"Keelatud"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 9e7fa29013df..7a81e5f4b469 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mantso kargatzen"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Hari gabe kargatzen"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ez da kargatzen ari"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Konektatuta dago. Ezin da kargatu une honetan."</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Konektatuta dago, baina ez da kargatzen ari"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Kargatuta"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Administratzaileak kontrolatzen du"</string> <string name="disabled" msgid="8017887509554714950">"Desgaituta"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 1d03bf5acd59..f8692a07f265 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"به برق وصل شده است، درحالحاضر شارژ نمیشود"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 68b42048e339..371fc57dd064 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Hidas lataus"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Langaton lataus"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei laturissa"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Kytketty virtalähteeseen, lataaminen ei onnistu"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Ladattu"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Järjestelmänvalvoja hallinnoi tätä asetusta."</string> <string name="disabled" msgid="8017887509554714950">"Pois päältä"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 98f346c97c8b..8e22f81d1b47 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Recharge lente"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"En recharge sans fil"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"N\'est pas en charge"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"L\'appareil est branché, mais il ne peut pas être chargé pour le moment"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connecté, 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> @@ -562,7 +562,7 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Avant de créer un profil limité, vous devez définir un écran de verrouillage pour protéger vos applications et vos données personnelles."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Définir verrouillage écran"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Passer à <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Créer un utilisateur…"</string> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Création d\'un utilisateur..."</string> <string name="add_user_failed" msgid="4809887794313944872">"Impossible de créer un utilisateur"</string> <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 11f7db704c0f..2556608bda0a 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"Appareil branché, mais impossible de le charger pour le moment"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index edf653f28e73..d6daeb00a29e 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Cargando lentamente"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Cargando sen fíos"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Non se está cargando"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Conectouse, pero non se pode cargar neste momento"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Opción controlada polo administrador"</string> <string name="disabled" msgid="8017887509554714950">"Desactivada"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 32efcdae84a2..ebfd7c5e7aae 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"પ્લગ ઇન કરેલ, હમણાં ચાર્જ કરી શકતા નથી"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index c312456195c1..6d45dae6d922 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"प्लग इन है, अभी चार्ज नहीं हो सकती"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 40e0a3aab950..0c048f07e028 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sporo punjenje"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Uključen, trenutačno se ne može puniti"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, ne puni se"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolira administrator"</string> <string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 8f0899700fc1..d56a84853871 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Lassú töltés"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Vezeték nélküli töltés"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nem tölt"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Csatlakoztatva, jelenleg nem tölt"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Feltöltve"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Rendszergazda által irányítva"</string> <string name="disabled" msgid="8017887509554714950">"Letiltva"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index ea1c59a43776..5256824170da 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"Միացված է հոսանքին, այս պահին չի կարող լիցքավորվել"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 20e6133f2a9c..58b4daf4ecde 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mengisi daya lambat"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Mengisi daya nirkabel"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Tidak mengisi daya"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Tercolok, tidak dapat mengisi baterai sekarang"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Terhubung, tidak mengisi daya"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Terisi"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Dikontrol oleh admin"</string> <string name="disabled" msgid="8017887509554714950">"Dinonaktifkan"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 7d36dc9b555a..d6f9be832c06 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Hæg hleðsla"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Hleður þráðlaust"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ekki í hleðslu"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Í sambandi, ekki hægt að hlaða eins og er"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Tengt, ekki í hleðslu"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Fullhlaðin"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Stjórnað af kerfisstjóra"</string> <string name="disabled" msgid="8017887509554714950">"Óvirkt"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index b0e660e920fd..7ef23dc097e1 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Ricarica lenta"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"In carica, wireless"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Non in carica"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Collegato alla corrente. Impossibile caricare al momento"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Dispositivo connesso, non in carica"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Carica"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Gestita dall\'amministratore"</string> <string name="disabled" msgid="8017887509554714950">"Disattivato"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 9e0c4a639c7f..b98ad007bb86 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -460,7 +460,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="8330015078868707899">"המכשיר מחובר, אבל לא ניתן לטעון עכשיו"</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> @@ -505,7 +505,7 @@ <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"פחות זמן."</string> <string name="cancel" msgid="5665114069455378395">"ביטול"</string> <string name="okay" msgid="949938843324579502">"אישור"</string> - <string name="alarms_and_reminders_label" msgid="6918395649731424294">"השכמות ותזכורות"</string> + <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" product="default" msgid="1122213569699233612">"הגדרה זו מתירה לאפליקציה להגדיר שעון מעורר ולתזמן אירועים אחרים. ייתכן שהאפליקציה תפעל גם כשלא נעשה שימוש בטלפון שלך, ולכן תגביר את צריכת הסוללה. אם ההרשאה הזו תושבת, ייתכן שהאפליקציה לא תפעל כראוי ושהשעונים המעוררים לא יפעלו כפי שתוזמנו."</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 4b484e54c11b..3975006fb7b6 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -460,7 +460,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="8330015078868707899">"接続されていますが、現在、充電できません"</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> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index cc30ccea15d8..6874824fa555 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -460,7 +460,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="8330015078868707899">"მიერთებულია, დატენვა ამჟამად ვერ ხერხდება"</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> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 97101f773755..e8c95c3c110c 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"Қосылған, зарядталмайды"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 086a8293ed7c..a069ec4af2fb 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"ដោតសាកថ្មរួចហើយ ប៉ុន្តែសាកថ្មមិនចូលទេឥឡូវនេះ"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 54d31cb57504..44e4e8e80457 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"ಪ್ಲಗ್ ಇನ್ ಮಾಡಲಾಗಿದೆ, ಇದೀಗ ಚಾರ್ಜ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 465467b2b6f2..d449a7f8072a 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"전원이 연결되었지만 현재 충전할 수 없음"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 538fe68d1dd2..10c3526b1252 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"сайылып турат, бирок кубатталган жок"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 59fdbc349b74..ab829ab7142d 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -460,7 +460,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="8330015078868707899">"ສຽບສາຍແລ້ວ, ບໍ່ສາມາດສາກໄດ້ໃນຕອນນີ້"</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> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 021ecce0f400..6aa200f12ccc 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Lėtai įkraunama"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kraunama be laidų"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nekraunama"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Įjungta į maitinimo lizdą, bet šiuo metu įkrauti neįmanoma"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Prijungta, neįkraunama"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Įkrauta"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Valdo administratorius"</string> <string name="disabled" msgid="8017887509554714950">"Neleidžiama"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index ecf915b1a80b..b9fcec1d059d 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Notiek lēnā uzlāde"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bezvadu uzlāde"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenotiek uzlāde"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Pievienots, taču pašlaik nevar veikt uzlādi"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Uzlādēts"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolē administrators"</string> <string name="disabled" msgid="8017887509554714950">"Atspējots"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 9dbe24c0c1c5..71a0c5c96d93 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -460,7 +460,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="8330015078868707899">"Приклучен е, но батеријата не може да се полни во моментов"</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> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index a99d864fa2f2..a31c344ed9ce 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"പ്ലഗ് ഇൻ ചെയ്തു, ഇപ്പോൾ ചാർജ് ചെയ്യാനാവില്ല"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> @@ -506,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" product="default" msgid="1122213569699233612">"അലാറങ്ങൾ സജ്ജീകരിക്കാനും മറ്റ് പ്രവർത്തനങ്ങൾ ഷെഡ്യൂൾ ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കുക. കൂടുതൽ ബാറ്ററി ഉപയോഗിച്ചേക്കാവുന്ന നിങ്ങളുടെ ഫോൺ ഉപയോഗിക്കാത്തപ്പോൾ ഈ ആപ്പ് ഉപയോഗിച്ചേക്കാം. ഈ അനുമതി ഓഫാണെങ്കിൽ, ഈ ആപ്പ് സ്വാഭാവികമായി പ്രവർത്തിച്ചേക്കില്ല, ഷെഡ്യൂൾ ചെയ്ത പോലെ അതിന്റെ അലാറങ്ങളും പ്രവർത്തിക്കില്ല."</string> + <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"അലാറങ്ങൾ സജ്ജീകരിക്കാനും മറ്റ് പ്രവർത്തനങ്ങൾ ഷെഡ്യൂൾ ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കുക. നിങ്ങൾ ഫോൺ ഉപയോഗിക്കാത്തപ്പോൾ ഈ ആപ്പ് ഉപയോഗിച്ചേക്കാം, ഇത് കൂടുതൽ ബാറ്ററി ഉപയോഗിക്കും. ഈ അനുമതി ഓഫാണെങ്കിൽ, ഈ ആപ്പ് സാധാരണ നിലയിൽ പ്രവർത്തിച്ചേക്കില്ല, ഷെഡ്യൂൾ ചെയ്ത പോലെ അതിന്റെ അലാറങ്ങളും പ്രവർത്തിക്കില്ല."</string> <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"അലാറങ്ങൾ സജ്ജീകരിക്കാനും മറ്റ് പ്രവർത്തനങ്ങൾ ഷെഡ്യൂൾ ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കുക. കൂടുതൽ ബാറ്ററി ഉപയോഗിച്ചേക്കാവുന്ന നിങ്ങളുടെ ടാബ്ലെറ്റ് ഉപയോഗിക്കാത്തപ്പോൾ ഈ ആപ്പ് ഉപയോഗിച്ചേക്കാം. ഈ അനുമതി ഓഫാണെങ്കിൽ, ഈ ആപ്പ് സ്വാഭാവികമായി പ്രവർത്തിച്ചേക്കില്ല, ഷെഡ്യൂൾ ചെയ്ത പോലെ അതിന്റെ അലാറങ്ങളും പ്രവർത്തിക്കില്ല."</string> <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"അലാറങ്ങൾ സജ്ജീകരിക്കാനും മറ്റ് പ്രവർത്തനങ്ങൾ ഷെഡ്യൂൾ ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കുക. കൂടുതൽ ബാറ്ററി ഉപയോഗിച്ചേക്കാവുന്ന നിങ്ങളുടെ ഉപകരണം ഉപയോഗിക്കാത്തപ്പോൾ ഈ ആപ്പ് ഉപയോഗിച്ചേക്കാം. ഈ അനുമതി ഓഫാണെങ്കിൽ, ഈ ആപ്പ് സ്വാഭാവികമായി പ്രവർത്തിച്ചേക്കില്ല, ഷെഡ്യൂൾ ചെയ്ത പോലെ അതിന്റെ അലാറങ്ങളും പ്രവർത്തിക്കില്ല."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ഷെഡ്യൂൾ, അലാറം, റിമെെൻഡർ, ക്ലോക്ക്"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index e8da28fd4c09..2e9fa6a567b3 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -460,7 +460,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="8330015078868707899">"Залгаастай тул одоо цэнэглэх боломжгүй"</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> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 332d16889320..23ebd39100e4 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"प्लग इन केलेले आहे, आता चार्ज करू शकत नाही"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 25c44cf950e9..24a195544b29 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mengecas dgn prlahan"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Mengecas tanpa wayar"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Tidak mengecas"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Dipalamkan, tidak boleh mengecas sekarang"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Sudah dicas"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Dikawal oleh pentadbir"</string> <string name="disabled" msgid="8017887509554714950">"Dilumpuhkan"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index f247c3704fa1..0cf3a0f31e61 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -460,7 +460,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="8330015078868707899">"ပလပ်ထိုးထားသောကြောင့် ယခုအားသွင်း၍ မရသေးပါ"</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> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index c28bf27e6e03..41debb60d2be 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Lader sakte"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Lader trådløst"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Lader ikke"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Laderen er koblet til – kan ikke lade akkurat nå"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Ladet"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrollert av administratoren"</string> <string name="disabled" msgid="8017887509554714950">"Slått av"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 645248c31207..50a09cff047f 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"प्लगइन गरिएको छ, अहिले नै चार्ज गर्न सकिँदैन"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 9889b22ac9b7..547278543c13 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Langzaam opladen"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Draadloos opladen"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Wordt niet opgeladen"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Aangesloten, kan nu niet opladen"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Verbonden, wordt niet opgeladen"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Opgeladen"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Ingesteld door beheerder"</string> <string name="disabled" msgid="8017887509554714950">"Uitgezet"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 66b1d61041ca..2fd00cb76dd7 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -460,7 +460,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="8330015078868707899">"ପ୍ଲଗ୍ରେ ଲାଗିଛି, ହେଲେ ଏବେ ଚାର୍ଜ କରିପାରିବ ନାହିଁ"</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> @@ -506,9 +506,9 @@ <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" product="default" msgid="1122213569699233612">"ଆଲାରାମଗୁଡ଼ିକୁ ସେଟ୍ କରିବା ଏବଂ ଅନ୍ୟ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଆପଣ ଆପଣଙ୍କ ଫୋନ୍ ବ୍ୟବହାର କରୁନଥିବା ସମୟରେ ଏହି ଆପକୁ ବ୍ୟବହାର କରାଯାଇପାରେ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ। ଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଥାଏ, ଏହି ଆପ୍ ସାମାନ୍ୟ ରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ ଏବଂ ଏହାର ଆଲାରାମଗୁଡ଼ିକ ସିଡୁଲ୍ କରାଯାଇଥିବା ପରି କାମ କରିବ ନାହିଁ।"</string> - <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"ଆଲାରାମଗୁଡ଼ିକୁ ସେଟ୍ କରିବା ଏବଂ ଅନ୍ୟ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଆପଣ ଆପଣଙ୍କ ଟାବଲେଟ୍ ବ୍ୟବହାର କରୁନଥିବା ସମୟରେ ଏହି ଆପକୁ ବ୍ୟବହାର କରାଯାଇପାରେ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ। ଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଥାଏ, ଏହି ଆପ୍ ସାମାନ୍ୟ ରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ ଏବଂ ଏହାର ଆଲାରାମଗୁଡ଼ିକ ସିଡୁଲ୍ କରାଯାଇଥିବା ପରି କାମ କରିବ ନାହିଁ।"</string> - <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"ଆଲାରାମଗୁଡ଼ିକୁ ସେଟ୍ କରିବା ଏବଂ ଅନ୍ୟ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଆପଣ ଆପଣଙ୍କ ଡିଭାଇସ୍ ବ୍ୟବହାର କରୁନଥିବା ସମୟରେ ଏହି ଆପକୁ ବ୍ୟବହାର କରାଯାଇପାରେ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ। ଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଥାଏ, ଏହି ଆପ୍ ସାମାନ୍ୟ ରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ ଏବଂ ଏହାର ଆଲାରାମଗୁଡ଼ିକ ସିଡୁଲ୍ କରାଯାଇଥିବା ପରି କାମ କରିବ ନାହିଁ।"</string> + <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"ଆଲାରାମଗୁଡ଼ିକୁ ସେଟ୍ କରିବା ଏବଂ ଅନ୍ୟ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଆପଣ ଆପଣଙ୍କ ଟାବଲେଟ୍ ବ୍ୟବହାର କରୁନଥିବା ସମୟରେ ଏହି ଆପକୁ ବ୍ୟବହାର କରାଯାଇପାରେ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ। ଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଥାଏ, ଏହି ଆପ୍ ସାମାନ୍ୟ ରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ ଏବଂ ଏହାର ଆଲାରାମଗୁଡ଼ିକ ସିଡୁଲ୍ କରାଯାଇଥିବା ଅନୁସାରେ କାମ କରିବ ନାହିଁ।"</string> + <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"ଆଲାରାମଗୁଡ଼ିକୁ ସେଟ୍ କରିବା ଏବଂ ଅନ୍ୟ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଆପଣ ଆପଣଙ୍କ ଟାବଲେଟ୍ ବ୍ୟବହାର କରୁନଥିବା ସମୟରେ ଏହି ଆପକୁ ବ୍ୟବହାର କରାଯାଇପାରେ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ। ଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଥାଏ, ଏହି ଆପ୍ ସାମାନ୍ୟ ରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ ଏବଂ ଏହାର ଆଲାରାମଗୁଡ଼ିକ ସିଡୁଲ୍ କରାଯାଇଥିବା ଅନୁସାରେ କାମ କରିବ ନାହିଁ।"</string> + <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"ଆଲାରାମଗୁଡ଼ିକୁ ସେଟ୍ କରିବା ଏବଂ ଅନ୍ୟ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଆପଣ ଆପଣଙ୍କ ଟାବଲେଟ୍ ବ୍ୟବହାର କରୁନଥିବା ସମୟରେ ଏହି ଆପକୁ ବ୍ୟବହାର କରାଯାଇପାରେ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ। ଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଥାଏ, ଏହି ଆପ୍ ସାମାନ୍ୟ ରୂପେ କାର୍ଯ୍ୟ କରିନପାରେ ଏବଂ ଏହାର ଆଲାରାମଗୁଡ଼ିକ ସିଡୁଲ୍ କରାଯାଇଥିବା ଅନୁସାରେ କାମ କରିବ ନାହିଁ।"</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-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index d314ad92597d..4ce6dc4c65e1 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"ਪਲੱਗ ਲੱਗਾ ਹੋਇਆ ਹੈ, ਇਸ ਸਮੇਂ ਚਾਰਜ ਨਹੀਂ ਹੋ ਸਕਦੀ"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 446fca3bbbb8..cac4a8e8a504 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Wolne ładowanie"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Ładowanie bezprzewodowe"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nie podłączony"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Podłączony. Nie można teraz ładować"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Podłączono, brak ładowania"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Naładowana"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolowane przez administratora"</string> <string name="disabled" msgid="8017887509554714950">"Wyłączone"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 117722ca65d3..ff8b5e179939 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregando devagar"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carregando sem fio"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Não está carregando"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Conectado. Não é possível carregar no momento"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado sem carregar"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada pelo admin"</string> <string name="disabled" msgid="8017887509554714950">"Desativado"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index ce3a748fa202..f5e2240dfca6 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregamento lento"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"A carregar sem fios"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Não está a carregar"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Ligada à corrente, não é possível carregar neste momento"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ligado, não está a carregar"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlado pelo gestor"</string> <string name="disabled" msgid="8017887509554714950">"Desativada"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 117722ca65d3..ff8b5e179939 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregando devagar"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carregando sem fio"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Não está carregando"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Conectado. Não é possível carregar no momento"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado sem carregar"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada pelo admin"</string> <string name="disabled" msgid="8017887509554714950">"Desativado"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index e0789eb88410..6099cd538cbe 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Se încarcă lent"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Se încarcă wireless"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nu se încarcă"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Conectat, nu se poate încărca chiar acum"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Încărcată"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlată de administrator"</string> <string name="disabled" msgid="8017887509554714950">"Dezactivată"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 375c0ca4474c..5bbb9f49940c 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -460,7 +460,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="8330015078868707899">"Подключено, не заряжается"</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> @@ -508,9 +508,9 @@ <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" product="default" msgid="1122213569699233612">"Разрешить этому приложению устанавливать будильники и планировать другие действия. Оно также сможет работать в то время, когда вы не пользуетесь телефоном. Не исключено, что в результате заряд батареи будет расходоваться быстрее. Если этот параметр отключен, в работе приложения могут возникнуть сбои, а будильники перестанут запускаться по расписанию."</string> - <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"Разрешить этому приложению устанавливать будильники и планировать другие действия. Оно также сможет работать в то время, когда вы не пользуетесь планшетом. Не исключено, что в результате заряд батареи будет расходоваться быстрее. Если этот параметр отключен, в работе приложения могут возникнуть сбои, а будильники перестанут запускаться по расписанию."</string> - <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"Разрешить этому приложению устанавливать будильники и планировать другие действия. Оно также сможет работать в то время, когда вы не пользуетесь устройством. Не исключено, что в результате заряд батареи будет расходоваться быстрее. Если этот параметр отключен, в работе приложения могут возникнуть сбои, а будильники перестанут запускаться по расписанию."</string> + <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"Если вы разрешите этому приложению устанавливать будильники и создавать напоминания, оно сможет работать, даже когда вы не пользуетесь телефоном. Правда, в результате заряд батареи будет расходоваться быстрее. Если отключить эту настройку, в работе приложения могут возникать сбои, а будильники не будут запускаться вовремя."</string> + <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"Если вы разрешите этому приложению устанавливать будильники и создавать напоминания, оно сможет работать, даже когда вы не пользуетесь планшетом. Правда, в результате заряд батареи будет расходоваться быстрее. Если отключить эту настройку, в работе приложения могут возникать сбои, а будильники не будут запускаться вовремя."</string> + <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"Если вы разрешите этому приложению устанавливать будильники и создавать напоминания, оно сможет работать, даже когда вы не пользуетесь устройством. Правда, в результате заряд батареи будет расходоваться быстрее. Если отключить эту настройку, в работе приложения могут возникать сбои, а будильники не будут запускаться вовремя."</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-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 84652292726b..0c73fdaf4ff7 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"පේනුගත කර ඇත, මේ අවස්ථාවේදී ආරෝපණය කළ නොහැකිය"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 09589cd97f6b..018ee861cb81 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Pomalé nabíjanie"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Nabíja sa bezdrôtovo"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenabíja sa"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Pripojené, ale nie je možné nabíjať"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Pripojené, nenabíja sa"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Nabité"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Ovládané správcom"</string> <string name="disabled" msgid="8017887509554714950">"Deaktivované"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 7edbecad941f..0ad339eb458f 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Počasno polnjenje"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Brezžično polnjenje"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Se ne polni"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Priključeno, trenutno ni mogoče polniti"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, se ne polni"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Baterija napolnjena"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Nadzira skrbnik"</string> <string name="disabled" msgid="8017887509554714950">"Onemogočeno"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index aa0c118116f2..5868d78667c3 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Po karikohet ngadalë"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Po karikohet pa tel"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nuk po karikohet"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Në prizë, por nuk mund të karikohet për momentin"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Karikuar"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolluar nga administratori"</string> <string name="disabled" msgid="8017887509554714950">"Çaktivizuar"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 0d03460a6758..ebcbfb9589d7 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"Прикључено је, али пуњење тренутно није могуће"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 49ee6f5c6280..ced30cf548e8 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Laddas långsamt"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Laddas trådlöst"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Laddar inte"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Ansluten, kan inte laddas just nu"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ansluten, laddas inte"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Laddat"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Strys av administratören"</string> <string name="disabled" msgid="8017887509554714950">"Inaktiverad"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index b4a79eb6ed94..028eb3f8812b 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Inachaji pole pole"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Inachaji bila kutumia waya"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Haichaji"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Haiwezi kuchaji kwa sasa"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Imechajiwa"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Imedhibitiwa na msimamizi"</string> <string name="disabled" msgid="8017887509554714950">"Imezimwa"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index bdefb85d017a..155c688c31b5 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"செருகப்பட்டது, ஆனால் இப்போது சார்ஜ் செய்ய முடியவில்லை"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index f91929663284..c53be38c2ced 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"ప్లగ్ ఇన్ చేయబడింది, ప్రస్తుతం ఛార్జ్ చేయడం సాధ్యం కాదు"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> @@ -506,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" product="default" msgid="1122213569699233612">"అలారాలను సెట్ చేయడానికి, ఇతర చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. మీరు మీ ఫోన్ను ఉపయోగించనప్పుడు ఈ యాప్ ఉపయోగించబడవచ్చు, ఇది ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు. ఈ అనుమతిని ఆఫ్ చేసినట్లైతే, ఈ యాప్ సాధారణంగా పనిచేయకపోవచ్చు అలాగే దాని అలారాలు షెడ్యూల్ ప్రకారం పనిచేయవు."</string> + <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"అలారాలను సెట్ చేయడానికి, ఇతర చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. మీ ఫోన్ను మీరు ఉపయోగించనప్పుడు కూడా ఈ యాప్ ఉపయోగించబడవచ్చు, తద్వారా ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు. ఈ అనుమతిని ఆఫ్ చేస్తే, ఈ యాప్ సాధారణ రీతిలో పనిచేయకపోవచ్చు, దాని అలారాలు షెడ్యూల్ ప్రకారం పనిచేయవు."</string> <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"అలారాలను సెట్ చేయడానికి, ఇతర చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. మీరు మీ టాబ్లెట్ను ఉపయోగించనప్పుడు ఈ యాప్ ఉపయోగించబడవచ్చు, ఇది ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు. ఈ అనుమతిని ఆఫ్ చేసినట్లైతే, ఈ యాప్ సాధారణంగా పనిచేయకపోవచ్చు అలాగే దాని అలారాలు షెడ్యూల్ ప్రకారం పనిచేయవు."</string> <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"అలారాలను సెట్ చేయడానికి, ఇతర చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. మీరు మీ పరికరాన్ని ఉపయోగించనప్పుడు ఈ యాప్ ఉపయోగించబడవచ్చు, ఇది ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు. ఈ అనుమతిని ఆఫ్ చేసినట్లైతే, ఈ యాప్ సాధారణంగా పనిచేయకపోవచ్చు అలాగే దాని అలారాలు షెడ్యూల్ ప్రకారం పనిచేయవు."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"షెడ్యూల్, అలారం, రిమైండర్, గడియారం"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 798531ace264..52dd1655b1cc 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"เสียบอยู่ ไม่สามารถชาร์จได้ในขณะนี้"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> @@ -506,9 +507,9 @@ <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" product="default" msgid="1122213569699233612">"อนุญาตให้แอปตั้งปลุกและกำหนดเวลาการดำเนินการอื่นๆ ระบบอาจใช้แอปนี้เมื่อคุณไม่ได้ใช้โทรศัพท์ ซึ่งอาจใช้แบตเตอรี่มากขึ้น หากสิทธิ์ดังกล่าวปิดอยู่ แอปอาจทำงานไม่ปกติ และการปลุกจะไม่ทำงานตามกำหนดเวลา"</string> - <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"อนุญาตให้แอปตั้งปลุกและกำหนดเวลาการดำเนินการอื่นๆ ระบบอาจใช้แอปนี้เมื่อคุณไม่ได้ใช้แท็บเล็ต ซึ่งอาจใช้แบตเตอรี่มากขึ้น หากสิทธิ์ดังกล่าวปิดอยู่ แอปอาจทำงานไม่ปกติ และการปลุกจะไม่ทำงานตามกำหนดเวลา"</string> - <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"อนุญาตให้แอปตั้งปลุกและกำหนดเวลาการดำเนินการอื่นๆ ระบบอาจใช้แอปนี้เมื่อคุณไม่ได้ใช้อุปกรณ์ ซึ่งอาจใช้แบตเตอรี่มากขึ้น หากสิทธิ์ดังกล่าวปิดอยู่ แอปอาจทำงานไม่ปกติ และการปลุกจะไม่ทำงานตามกำหนดเวลา"</string> + <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"อนุญาตให้แอปตั้งปลุกและกำหนดเวลาการดำเนินการอื่นๆ ระบบอาจใช้แอปนี้แม้คุณจะไม่ได้ใช้โทรศัพท์อยู่ จึงอาจทำให้เปลืองแบตเตอรี่มากขึ้น การปิดสิทธิ์นี้จะทำให้แอปทำงานไม่ปกติและการปลุกไม่ทำงานตามกำหนดเวลา"</string> + <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"อนุญาตให้แอปตั้งปลุกและกำหนดเวลาการดำเนินการอื่นๆ ระบบอาจใช้แอปนี้แม้คุณจะไม่ได้ใช้แท็บเล็ตอยู่ จึงอาจทำให้เปลืองแบตเตอรี่มากขึ้น การปิดสิทธิ์นี้จะทำให้แอปทำงานไม่ปกติและการปลุกไม่ทำงานตามกำหนดเวลา"</string> + <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"อนุญาตให้แอปตั้งปลุกและกำหนดเวลาการดำเนินการอื่นๆ ระบบอาจใช้แอปนี้แม้คุณจะไม่ได้ใช้อุปกรณ์อยู่ จึงอาจทำให้เปลืองแบตเตอรี่มากขึ้น การปิดสิทธิ์นี้จะทำให้แอปทำงานไม่ปกติและการปลุกไม่ทำงานตามกำหนดเวลา"</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-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index ad7e5577c9b7..0962f932c08a 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mabagal na charge"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Wireless na nagcha-charge"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Hindi nagcha-charge"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Nakasaksak, hindi makapag-charge sa ngayon"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Nakakonekta, hindi nagcha-charge"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Nasingil"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Pinapamahalaan ng admin"</string> <string name="disabled" msgid="8017887509554714950">"Naka-disable"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index f80a32e180e8..727dee883e37 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Yavaş şarj oluyor"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kablosuz şarj oluyor"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Şarj olmuyor"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Prize takıldı, şu anda şarj olamıyor"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Bağlandı, şarj olmuyor"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Şarj oldu"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Yönetici tarafından denetleniyor"</string> <string name="disabled" msgid="8017887509554714950">"Devre dışı"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 292ec6220f63..dffc3d8f7d73 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -460,7 +460,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="8330015078868707899">"Підключено, не заряджається"</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> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 4a3800dfacef..a71a958eb492 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"پلگ ان ہے، ابھی چارج نہیں کر سکتے"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 228ba7789443..dc005a438a62 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sekin quvvat olmoqda"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Simsiz quvvat olmoqda"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Quvvat olmayapti"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Ulangan, lekin quvvat olmayapti"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ulangan, quvvat olmayapti"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Quvvat oldi"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Administrator tomonidan boshqariladi"</string> <string name="disabled" msgid="8017887509554714950">"Yoqilmagan"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 36df4cd82da4..186aa057792b 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -460,7 +460,8 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Đang sạc chậm"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Đang sạc không dây"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Hiện không sạc"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Đã cắm nhưng không thể sạc ngay"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <string name="battery_info_status_full" msgid="1339002294876531312">"Đã sạc"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Do quản trị viên kiểm soát"</string> <string name="disabled" msgid="8017887509554714950">"Đã tắt"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 4934e44d68f4..0069517637bb 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -460,7 +460,8 @@ <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="8330015078868707899">"已插入电源,但是现在无法充电"</string> + <!-- no translation found for battery_info_status_not_charging (3371084153747234837) --> + <skip /> <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> @@ -506,9 +507,9 @@ <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" product="default" msgid="1122213569699233612">"允许该应用设置闹钟或安排其他操作的执行时间。该应用可能会在您未使用手机时运行,手机或许会比较耗电。如果您关闭此权限,该应用可能无法正常运行,已设置的闹钟将无法在排定的时间响起。"</string> - <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"允许该应用设置闹钟或安排其他操作的执行时间。该应用可能会在您未使用平板电脑时运行,平板电脑或许会比较耗电。如果您关闭此权限,该应用可能无法正常运行,已设置的闹钟将无法在排定的时间响起。"</string> - <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"允许该应用设置闹钟或安排其他操作的执行时间。该应用可能会在您未使用设备时运行,设备或许会比较耗电。如果您关闭此权限,该应用可能无法正常运行,已设置的闹钟将无法在排定的时间响起。"</string> + <string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"允许该应用设置闹钟及安排其他操作的执行时间。该应用可能会在您未使用手机时运行,以致手机的电源消耗更快。如果您关闭此权限,该应用可能无法正常运行,已设置的闹钟也无法在排定的时间响起。"</string> + <string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"允许该应用设置闹钟及安排其他操作的执行时间。该应用可能会在您未使用平板电脑时运行,以致平板电脑的电源消耗更快。如果您关闭此权限,该应用可能无法正常运行,已设置的闹钟也无法在排定的时间响起。"</string> + <string name="alarms_and_reminders_footer_title" product="device" msgid="349578867821273761">"允许该应用设置闹钟及安排其他操作的执行时间。该应用可能会在您未使用设备时运行,以致设备电源消耗更快。如果您关闭此权限,该应用可能无法正常运行,已设置的闹钟也无法在排定的时间响起。"</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"设置, 闹钟, 提醒, 时钟, schedule, alarm, reminder, clock"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"开启"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"开启勿扰模式"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index f46710e3ed7c..56249053f245 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -460,7 +460,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="8330015078868707899">"已連接電源插頭,但目前無法充電"</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> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 2e77d6d6faf1..1215eb168ea7 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -460,7 +460,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="8330015078868707899">"已接上電源,但現在無法充電"</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> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 30e231628dff..825f173dd5eb 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -460,7 +460,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Ishaja kancane"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Iyashaja ngaphandle kwentambo"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ayishaji"</string> - <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Kuxhunyiwe, ayikwazi ukushaja khona manje"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ixhunyiwe, ayishaji"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Kushajiwe"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kulawulwa umqondisi"</string> <string name="disabled" msgid="8017887509554714950">"Akusebenzi"</string> diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java index 1a4f0efd3834..4503669f5901 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java @@ -62,4 +62,11 @@ public class FooterPreferenceTest { assertThat(mFooterPreference.getTitle()).isEqualTo("summary"); } + + @Test + public void setContentDescription_contentSet_shouldGetSameContentDescription() { + mFooterPreference.setContentDescription("test"); + + assertThat(mFooterPreference.getContentDescription()).isEqualTo("test"); + } } diff --git a/packages/Shell/res/values-te/strings.xml b/packages/Shell/res/values-te/strings.xml index 6050c1f0bb73..989b53e7810d 100644 --- a/packages/Shell/res/values-te/strings.xml +++ b/packages/Shell/res/values-te/strings.xml @@ -43,5 +43,5 @@ <string name="bugreport_info_title" msgid="2306030793918239804">"బగ్ శీర్షిక"</string> <string name="bugreport_info_description" msgid="5072835127481627722">"బగ్ సారాంశం"</string> <string name="save" msgid="4781509040564835759">"సేవ్ చేయి"</string> - <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"బగ్ నివేదిక భాగస్వామ్యం చేయండి"</string> + <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"బగ్ నివేదిక షేర్ చేయండి"</string> </resources> diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java index 32def0309aaa..b3c437480af7 100644 --- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java +++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java @@ -19,6 +19,7 @@ package com.android.systemui.plugins; import android.app.PendingIntent; import android.app.smartspace.SmartspaceAction; import android.app.smartspace.SmartspaceTarget; +import android.app.smartspace.SmartspaceTargetEvent; import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Parcelable; @@ -45,6 +46,18 @@ public interface BcSmartspaceDataPlugin extends Plugin { /** Unregister a listener. */ void unregisterListener(SmartspaceTargetListener listener); + /** Register a SmartspaceEventNotifier. */ + default void registerSmartspaceEventNotifier(SmartspaceEventNotifier notifier) {} + + /** Push a SmartspaceTargetEvent to the SmartspaceEventNotifier. */ + default void notifySmartspaceEvent(SmartspaceTargetEvent event) {} + + /** Allows for notifying the SmartspaceSession of SmartspaceTargetEvents. */ + interface SmartspaceEventNotifier { + /** Pushes a given SmartspaceTargetEvent to the SmartspaceSession. */ + void notifySmartspaceEvent(SmartspaceTargetEvent event); + } + /** * Create a view to be shown within the parent. Do not add the view, as the parent * will be responsible for correctly setting the LayoutParams diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml index 0ee1b694432d..8bb78775e727 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml @@ -26,6 +26,7 @@ android:orientation="vertical" android:gravity="center" android:layout_gravity="center_horizontal" + android:layout_marginBottom="12dp" android:layout_alignParentBottom="true"> <com.android.keyguard.CarrierText @@ -44,7 +45,6 @@ android:id="@+id/emergency_call_button" android:layout_width="wrap_content" android:layout_height="48dp" - android:layout_marginBottom="12dp" android:text="@*android:string/lockscreen_emergency_call" style="@style/Keyguard.TextView.EmergencyButton" /> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml index c64afd307f23..a957df6537ff 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml @@ -192,6 +192,7 @@ android:orientation="vertical" android:layout_gravity="bottom|center_horizontal" android:layout_marginTop="@dimen/keyguard_eca_top_margin" + android:layout_marginBottom="12dp" android:gravity="center_horizontal"/> </com.android.keyguard.KeyguardPINView> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml index 297c20ef8d6d..3e34e4b255eb 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml @@ -57,7 +57,7 @@ android:id="@+id/row0" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingBottom="16dp" + android:paddingBottom="4dp" > <com.android.keyguard.PasswordTextView android:id="@+id/simPinEntry" @@ -202,6 +202,7 @@ android:orientation="vertical" android:layout_gravity="bottom|center_horizontal" android:layout_marginTop="@dimen/keyguard_eca_top_margin" + android:layout_marginBottom="2dp" android:gravity="center_horizontal"/> </com.android.keyguard.KeyguardSimPinView> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml index 388919e6d81c..d5510e90af29 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml @@ -58,7 +58,7 @@ android:id="@+id/row0" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingBottom="16dp" + android:paddingBottom="4dp" > <com.android.keyguard.PasswordTextView android:id="@+id/pukEntry" @@ -204,5 +204,6 @@ android:orientation="vertical" android:layout_gravity="bottom|center_horizontal" android:layout_marginTop="@dimen/keyguard_eca_top_margin" + android:layout_marginBottom="2dp" android:gravity="center_horizontal"/> </com.android.keyguard.KeyguardSimPukView> diff --git a/packages/SystemUI/res/drawable/ic_move_magnification.xml b/packages/SystemUI/res/drawable/ic_move_magnification.xml index 642fc315efcc..96db3651ac25 100644 --- a/packages/SystemUI/res/drawable/ic_move_magnification.xml +++ b/packages/SystemUI/res/drawable/ic_move_magnification.xml @@ -19,7 +19,7 @@ <item> <shape android:shape="rectangle"> <solid - android:color="@color/magnification_switch_button_color" /> + android:color="@color/magnification_drag_handle_color" /> <size android:height="@dimen/magnification_drag_view_size" android:width="@dimen/magnification_drag_view_size"/> @@ -28,8 +28,8 @@ <item android:gravity="center"> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" + android:width="30dp" + android:height="30dp" android:viewportWidth="24" android:viewportHeight="24"> <path diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml index 5bb7390d660f..36a1224a78c9 100644 --- a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml +++ b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml @@ -24,10 +24,11 @@ </shape> </item> - <item> + <item + android:gravity="center"> <vector - android:width="24dp" - android:height="24dp" + android:width="36dp" + android:height="36dp" android:viewportWidth="24" android:viewportHeight="24"> <group> diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml index 2d8a5d95825b..f41f78431788 100644 --- a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml +++ b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml @@ -24,10 +24,11 @@ </shape> </item> - <item> + <item + android:gravity="center"> <vector - android:width="24dp" - android:height="24dp" + android:width="36dp" + android:height="36dp" android:viewportWidth="24" android:viewportHeight="24"> <path diff --git a/packages/SystemUI/res/layout/controls_onboarding.xml b/packages/SystemUI/res/layout/controls_onboarding.xml index 577a3b404472..d234c32b1577 100644 --- a/packages/SystemUI/res/layout/controls_onboarding.xml +++ b/packages/SystemUI/res/layout/controls_onboarding.xml @@ -46,7 +46,7 @@ android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_vertical" - android:textColor="?attr/wallpaperTextColor" + android:textColor="?android:attr/colorPrimary" android:textSize="16sp"/> <ImageView android:id="@+id/dismiss" @@ -58,7 +58,7 @@ android:layout_marginEnd="2dp" android:alpha="0.7" android:src="@drawable/ic_close_white" - android:tint="?attr/wallpaperTextColor" + android:tint="?android:attr/colorPrimary" android:background="?android:attr/selectableItemBackgroundBorderless" android:contentDescription="@string/accessibility_desc_close"/> </LinearLayout> diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 69d73c1b94e1..95483f13ec6a 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -95,6 +95,7 @@ android:background="@drawable/wallet_lockscreen_bg" android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset" android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset" + android:contentDescription="@string/accessibility_wallet_button" android:visibility="gone" /> <FrameLayout diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml index 2ec4e73145f5..9b2f0ac364da 100644 --- a/packages/SystemUI/res/layout/notification_conversation_info.xml +++ b/packages/SystemUI/res/layout/notification_conversation_info.xml @@ -24,7 +24,7 @@ android:clipChildren="true" android:clipToPadding="true" android:orientation="vertical" - android:paddingStart="12dp"> + android:paddingStart="@dimen/notification_shade_content_margin_horizontal"> <!-- Package Info --> <LinearLayout diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml index a146547d0083..90214b781ba1 100644 --- a/packages/SystemUI/res/layout/ongoing_call_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml @@ -23,6 +23,7 @@ android:background="@drawable/ongoing_call_chip_bg" android:paddingStart="@dimen/ongoing_call_chip_side_padding" android:paddingEnd="@dimen/ongoing_call_chip_side_padding" + android:contentDescription="@string/ongoing_phone_call_content_description" > <ImageView diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml index f056402b0cd4..59e1a755d7d2 100644 --- a/packages/SystemUI/res/layout/qs_detail.xml +++ b/packages/SystemUI/res/layout/qs_detail.xml @@ -23,7 +23,6 @@ android:clickable="true" android:orientation="vertical" android:layout_marginTop="@*android:dimen/quick_qs_offset_height" - android:layout_marginBottom="@dimen/qs_container_bottom_padding" android:paddingBottom="8dp" android:visibility="invisible" android:elevation="4dp" diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml index 4607e5f5cd79..4c6418ace3fc 100644 --- a/packages/SystemUI/res/layout/qs_panel.xml +++ b/packages/SystemUI/res/layout/qs_panel.xml @@ -17,7 +17,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/quick_settings_container" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:clipToPadding="false" android:clipChildren="false" > @@ -25,7 +25,6 @@ android:id="@+id/expanded_qs_scroll_view" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingBottom="@dimen/qs_container_bottom_padding" android:elevation="4dp" android:importantForAccessibility="no" android:scrollbars="none" diff --git a/packages/SystemUI/res/layout/qs_tile_side_icon.xml b/packages/SystemUI/res/layout/qs_tile_side_icon.xml index 9f9af9d21db1..1ae0a1c7c5a8 100644 --- a/packages/SystemUI/res/layout/qs_tile_side_icon.xml +++ b/packages/SystemUI/res/layout/qs_tile_side_icon.xml @@ -35,6 +35,7 @@ android:layout_width="@dimen/qs_icon_size" android:layout_height="@dimen/qs_icon_size" android:src="@*android:drawable/ic_chevron_end" + android:autoMirrored="true" android:visibility="gone" android:importantForAccessibility="no" /> diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml index a62310be9bac..c88703dabeea 100644 --- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml +++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml @@ -28,7 +28,7 @@ <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" - android:layout_width="0dp" + android:layout_width="wrap_content" android:layout_height="match_parent" android:minWidth="48dp" android:minHeight="48dp" @@ -61,9 +61,9 @@ <LinearLayout android:id="@+id/rightLayout" - android:layout_width="0dp" + android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_weight="1" + android:gravity="center_vertical|end" > <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons" diff --git a/packages/SystemUI/res/layout/quick_settings_security_footer.xml b/packages/SystemUI/res/layout/quick_settings_security_footer.xml index ce7f82780dfe..08bd71c12eb1 100644 --- a/packages/SystemUI/res/layout/quick_settings_security_footer.xml +++ b/packages/SystemUI/res/layout/quick_settings_security_footer.xml @@ -55,6 +55,7 @@ android:layout_marginStart="8dp" android:contentDescription="@null" android:src="@*android:drawable/ic_chevron_end" + android:autoMirrored="true" android:tint="?android:attr/textColorSecondary" /> </com.android.systemui.util.DualHeightHorizontalLinearLayout> diff --git a/packages/SystemUI/res/layout/wallet_fullscreen.xml b/packages/SystemUI/res/layout/wallet_fullscreen.xml index d365aa341493..aceefeecad5a 100644 --- a/packages/SystemUI/res/layout/wallet_fullscreen.xml +++ b/packages/SystemUI/res/layout/wallet_fullscreen.xml @@ -22,60 +22,67 @@ android:layout_height="match_parent" android:clipChildren="false"> <Toolbar - android:id="@+id/action_bar" - style="?android:attr/actionBarStyle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@android:color/transparent" - android:navigationContentDescription="@null" /> + android:id="@+id/action_bar" + style="?android:attr/actionBarStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@android:color/transparent" + android:navigationContentDescription="@null" /> <LinearLayout android:id="@+id/card_carousel_container" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="48dp" android:orientation="vertical"> - <ImageView - android:id="@+id/icon" - android:layout_width="@dimen/wallet_screen_header_view_size" - android:layout_height="@dimen/wallet_screen_header_view_size" - android:layout_gravity="center_horizontal" - android:layout_marginVertical="10dp" - android:scaleType="center" - android:contentDescription="@null"/> - <TextView - android:id="@+id/label" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/Wallet.TextAppearance" - android:textAlignment="center"/> - - <com.android.systemui.wallet.ui.WalletCardCarousel - android:id="@+id/card_carousel" + <androidx.core.widget.NestedScrollView android:layout_width="match_parent" - android:layout_height="wrap_content" - android:transitionName="dotIndicator" - android:clipChildren="false" - android:clipToPadding="false" - android:layout_marginVertical="24dp"/> - - <Button - android:id="@+id/wallet_action_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:layout_marginVertical="16dp" - android:paddingVertical="@dimen/wallet_button_vertical_padding" - android:paddingHorizontal="@dimen/wallet_button_horizontal_padding" - android:background="@drawable/wallet_action_button_bg" - android:textColor="?androidprv:attr/textColorPrimaryInverse" - android:textAlignment="center" - android:visibility="gone"/> + android:layout_height="0dp" + android:layout_weight="1"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="0dp" + android:orientation="vertical"> + <ImageView + android:id="@+id/icon" + android:layout_width="@dimen/wallet_screen_header_view_size" + android:layout_height="@dimen/wallet_screen_header_view_size" + android:layout_gravity="center_horizontal" + android:layout_marginVertical="10dp" + android:scaleType="center" + android:contentDescription="@null"/> + <TextView + android:id="@+id/label" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/Wallet.TextAppearance" + android:textAlignment="center"/> + <com.android.systemui.wallet.ui.WalletCardCarousel + android:id="@+id/card_carousel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:transitionName="dotIndicator" + android:clipChildren="false" + android:clipToPadding="false" + android:layout_marginVertical="24dp"/> + <Button + android:id="@+id/wallet_action_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_marginVertical="16dp" + android:paddingVertical="@dimen/wallet_button_vertical_padding" + android:paddingHorizontal="@dimen/wallet_button_horizontal_padding" + android:background="@drawable/wallet_action_button_bg" + android:textColor="?androidprv:attr/textColorPrimaryInverse" + android:textAlignment="center" + android:visibility="gone"/> + </LinearLayout> + </androidx.core.widget.NestedScrollView> <View android:layout_width="match_parent" android:layout_height="0dp" - android:layout_weight="1"/> - + android:layout_weight="0.1"/> <Button android:id="@+id/wallet_app_button" android:layout_width="wrap_content" @@ -88,7 +95,6 @@ android:textColor="?androidprv:attr/colorAccentPrimary" android:textAlignment="center" android:layout_marginVertical="24dp"/> - </LinearLayout> <include layout="@layout/wallet_empty_state"/> diff --git a/packages/SystemUI/res/layout/window_magnifier_view.xml b/packages/SystemUI/res/layout/window_magnifier_view.xml index 96de5a91f37c..ae16e5c919c4 100644 --- a/packages/SystemUI/res/layout/window_magnifier_view.xml +++ b/packages/SystemUI/res/layout/window_magnifier_view.xml @@ -75,7 +75,7 @@ android:id="@+id/drag_handle" android:layout_width="@dimen/magnification_drag_view_size" android:layout_height="@dimen/magnification_drag_view_size" - android:layout_margin="@dimen/magnification_outer_border_margin" + android:layout_margin="@dimen/magnification_inner_border_margin" android:layout_gravity="right|bottom" android:paddingEnd="@dimen/magnifier_drag_handle_padding" android:paddingBottom="@dimen/magnifier_drag_handle_padding" diff --git a/packages/SystemUI/res/values-ar/strings_tv.xml b/packages/SystemUI/res/values-ar/strings_tv.xml index 7297c898f99a..769999f5f902 100644 --- a/packages/SystemUI/res/values-ar/strings_tv.xml +++ b/packages/SystemUI/res/values-ar/strings_tv.xml @@ -21,8 +21,8 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mic_active" msgid="5766614241012047024">"الميكروفون نشط"</string> <string name="app_accessed_mic" msgid="2754428675130470196">"تمكن %1$s من الوصول إلى الميكروفون الخاص بك."</string> - <string name="notification_vpn_connected" msgid="3891023882833274730">"الشبكة الافتراضية الخاصة (VPN) متصلة."</string> - <string name="notification_vpn_disconnected" msgid="7150747626448044843">"الشبكة الافتراضية الخاصة (VPN) غير متصلة."</string> + <string name="notification_vpn_connected" msgid="3891023882833274730">"شبكة VPN متصلة."</string> + <string name="notification_vpn_disconnected" msgid="7150747626448044843">"شبكة VPN غير متصلة."</string> <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"عبر <xliff:g id="VPN_APP">%1$s</xliff:g>"</string> <string name="tv_notification_panel_title" msgid="5311050946506276154">"الإشعارات"</string> <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ما من إشعارات"</string> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 8d2a82cb1da1..7101935f1f93 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -247,6 +247,7 @@ <!-- Window magnification colors --> <color name="magnification_border_color">#FF9900</color> <color name="magnification_switch_button_color">#7F000000</color> + <color name="magnification_drag_handle_color">#B3000000</color> <!-- Volume dialog colors --> <color name="volume_dialog_background_color">@android:color/transparent</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 57e6cc3fe302..87a669f710db 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -623,8 +623,6 @@ <dimen name="qs_notif_collapsed_space">64dp</dimen> - <dimen name="qs_container_bottom_padding">24dp</dimen> - <!-- Desired qs icon overlay size. --> <dimen name="qs_detail_icon_overlay_size">24dp</dimen> @@ -1292,9 +1290,9 @@ <dimen name="magnification_mirror_surface_margin">20dp</dimen> <dimen name="magnification_frame_move_short">5dp</dimen> <dimen name="magnification_frame_move_long">25dp</dimen> - <dimen name="magnification_drag_view_size">38dp</dimen> + <dimen name="magnification_drag_view_size">36dp</dimen> <dimen name="magnification_controls_size">90dp</dimen> - <dimen name="magnification_switch_button_size">60dp</dimen> + <dimen name="magnification_switch_button_size">48dp</dimen> <dimen name="magnification_switch_button_padding">6dp</dimen> <dimen name="magnifier_left_right_controls_width">35dp</dimen> <dimen name="magnifier_left_right_controls_height">45dp</dimen> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index 6104588690e3..6393147f8b0b 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -49,4 +49,6 @@ <bool name="flag_ongoing_call_status_bar_chip">true</bool> <bool name="flag_smartspace">false</bool> + + <bool name="flag_smartspace_deduping">true</bool> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 150fc733ee3e..427ede51ad9c 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -336,6 +336,8 @@ <string name="accessibility_phone_button">Phone</string> <!-- Content description of the phone button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_voice_assist_button">Voice Assist</string> + <!-- Content description of the wallet button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_wallet_button">Wallet</string> <!-- Content description of the unlock button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_unlock_button">Unlock</string> <!-- Content description of the lock icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> @@ -2965,7 +2967,11 @@ <!-- Accessibility action for tapping on an affordance on an unlocked lock screen (ie: "Double tap to enter device") [CHAR LIMIT=NONE] --> <string name="accessibility_enter_hint">enter device</string> - <!-- Message shown to suggest authentication using [CHAR LIMIT=60]--> + <!-- Message shown to suggest authentication using fingerprint [CHAR LIMIT=60]--> <string name="keyguard_try_fingerprint">Use fingerprint to open</string> + <!-- Accessibility announcement to inform user to unlock using the fingerprint sensor [CHAR LIMIT=NONE] --> + <string name="accessibility_fingerprint_bouncer">Authentication required. Touch the fingerprint sensor to authenticate.</string> + <!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] --> + <string name="ongoing_phone_call_content_description">Ongoing phone call</string> </resources> diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java index 825e008263fa..4b9bf8c27f8a 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java @@ -131,6 +131,11 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback); } + /** Animate the clock appearance */ + public void animateAppear() { + mView.animateAppear(); + } + /** * Updates the time for the view. */ diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java index 41d991c25393..a07a849c9fa1 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java @@ -44,6 +44,7 @@ public class AnimatableClockView extends TextView { private static final CharSequence SINGLE_LINE_FORMAT_12_HOUR = "h:mm"; private static final CharSequence SINGLE_LINE_FORMAT_24_HOUR = "HH:mm"; private static final long DOZE_ANIM_DURATION = 300; + private static final long APPEAR_ANIM_DURATION = 350; private static final long CHARGE_ANIM_DURATION_PHASE_0 = 500; private static final long CHARGE_ANIM_DURATION_PHASE_1 = 1000; @@ -156,6 +157,30 @@ public class AnimatableClockView extends TextView { mLockScreenColor = lockScreenColor; } + void animateAppear() { + if (mTextAnimator == null) { + return; + } + + setTextStyle( + mDozingWeight, + -1 /* text size, no update */, + mLockScreenColor, + false /* animate */, + 0 /* duration */, + 0 /* delay */, + null /* onAnimationEnd */); + + setTextStyle( + mLockScreenWeight, + -1 /* text size, no update */, + mLockScreenColor, + true, /* animate */ + APPEAR_ANIM_DURATION, + 0 /* delay */, + null /* onAnimationEnd */); + } + void animateDisappear() { if (mTextAnimator == null) { return; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 407146fa991c..1c4cde8150d6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -35,6 +35,7 @@ public class KeyguardClockSwitch extends RelativeLayout { private static final long CLOCK_OUT_MILLIS = 150; private static final long CLOCK_IN_MILLIS = 200; + private static final long SMARTSPACE_MOVE_MILLIS = 350; /** * Optional/alternative clock injected via plugin. @@ -54,6 +55,8 @@ public class KeyguardClockSwitch extends RelativeLayout { * show it below the alternate clock. */ private View mKeyguardStatusArea; + /** Mutually exclusive with mKeyguardStatusArea */ + private View mSmartspaceView; /** * Maintain state so that a newly connected plugin can be initialized. @@ -67,6 +70,7 @@ public class KeyguardClockSwitch extends RelativeLayout { private AnimatorSet mClockInAnim = null; private AnimatorSet mClockOutAnim = null; + private ObjectAnimator mSmartspaceAnim = null; /** * If the Keyguard Slice has a header (big center-aligned text.) @@ -177,17 +181,22 @@ public class KeyguardClockSwitch extends RelativeLayout { private void animateClockChange(boolean useLargeClock) { if (mClockInAnim != null) mClockInAnim.cancel(); if (mClockOutAnim != null) mClockOutAnim.cancel(); + if (mSmartspaceAnim != null) mSmartspaceAnim.cancel(); View in, out; int direction = 1; + float smartspaceYTranslation; if (useLargeClock) { out = mClockFrame; in = mLargeClockFrame; if (indexOfChild(in) == -1) addView(in); direction = -1; + smartspaceYTranslation = mSmartspaceView == null ? 0 + : mClockFrame.getY() - mSmartspaceView.getY(); } else { in = mClockFrame; out = mLargeClockFrame; + smartspaceYTranslation = 0f; // Must remove in order for notifications to appear in the proper place removeView(out); @@ -222,6 +231,19 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockInAnim.start(); mClockOutAnim.start(); + + if (mSmartspaceView != null) { + mSmartspaceAnim = ObjectAnimator.ofFloat(mSmartspaceView, View.TRANSLATION_Y, + smartspaceYTranslation); + mSmartspaceAnim.setDuration(SMARTSPACE_MOVE_MILLIS); + mSmartspaceAnim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mSmartspaceAnim.addListener(new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animation) { + mSmartspaceAnim = null; + } + }); + mSmartspaceAnim.start(); + } } /** @@ -237,15 +259,18 @@ public class KeyguardClockSwitch extends RelativeLayout { } /** - * Set whether or not the lock screen is showing notifications. + * Based upon whether notifications are showing or not, display/hide the large clock and + * the smaller version. */ - void setHasVisibleNotifications(boolean hasVisibleNotifications) { + boolean willSwitchToLargeClock(boolean hasVisibleNotifications) { if (hasVisibleNotifications == mHasVisibleNotifications) { - return; + return false; } - animateClockChange(!hasVisibleNotifications); + boolean useLargeClock = !hasVisibleNotifications; + animateClockChange(useLargeClock); mHasVisibleNotifications = hasVisibleNotifications; + return useLargeClock; } public Paint getPaint() { @@ -303,6 +328,10 @@ public class KeyguardClockSwitch extends RelativeLayout { } } + void setSmartspaceView(View smartspaceView) { + mSmartspaceView = smartspaceView; + } + void updateColors(ColorExtractor.GradientColors colors) { mSupportsDarkText = colors.supportsDarkText(); mColorPalette = colors.getColorPalette(); @@ -317,6 +346,7 @@ public class KeyguardClockSwitch extends RelativeLayout { pw.println(" mClockFrame: " + mClockFrame); pw.println(" mLargeClockFrame: " + mLargeClockFrame); pw.println(" mKeyguardStatusArea: " + mKeyguardStatusArea); + pw.println(" mSmartspaceView: " + mSmartspaceView); pw.println(" mDarkAmount: " + mDarkAmount); pw.println(" mSupportsDarkText: " + mSupportsDarkText); pw.println(" mColorPalette: " + Arrays.toString(mColorPalette)); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 4b71a3a8fbdc..4245fbc2c3cd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -19,42 +19,22 @@ package com.android.keyguard; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import android.app.PendingIntent; import android.app.WallpaperManager; -import android.app.smartspace.SmartspaceConfig; -import android.app.smartspace.SmartspaceManager; -import android.app.smartspace.SmartspaceSession; -import android.app.smartspace.SmartspaceTarget; -import android.content.Intent; -import android.content.pm.UserInfo; import android.content.res.Resources; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.Handler; -import android.os.UserHandle; -import android.provider.Settings; import android.text.TextUtils; import android.text.format.DateFormat; import android.view.View; import android.widget.FrameLayout; import android.widget.RelativeLayout; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.clock.ClockManager; -import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.BcSmartspaceDataPlugin; -import com.android.systemui.plugins.BcSmartspaceDataPlugin.IntentStarter; import com.android.systemui.plugins.ClockPlugin; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.settings.UserTracker; -import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.stack.AnimationProperties; @@ -62,14 +42,10 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.NotificationIconContainer; import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.ViewController; -import com.android.systemui.util.settings.SecureSettings; import java.util.Locale; -import java.util.Optional; import java.util.TimeZone; -import java.util.concurrent.Executor; import javax.inject.Inject; @@ -85,9 +61,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private final KeyguardSliceViewController mKeyguardSliceViewController; private final NotificationIconAreaController mNotificationIconAreaController; private final BroadcastDispatcher mBroadcastDispatcher; - private final Executor mUiExecutor; private final BatteryController mBatteryController; - private final FeatureFlags mFeatureFlags; + private final LockscreenSmartspaceController mSmartspaceController; /** * Clock for both small and large sizes @@ -97,20 +72,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private AnimatableClockController mLargeClockViewController; private FrameLayout mLargeClockFrame; - private SmartspaceSession mSmartspaceSession; - private SmartspaceSession.OnTargetsAvailableListener mSmartspaceCallback; - private ConfigurationController mConfigurationController; - private ActivityStarter mActivityStarter; - private FalsingManager mFalsingManager; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final KeyguardBypassController mBypassController; - private Handler mHandler; - private UserTracker mUserTracker; - private SecureSettings mSecureSettings; - private ContentObserver mSettingsObserver; - private boolean mShowSensitiveContentForCurrentUser; - private boolean mShowSensitiveContentForManagedUser; - private UserHandle mManagedUserHandle; /** * Listener for changes to the color palette. @@ -118,59 +81,30 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS * The color palette changes when the wallpaper is changed. */ private final ColorExtractor.OnColorsChangedListener mColorsListener = - new ColorExtractor.OnColorsChangedListener() { - @Override - public void onColorsChanged(ColorExtractor extractor, int which) { - if ((which & WallpaperManager.FLAG_LOCK) != 0) { - mView.updateColors(getGradientColors()); - } - } - }; - - private final ConfigurationController.ConfigurationListener mConfigurationListener = - new ConfigurationController.ConfigurationListener() { - @Override - public void onThemeChanged() { - updateWallpaperColor(); - } - }; - - private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin; - - private final StatusBarStateController.StateListener mStatusBarStateListener = - new StatusBarStateController.StateListener() { - @Override - public void onDozeAmountChanged(float linear, float eased) { - if (mSmartspaceView != null) { - mSmartspaceView.setDozeAmount(eased); - } + (extractor, which) -> { + if ((which & WallpaperManager.FLAG_LOCK) != 0) { + mView.updateColors(getGradientColors()); } }; + private final ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin; + // If set, will replace keyguard_status_area - private BcSmartspaceDataPlugin.SmartspaceView mSmartspaceView; - private Optional<BcSmartspaceDataPlugin> mSmartspacePlugin; + private View mSmartspaceView; @Inject public KeyguardClockSwitchController( KeyguardClockSwitch keyguardClockSwitch, StatusBarStateController statusBarStateController, - SysuiColorExtractor colorExtractor, ClockManager clockManager, + SysuiColorExtractor colorExtractor, + ClockManager clockManager, KeyguardSliceViewController keyguardSliceViewController, NotificationIconAreaController notificationIconAreaController, BroadcastDispatcher broadcastDispatcher, - FeatureFlags featureFlags, - @Main Executor uiExecutor, BatteryController batteryController, - ConfigurationController configurationController, - ActivityStarter activityStarter, - FalsingManager falsingManager, KeyguardUpdateMonitor keyguardUpdateMonitor, KeyguardBypassController bypassController, - @Main Handler handler, - UserTracker userTracker, - SecureSettings secureSettings, - Optional<BcSmartspaceDataPlugin> smartspacePlugin) { + LockscreenSmartspaceController smartspaceController) { super(keyguardClockSwitch); mStatusBarStateController = statusBarStateController; mColorExtractor = colorExtractor; @@ -178,18 +112,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mKeyguardSliceViewController = keyguardSliceViewController; mNotificationIconAreaController = notificationIconAreaController; mBroadcastDispatcher = broadcastDispatcher; - mFeatureFlags = featureFlags; - mUiExecutor = uiExecutor; mBatteryController = batteryController; - mConfigurationController = configurationController; - mActivityStarter = activityStarter; - mFalsingManager = falsingManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mBypassController = bypassController; - mHandler = handler; - mUserTracker = userTracker; - mSecureSettings = secureSettings; - mSmartspacePlugin = smartspacePlugin; + mSmartspaceController = smartspaceController; } /** @@ -232,119 +158,35 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mBypassController); mLargeClockViewController.init(); - mStatusBarStateController.addCallback(mStatusBarStateListener); - mConfigurationController.addCallback(mConfigurationListener); + if (mSmartspaceController.isEnabled()) { + mSmartspaceView = mSmartspaceController.buildAndConnectView(mView); - if (mFeatureFlags.isSmartspaceEnabled() && mSmartspacePlugin.isPresent()) { - BcSmartspaceDataPlugin smartspaceDataPlugin = mSmartspacePlugin.get(); View ksa = mView.findViewById(R.id.keyguard_status_area); int ksaIndex = mView.indexOfChild(ksa); ksa.setVisibility(View.GONE); - mSmartspaceView = smartspaceDataPlugin.getView(mView); - mSmartspaceView.registerDataProvider(smartspaceDataPlugin); - mSmartspaceView.setIntentStarter(new IntentStarter() { - public void startIntent(View v, Intent i) { - mActivityStarter.startActivity(i, true /* dismissShade */); - } - - public void startPendingIntent(PendingIntent pi) { - mActivityStarter.startPendingIntentDismissingKeyguard(pi); - } - }); - mSmartspaceView.setFalsingManager(mFalsingManager); - updateWallpaperColor(); - View asView = (View) mSmartspaceView; - // Place smartspace view below normal clock... RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( MATCH_PARENT, WRAP_CONTENT); lp.addRule(RelativeLayout.BELOW, R.id.lockscreen_clock_view); - mView.addView(asView, ksaIndex, lp); + mView.addView(mSmartspaceView, ksaIndex, lp); int padding = getContext().getResources() .getDimensionPixelSize(R.dimen.below_clock_padding_start); - asView.setPadding(padding, 0, padding, 0); + mSmartspaceView.setPadding(padding, 0, padding, 0); // ... but above the large clock lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT); - lp.addRule(RelativeLayout.BELOW, asView.getId()); + lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId()); mLargeClockFrame.setLayoutParams(lp); View nic = mView.findViewById( R.id.left_aligned_notification_icon_container); lp = (RelativeLayout.LayoutParams) nic.getLayoutParams(); - lp.addRule(RelativeLayout.BELOW, asView.getId()); + lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId()); nic.setLayoutParams(lp); - mSmartspaceSession = getContext().getSystemService(SmartspaceManager.class) - .createSmartspaceSession( - new SmartspaceConfig.Builder(getContext(), "lockscreen").build()); - mSmartspaceCallback = targets -> { - targets.removeIf(this::filterSmartspaceTarget); - smartspaceDataPlugin.onTargetsAvailable(targets); - }; - mSmartspaceSession.addOnTargetsAvailableListener(mUiExecutor, mSmartspaceCallback); - mSettingsObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange, Uri uri) { - reloadSmartspace(); - } - }; - - getContext().getContentResolver().registerContentObserver( - Settings.Secure.getUriFor( - Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), - true, mSettingsObserver, UserHandle.USER_ALL); - reloadSmartspace(); - } - - float dozeAmount = mStatusBarStateController.getDozeAmount(); - mStatusBarStateListener.onDozeAmountChanged(dozeAmount, dozeAmount); - } - - @VisibleForTesting - boolean filterSmartspaceTarget(SmartspaceTarget t) { - if (!t.isSensitive()) return false; - - if (t.getUserHandle().equals(mUserTracker.getUserHandle())) { - return !mShowSensitiveContentForCurrentUser; - } - if (t.getUserHandle().equals(mManagedUserHandle)) { - return !mShowSensitiveContentForManagedUser; - } - - return false; - } - - private void reloadSmartspace() { - mManagedUserHandle = getWorkProfileUser(); - final String setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS; - - mShowSensitiveContentForCurrentUser = - mSecureSettings.getIntForUser(setting, 0, mUserTracker.getUserId()) == 1; - if (mManagedUserHandle != null) { - int id = mManagedUserHandle.getIdentifier(); - mShowSensitiveContentForManagedUser = - mSecureSettings.getIntForUser(setting, 0, id) == 1; - } - - mSmartspaceSession.requestSmartspaceUpdate(); - } - - private UserHandle getWorkProfileUser() { - for (UserInfo userInfo : mUserTracker.getUserProfiles()) { - if (userInfo.isManagedProfile()) { - return userInfo.getUserHandle(); - } - } - return null; - } - - private void updateWallpaperColor() { - if (mSmartspaceView != null) { - int color = Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColor); - mSmartspaceView.setPrimaryTextColor(color); + mView.setSmartspaceView(mSmartspaceView); } } @@ -356,16 +198,16 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mColorExtractor.removeOnColorsChangedListener(mColorsListener); mView.setClockPlugin(null, mStatusBarStateController.getState()); - if (mSmartspaceSession != null) { - mSmartspaceSession.removeOnTargetsAvailableListener(mSmartspaceCallback); - mSmartspaceSession.close(); - mSmartspaceSession = null; - } - mStatusBarStateController.removeCallback(mStatusBarStateListener); - mConfigurationController.removeCallback(mConfigurationListener); + mSmartspaceController.disconnect(); - if (mSettingsObserver != null) { - getContext().getContentResolver().unregisterContentObserver(mSettingsObserver); + // TODO: This is an unfortunate necessity since smartspace plugin retains a single instance + // of the smartspace view -- if we don't remove the view, it can't be reused by a later + // instance of this class. In order to fix this, we need to modify the plugin so that + // (a) we get a new view each time and (b) we can properly clean up an old view by making + // it unregister itself as a plugin listener. + if (mSmartspaceView != null) { + mView.removeView(mSmartspaceView); + mSmartspaceView = null; } } @@ -380,7 +222,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS * Set whether or not the lock screen is showing notifications. */ public void setHasVisibleNotifications(boolean hasVisibleNotifications) { - mView.setHasVisibleNotifications(hasVisibleNotifications); + if (mView.willSwitchToLargeClock(hasVisibleNotifications)) { + mLargeClockViewController.animateAppear(); + } } /** @@ -436,7 +280,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS scale, props, animate); if (mSmartspaceView != null) { - PropertyAnimator.setProperty((View) mSmartspaceView, AnimatableProperty.TRANSLATION_X, + PropertyAnimator.setProperty(mSmartspaceView, AnimatableProperty.TRANSLATION_X, x, props, animate); } @@ -510,14 +354,4 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private int getCurrentLayoutDirection() { return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()); } - - @VisibleForTesting - ConfigurationController.ConfigurationListener getConfigurationListener() { - return mConfigurationListener; - } - - @VisibleForTesting - ContentObserver getSettingsObserver() { - return mSettingsObserver; - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt index ab15630a6975..ff20805c5ea4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt @@ -12,6 +12,7 @@ data class KeyguardFaceListenModel( val isListeningForFace: Boolean, val isBouncer: Boolean, val isAuthInterruptActive: Boolean, + val isOccludingAppRequestingFaceAuth: Boolean, val isKeyguardAwake: Boolean, val isListeningForFaceAssistant: Boolean, val isSwitchingUser: Boolean, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 588f4bb20340..3a3f2fc40b25 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -51,8 +51,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV private final KeyguardVisibilityHelper mKeyguardVisibilityHelper; private final Rect mClipBounds = new Rect(); - private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; - @Inject public KeyguardStatusViewController( KeyguardStatusView keyguardStatusView, @@ -192,28 +190,11 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV * Update position of the view with an optional animation */ public void updatePosition(int x, int y, float scale, boolean animate) { - // We animate the status view visible/invisible using Y translation, so don't change it - // while the animation is running. - if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) { - PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES, - animate); - } - - if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { - // reset any prior movement - PropertyAnimator.setProperty(mView, AnimatableProperty.X, 0, - CLOCK_ANIMATION_PROPERTIES, animate); + PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES, + animate); - mKeyguardClockSwitchController.updatePosition(x, scale, CLOCK_ANIMATION_PROPERTIES, - animate); - } else { - // reset any prior movement - mKeyguardClockSwitchController.updatePosition(0, 0f, CLOCK_ANIMATION_PROPERTIES, - animate); - - PropertyAnimator.setProperty(mView, AnimatableProperty.X, x, - CLOCK_ANIMATION_PROPERTIES, animate); - } + mKeyguardClockSwitchController.updatePosition(x, scale, CLOCK_ANIMATION_PROPERTIES, + animate); } /** @@ -254,7 +235,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { @Override public void onLockScreenModeChanged(int mode) { - mLockScreenMode = mode; mKeyguardSliceViewController.updateLockScreenMode(mode); mView.setCanShowOwnerInfo(false); mView.updateLogoutView(false); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 321c6b75d671..64bc77f3f03f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -276,6 +276,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private boolean mHasLockscreenWallpaper; private boolean mAssistantVisible; private boolean mKeyguardOccluded; + private boolean mOccludingAppRequestingFp; + private boolean mOccludingAppRequestingFace; private boolean mSecureCameraLaunched; @VisibleForTesting protected boolean mTelephonyCapable; @@ -587,6 +589,29 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab updateBiometricListeningState(); } + + /** + * Request to listen for face authentication when an app is occluding keyguard. + * @param request if true and mKeyguardOccluded, request face auth listening, else default + * to normal behavior. + * See {@link KeyguardUpdateMonitor#shouldListenForFace()} + */ + public void requestFaceAuthOnOccludingApp(boolean request) { + mOccludingAppRequestingFace = request; + updateFaceListeningState(); + } + + /** + * Request to listen for fingerprint when an app is occluding keyguard. + * @param request if true and mKeyguardOccluded, request fingerprint listening, else default + * to normal behavior. + * See {@link KeyguardUpdateMonitor#shouldListenForFingerprint(boolean)} + */ + public void requestFingerprintAuthOnOccludingApp(boolean request) { + mOccludingAppRequestingFp = request; + updateFingerprintListeningState(); + } + /** * Invoked when the secure camera is launched. */ @@ -2093,14 +2118,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting protected boolean shouldListenForFingerprint(boolean isUdfps) { + final boolean userDoesNotHaveTrust = !getUserHasTrust(getCurrentUser()); final boolean shouldListenKeyguardState = - mKeyguardIsVisible - || !mDeviceInteractive - || (mBouncer && !mKeyguardGoingAway) - || mGoingToSleep - || shouldListenForFingerprintAssistant() - || (mKeyguardOccluded && mIsDreaming) - || (isUdfps && mKeyguardOccluded); + mKeyguardIsVisible + || !mDeviceInteractive + || (mBouncer && !mKeyguardGoingAway) + || mGoingToSleep + || shouldListenForFingerprintAssistant() + || (mKeyguardOccluded && mIsDreaming) + || (mKeyguardOccluded && userDoesNotHaveTrust + && (mOccludingAppRequestingFp || isUdfps)); // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware. @@ -2116,8 +2143,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean shouldListenUdfpsState = !isUdfps || (!getUserCanSkipBouncer(getCurrentUser()) - && !isEncryptedOrLockdown(getCurrentUser()) - && mStrongAuthTracker.hasUserAuthenticatedSinceBoot()); + && !isEncryptedOrLockdown(getCurrentUser()) + && mStrongAuthTracker.hasUserAuthenticatedSinceBoot() + && userDoesNotHaveTrust); return shouldListenKeyguardState && shouldListenUserState && shouldListenBouncerState && shouldListenUdfpsState; @@ -2166,7 +2194,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware. final boolean shouldListen = - (mBouncer || mAuthInterruptActive || awakeKeyguard + (mBouncer || mAuthInterruptActive || mOccludingAppRequestingFace || awakeKeyguard || shouldListenForFaceAssistant()) && !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer && !mKeyguardGoingAway && mBiometricEnabledForUser.get(user) && !mLockIconPressed @@ -2181,6 +2209,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab shouldListen, mBouncer, mAuthInterruptActive, + mOccludingAppRequestingFace, awakeKeyguard, shouldListenForFaceAssistant(), mSwitchingUser, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java index 947f14138cd7..6aca5a702a1a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java @@ -36,6 +36,7 @@ public class KeyguardVisibilityHelper { private final KeyguardStateController mKeyguardStateController; private final DozeParameters mDozeParameters; private boolean mKeyguardViewVisibilityAnimating; + private boolean mLastOccludedState = false; public KeyguardVisibilityHelper(View view, KeyguardStateController keyguardStateController, DozeParameters dozeParameters) { @@ -57,6 +58,7 @@ public class KeyguardVisibilityHelper { boolean goingToFullShade, int oldStatusBarState) { mView.animate().cancel(); + boolean isOccluded = mKeyguardStateController.isOccluded(); mKeyguardViewVisibilityAnimating = false; if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD && statusBarState != KEYGUARD) || goingToFullShade) { @@ -95,6 +97,17 @@ public class KeyguardVisibilityHelper { .setStartDelay(0) .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable) .start(); + } else if (mLastOccludedState && !isOccluded) { + // An activity was displayed over the lock screen, and has now gone away + mView.setVisibility(View.VISIBLE); + mView.setAlpha(0f); + + mView.animate() + .setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .alpha(1f) + .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable) + .start(); } else if (mDozeParameters.shouldControlUnlockedScreenOff()) { mKeyguardViewVisibilityAnimating = true; @@ -119,6 +132,8 @@ public class KeyguardVisibilityHelper { mView.setVisibility(View.GONE); mView.setAlpha(1f); } + + mLastOccludedState = isOccluded; } private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = () -> { diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index b5f2ab213559..b367bdf08886 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -50,6 +50,7 @@ import com.android.systemui.util.ViewController; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Objects; import javax.inject.Inject; @@ -75,6 +76,9 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @NonNull private final Drawable mButton; @NonNull private final Drawable mUnlockIcon; @NonNull private final Drawable mLockIcon; + @NonNull private final CharSequence mDisabledLabel; + @NonNull private final CharSequence mUnlockedLabel; + @NonNull private final CharSequence mLockedLabel; private boolean mIsDozing; private boolean mIsBouncerShowing; @@ -121,6 +125,10 @@ public class LockIconViewController extends ViewController<LockIconView> impleme com.android.internal.R.drawable.ic_lock, context.getTheme()), context.getResources().getDimensionPixelSize( com.android.systemui.R.dimen.udfps_unlock_icon_inset)); + mDisabledLabel = context.getResources().getString( + R.string.accessibility_udfps_disabled_button); + mUnlockedLabel = context.getResources().getString(R.string.accessibility_unlock_button); + mLockedLabel = context.getResources().getString(R.string.accessibility_lock_icon); dumpManager.registerDumpable("LockIconViewController", this); } @@ -225,25 +233,27 @@ public class LockIconViewController extends ViewController<LockIconView> impleme && mFaceAuthEnrolled; updateClickListener(); + final CharSequence prevContentDescription = mView.getContentDescription(); if (mShowButton) { mView.setImageDrawable(mButton); mView.setVisibility(View.VISIBLE); - mView.setContentDescription(getResources().getString( - R.string.accessibility_udfps_disabled_button)); + mView.setContentDescription(mDisabledLabel); } else if (mShowUnlockIcon) { mView.setImageDrawable(mUnlockIcon); mView.setVisibility(View.VISIBLE); - mView.setContentDescription(getResources().getString( - R.string.accessibility_unlock_button)); + mView.setContentDescription(mUnlockedLabel); } else if (mShowLockIcon) { mView.setImageDrawable(mLockIcon); mView.setVisibility(View.VISIBLE); - mView.setContentDescription(getResources().getString( - R.string.accessibility_lock_icon)); + mView.setContentDescription(mLockedLabel); } else { mView.setVisibility(View.INVISIBLE); mView.setContentDescription(null); } + if (!Objects.equals(prevContentDescription, mView.getContentDescription()) + && mView.getContentDescription() != null) { + mView.announceForAccessibility(mView.getContentDescription()); + } } private final View.AccessibilityDelegate mAccessibilityDelegate = @@ -258,20 +268,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme getResources().getString(R.string.accessibility_enter_hint)); public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(v, info); - removeAllActions(info); if (mShowButton || mShowLockIcon) { info.addAction(mAccessibilityAuthenticateHint); } else if (mShowUnlockIcon) { info.addAction(mAccessibilityEnterHint); } } - - private void removeAllActions(AccessibilityNodeInfo info) { - info.removeAction(mAccessibilityAuthenticateHint); - info.removeAction(mAccessibilityEnterHint); - info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK); - mView.setLongClickable(false); - } }; private boolean isLockScreen() { @@ -286,6 +288,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mView.setOnClickListener(v -> onAffordanceClick()); if (mAccessibilityManager.isTouchExplorationEnabled()) { mView.setOnLongClickListener(null); + mView.setLongClickable(false); } else { mView.setOnLongClickListener(v -> onAffordanceClick()); } 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 49e6b47b3b79..55f398196c8d 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java @@ -79,7 +79,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout private static final int FADE_OUT_DURATION_MS = 1000; private static final int FADE_EFFECT_DURATION_MS = 3000; private static final int SNAP_TO_LOCATION_DURATION_MS = 150; - private static final int MIN_WINDOW_X = 0; private static final int MIN_WINDOW_Y = 0; private static final float LOCATION_Y_PERCENTAGE = 0.8f; @@ -212,7 +211,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout mPercentageY = calculateCurrentPercentageY(); updateLocationWith(mAlignment, mPercentageY); - updateMarginsWith(mAlignment); updateInsetWith(getResources().getConfiguration().uiMode, mAlignment); @@ -265,7 +263,8 @@ public class AccessibilityFloatingMenuView extends FrameLayout : ShapeType.OVAL; final int newWindowX = currentRawX + mRelativeToPointerDownX; final int newWindowY = currentRawY + mRelativeToPointerDownY; - mCurrentLayoutParams.x = constrain(newWindowX, MIN_WINDOW_X, getMaxWindowX()); + mCurrentLayoutParams.x = + constrain(newWindowX, getMinWindowX(), getMaxWindowX()); mCurrentLayoutParams.y = constrain(newWindowY, MIN_WINDOW_Y, getMaxWindowY()); mWindowManager.updateViewLayout(this, mCurrentLayoutParams); } @@ -275,9 +274,10 @@ public class AccessibilityFloatingMenuView extends FrameLayout if (mIsDragging) { mIsDragging = false; + final int minX = getMinWindowX(); final int maxX = getMaxWindowX(); - final int endX = mCurrentLayoutParams.x > ((MIN_WINDOW_X + maxX) / 2) - ? maxX : MIN_WINDOW_X; + final int endX = mCurrentLayoutParams.x > ((minX + maxX) / 2) + ? maxX : minX; final int endY = mCurrentLayoutParams.y; snapToLocation(endX, endY); @@ -631,6 +631,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout final LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + layoutParams.setMargins(mMargin, mMargin, mMargin, mMargin); mListView.setLayoutParams(layoutParams); final InstantInsetLayerDrawable layerDrawable = new InstantInsetLayerDrawable(new Drawable[]{background}); @@ -648,8 +649,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout final int elevation = getResources().getDimensionPixelSize(R.dimen.accessibility_floating_menu_elevation); mListView.setElevation(elevation); - - updateMarginsWith(mAlignment); } private WindowManager.LayoutParams createDefaultLayoutParams() { @@ -657,7 +656,8 @@ public class AccessibilityFloatingMenuView extends FrameLayout WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSLUCENT); params.windowAnimations = android.R.style.Animation_Translucent; params.gravity = Gravity.START | Gravity.TOP; @@ -704,6 +704,10 @@ public class AccessibilityFloatingMenuView extends FrameLayout mWindowManager.updateViewLayout(this, mCurrentLayoutParams); } + private int getMinWindowX() { + return -mMargin; + } + private int getMaxWindowX() { return mScreenWidth - mMargin - getLayoutWidth(); } @@ -724,7 +728,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout * Updates the floating menu to be fixed at the side of the screen. */ private void updateLocationWith(@Alignment int side, float percentageCurrentY) { - mCurrentLayoutParams.x = (side == Alignment.RIGHT) ? getMaxWindowX() : MIN_WINDOW_X; + mCurrentLayoutParams.x = (side == Alignment.RIGHT) ? getMaxWindowX() : getMinWindowX(); mCurrentLayoutParams.y = (int) (percentageCurrentY * getMaxWindowY()); mWindowManager.updateViewLayout(this, mCurrentLayoutParams); } @@ -735,20 +739,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout mListView.animate().translationX(side == Alignment.RIGHT ? offset : -offset); } - private void updateMarginsWith(@Alignment int side) { - final LayoutParams layoutParams = (LayoutParams) mListView.getLayoutParams(); - final int marginLeft = (side == Alignment.LEFT) ? 0 : mMargin; - final int marginRight = (side == Alignment.RIGHT) ? 0 : mMargin; - - if (marginLeft == layoutParams.leftMargin - && marginRight == layoutParams.rightMargin) { - return; - } - - layoutParams.setMargins(marginLeft, mMargin, marginRight, mMargin); - mListView.setLayoutParams(layoutParams); - } - private void updateScrollModeWith(boolean hasExceededMaxLayoutHeight) { mListView.setOverScrollMode(hasExceededMaxLayoutHeight ? OVER_SCROLL_ALWAYS @@ -814,7 +804,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout @Alignment private int calculateCurrentAlignment() { - return mCurrentLayoutParams.x >= ((MIN_WINDOW_X + getMaxWindowX()) / 2) + return mCurrentLayoutParams.x >= ((getMinWindowX() + getMaxWindowX()) / 2) ? Alignment.RIGHT : Alignment.LEFT; } @@ -863,7 +853,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout } private int getWindowWidth() { - return mMargin + getLayoutWidth(); + return mMargin * 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 875bfdbffff3..e8300b92a376 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -338,7 +338,7 @@ public class UdfpsController implements DozeReceiver { switch (event.getActionMasked()) { case MotionEvent.ACTION_OUTSIDE: udfpsView.onTouchOutsideView(); - break; + return true; case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_HOVER_ENTER: // To simplify the lifecycle of the velocity tracker, make sure it's never null @@ -602,6 +602,8 @@ public class UdfpsController implements DozeReceiver { default: // Do nothing to stay in portrait mode. } + // avoid announcing window title + mCoreLayoutParams.accessibilityTitle = " "; return mCoreLayoutParams; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java index cd5abd74c260..83ae865d7427 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java @@ -16,6 +16,8 @@ package com.android.systemui.biometrics; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; @@ -24,6 +26,9 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.view.animation.AccelerateDecelerateInterpolator; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -36,19 +41,35 @@ import com.android.systemui.R; public class UdfpsEnrollDrawable extends UdfpsDrawable { private static final String TAG = "UdfpsAnimationEnroll"; - static final float PROGRESS_BAR_RADIUS = 180.f; + static final float PROGRESS_BAR_RADIUS = 360.f; + + private static final long ANIM_DURATION = 800; + // 1 + SCALE_MAX is the maximum that the moving target will animate to + private static final float SCALE_MAX = 0.25f; @NonNull private final Drawable mMovingTargetFpIcon; @NonNull private final Paint mSensorOutlinePaint; @NonNull private final Paint mBlueFill; @NonNull private final Paint mBlueStroke; + @NonNull private final Handler mHandler; @Nullable private RectF mSensorRect; @Nullable private UdfpsEnrollHelper mEnrollHelper; + // Moving target animator set + @Nullable AnimatorSet mAnimatorSet; + // Moving target location + float mCurrentX; + float mCurrentY; + // Moving target size + float mCurrentScale = 1.f; + + UdfpsEnrollDrawable(@NonNull Context context) { super(context); + mHandler = new Handler(Looper.getMainLooper()); + mSensorOutlinePaint = new Paint(0 /* flags */); mSensorOutlinePaint.setAntiAlias(true); mSensorOutlinePaint.setColor(mContext.getColor(R.color.udfps_enroll_icon)); @@ -90,26 +111,60 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { invalidateSelf(); } + void onEnrollmentProgress(int remaining, int totalSteps) { + if (mEnrollHelper.isCenterEnrollmentComplete()) { + mHandler.post(() -> { + if (mAnimatorSet != null && mAnimatorSet.isRunning()) { + mAnimatorSet.end(); + } + + final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint(); + + final ValueAnimator x = ValueAnimator.ofFloat(mCurrentX, point.x); + x.addUpdateListener(animation -> { + mCurrentX = (float) animation.getAnimatedValue(); + invalidateSelf(); + }); + + final ValueAnimator y = ValueAnimator.ofFloat(mCurrentY, point.y); + y.addUpdateListener(animation -> { + mCurrentY = (float) animation.getAnimatedValue(); + invalidateSelf(); + }); + + final ValueAnimator scale = ValueAnimator.ofFloat(0, (float) Math.PI); + scale.setDuration(ANIM_DURATION); + scale.addUpdateListener(animation -> { + // Grow then shrink + mCurrentScale = 1 + + SCALE_MAX * (float) Math.sin((float) animation.getAnimatedValue()); + invalidateSelf(); + }); + + mAnimatorSet = new AnimatorSet(); + + mAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator()); + mAnimatorSet.setDuration(ANIM_DURATION); + mAnimatorSet.playTogether(x, y, scale); + mAnimatorSet.start(); + }); + } + } + @Override public void draw(@NonNull Canvas canvas) { if (isIlluminationShowing()) { return; } - if (mSensorRect != null) { - canvas.drawOval(mSensorRect, mSensorOutlinePaint); - } - mFingerprintDrawable.draw(canvas); - // Draw moving target if (mEnrollHelper.isCenterEnrollmentComplete()) { - mFingerprintDrawable.setAlpha(mAlpha == 255 ? 64 : mAlpha); - mSensorOutlinePaint.setAlpha(mAlpha == 255 ? 64 : mAlpha); - canvas.save(); - final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint(); - canvas.translate(point.x, point.y); + canvas.translate(mCurrentX, mCurrentY); + if (mSensorRect != null) { + canvas.scale(mCurrentScale, mCurrentScale, + mSensorRect.centerX(), mSensorRect.centerY()); canvas.drawOval(mSensorRect, mBlueFill); canvas.drawOval(mSensorRect, mBlueStroke); } @@ -117,6 +172,10 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable { mMovingTargetFpIcon.draw(canvas); canvas.restore(); } else { + if (mSensorRect != null) { + canvas.drawOval(mSensorRect, mSensorOutlinePaint); + } + mFingerprintDrawable.draw(canvas); mFingerprintDrawable.setAlpha(mAlpha); mSensorOutlinePaint.setAlpha(mAlpha); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java index 75e8638e43df..3bf1864aca4c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java @@ -60,4 +60,8 @@ public class UdfpsEnrollView extends UdfpsAnimationView { void setEnrollHelper(UdfpsEnrollHelper enrollHelper) { mFingerprintDrawable.setEnrollHelper(enrollHelper); } + + void onEnrollmentProgress(int remaining, int totalSteps) { + mFingerprintDrawable.onEnrollmentProgress(remaining, totalSteps); + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java index 1ebbfbf84814..c1f3fe0ffbfd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java @@ -94,6 +94,7 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController<Udfp * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1); mProgressBar.setProgress(interpolatedProgress, true); + mView.onEnrollmentProgress(remaining, totalSteps); } }; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 33d0d0c5b5ff..a0ac2dc7289e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -27,6 +27,7 @@ import androidx.annotation.Nullable; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.R; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -165,8 +166,10 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud updatePauseAuth(); if (mShowingUdfpsBouncer) { mView.animateUdfpsBouncer(); + mView.announceForAccessibility(mView.getContext().getString( + R.string.accessibility_fingerprint_bouncer)); } else { - mView.animateAwayUdfpsBouncer(() -> mKeyguardViewManager.cancelPostAuthActions()); + mView.animateAwayUdfpsBouncer(null); } return true; } @@ -228,8 +231,8 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud */ private void maybeShowInputBouncer() { if (mShowingUdfpsBouncer) { - mKeyguardViewManager.resetAlternateAuth(false); mKeyguardViewManager.showBouncer(true); + mKeyguardViewManager.resetAlternateAuth(false); } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index a904cefc48a0..26be98743eed 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -120,7 +120,7 @@ class ControlsUiControllerImpl @Inject constructor ( private val onSeedingComplete = Consumer<Boolean> { accepted -> if (accepted) { - selectedStructure = controlsController.get().getFavorites().maxByOrNull { + selectedStructure = controlsController.get().getFavorites().maxBy { it.controls.size } ?: EMPTY_STRUCTURE updatePreferences(selectedStructure) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index fd80d50c2894..26db33d6dea8 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -29,6 +29,7 @@ import android.app.StatsManager; import android.app.WallpaperManager; import android.app.admin.DevicePolicyManager; import android.app.role.RoleManager; +import android.app.smartspace.SmartspaceManager; import android.app.trust.TrustManager; import android.content.ContentResolver; import android.content.Context; @@ -400,4 +401,10 @@ public class FrameworkServicesModule { static PermissionManager providePermissionManager(Context context) { return context.getSystemService(PermissionManager.class); } + + @Provides + @Singleton + static SmartspaceManager provideSmartspaceManager(Context context) { + return context.getSystemService(SmartspaceManager.class); + } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 3e9559b1a9ec..c608dc78e583 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -428,7 +428,7 @@ public class MediaControlPanel { mMediaDataManagerLazy.get().dismissMediaData(mKey, MediaViewController.GUTS_ANIMATION_DURATION + 100); return true; - }, /* requiresShadeOpen */ true); + }, /* requiresShadeOpen */ true, false); } else { Log.w(TAG, "Dismiss media with null notification. Token uid=" + data.getToken().getUid()); @@ -564,7 +564,7 @@ public class MediaControlPanel { mMediaDataManagerLazy.get().dismissSmartspaceRecommendation( MediaViewController.GUTS_ANIMATION_DURATION + 100L); return true; - }, true /* requiresShadeOpen */); + }, true /* requiresShadeOpen */, false); }); mController = null; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index 9c3cb2e4604d..ae9b5987bac5 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -184,6 +184,7 @@ public class NavigationBarController implements Callbacks, mNavMode = mNavigationModeController.addListener(this); mNavigationModeController.addListener(this); mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService); + mIsTablet = isTablet(mContext.getResources().getConfiguration()); } @Override @@ -297,6 +298,10 @@ public class NavigationBarController implements Callbacks, */ public void createNavigationBars(final boolean includeDefaultDisplay, RegisterStatusBarResult result) { + if (updateNavbarForTaskbar()) { + return; + } + Display[] displays = mDisplayManager.getDisplays(); for (Display display : displays) { if (includeDefaultDisplay || display.getDisplayId() != DEFAULT_DISPLAY) { diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt index eec69f98b9be..1d2e74703b42 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt @@ -28,7 +28,7 @@ class PrivacyChipBuilder(private val context: Context, itemsList: List<PrivacyIt appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType }) .toList() .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps - { it.second.minOrNull() })) // Sort by "smallest" AppOpp (Location is largest) + { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest) types = itemsList.map { it.privacyType }.distinct().sorted() } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index c459963f7217..3a3f3f1ca7ea 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -129,6 +129,12 @@ public class QSContainerImpl extends FrameLayout { @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { mNavBarInset = insets.getInsets(WindowInsets.Type.navigationBars()).bottom; + mQSPanelContainer.setPaddingRelative( + mQSPanelContainer.getPaddingStart(), + mQSPanelContainer.getPaddingTop(), + mQSPanelContainer.getPaddingEnd(), + mNavBarInset + ); return super.onApplyWindowInsets(insets); } @@ -138,8 +144,7 @@ public class QSContainerImpl extends FrameLayout { // bottom and footer are inside the screen. MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams(); - int availableScreenHeight = getDisplayHeight() - mNavBarInset; - int maxQs = availableScreenHeight - layoutParams.topMargin - layoutParams.bottomMargin + int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin - getPaddingBottom(); int padding = mPaddingLeft + mPaddingRight + layoutParams.leftMargin + layoutParams.rightMargin; @@ -148,10 +153,8 @@ public class QSContainerImpl extends FrameLayout { mQSPanelContainer.measure(qsPanelWidthSpec, MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST)); int width = mQSPanelContainer.getMeasuredWidth() + padding; - int height = layoutParams.topMargin + layoutParams.bottomMargin - + mQSPanelContainer.getMeasuredHeight() + getPaddingBottom(); super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(availableScreenHeight, MeasureSpec.EXACTLY)); + MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY)); // QSCustomizer will always be the height of the screen, but do this after // other measuring to avoid changing the height of the QS. mQSCustomizer.measure(widthMeasureSpec, @@ -196,13 +199,10 @@ public class QSContainerImpl extends FrameLayout { void updateResources(QSPanelController qsPanelController, QuickStatusBarHeaderController quickStatusBarHeaderController) { - mQSPanelContainer.setPaddingRelative( - mQSPanelContainer.getPaddingStart(), - mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.quick_qs_offset_height), - mQSPanelContainer.getPaddingEnd(), - mContext.getResources().getDimensionPixelSize(R.dimen.qs_container_bottom_padding) - ); + LayoutParams layoutParams = (LayoutParams) mQSPanelContainer.getLayoutParams(); + layoutParams.topMargin = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.quick_qs_offset_height); + mQSPanelContainer.setLayoutParams(layoutParams); int sideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings); int padding = getResources().getDimensionPixelSize( diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java index 05197e46fb25..0335319f5b49 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java @@ -30,6 +30,7 @@ import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; import android.view.ViewStub; +import android.view.WindowInsets; import android.view.accessibility.AccessibilityEvent; import android.widget.ImageView; import android.widget.LinearLayout; @@ -153,11 +154,18 @@ public class QSDetail extends LinearLayout { MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); lp.topMargin = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.quick_qs_offset_height); - lp.bottomMargin = mContext.getResources().getDimensionPixelSize( - R.dimen.qs_container_bottom_padding); setLayoutParams(lp); } + @Override + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + int bottomNavBar = insets.getInsets(WindowInsets.Type.navigationBars()).bottom; + MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); + lp.bottomMargin = bottomNavBar; + setLayoutParams(lp); + return super.onApplyWindowInsets(insets); + } + public boolean isClosingDetail() { return mClosingDetail; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 26332f41f675..81b5318d8089 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -23,6 +23,7 @@ import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; import android.util.AttributeSet; +import android.util.FeatureFlagUtils; import android.util.Pair; import android.view.DisplayCutout; import android.view.View; @@ -87,13 +88,21 @@ public class QuickStatusBarHeader extends FrameLayout { private int mTopViewMeasureHeight; private final String mMobileSlotName; + private final String mNoCallingSlotName; private final String mCallStrengthSlotName; + private final boolean mProviderModel; public QuickStatusBarHeader(Context context, AttributeSet attrs) { super(context, attrs); - mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_no_calling); + mMobileSlotName = context.getString(com.android.internal.R.string.status_bar_mobile); + mNoCallingSlotName = context.getString(com.android.internal.R.string.status_bar_no_calling); mCallStrengthSlotName = context.getString(com.android.internal.R.string.status_bar_call_strength); + if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) { + mProviderModel = true; + } else { + mProviderModel = false; + } } /** @@ -242,14 +251,24 @@ public class QuickStatusBarHeader extends FrameLayout { .setListener(new TouchAnimator.ListenerAdapter() { @Override public void onAnimationAtEnd() { - mIconContainer.addIgnoredSlot(mMobileSlotName); - mIconContainer.addIgnoredSlot(mCallStrengthSlotName); + // TODO(b/185580157): Remove the mProviderModel if the mobile slot can be + // hidden in Provider model. + if (mProviderModel) { + mIconContainer.addIgnoredSlot(mNoCallingSlotName); + mIconContainer.addIgnoredSlot(mCallStrengthSlotName); + } else { + mIconContainer.addIgnoredSlot(mMobileSlotName); + } } @Override public void onAnimationStarted() { - mIconContainer.addIgnoredSlot(mMobileSlotName); - mIconContainer.addIgnoredSlot(mCallStrengthSlotName); + if (mProviderModel) { + mIconContainer.addIgnoredSlot(mNoCallingSlotName); + mIconContainer.addIgnoredSlot(mCallStrengthSlotName); + } else { + mIconContainer.addIgnoredSlot(mMobileSlotName); + } setSeparatorVisibility(false); } @@ -257,8 +276,12 @@ public class QuickStatusBarHeader extends FrameLayout { @Override public void onAnimationAtStart() { super.onAnimationAtStart(); - mIconContainer.removeIgnoredSlot(mMobileSlotName); - mIconContainer.removeIgnoredSlot(mCallStrengthSlotName); + if (mProviderModel) { + mIconContainer.removeIgnoredSlot(mNoCallingSlotName); + mIconContainer.removeIgnoredSlot(mCallStrengthSlotName); + } else { + mIconContainer.removeIgnoredSlot(mMobileSlotName); + } setSeparatorVisibility(mShowClockIconsSeparator); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt index 705576059b58..2bac29846893 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt @@ -45,13 +45,15 @@ class IgnorableChildLinearLayout @JvmOverloads constructor( super.onMeasure(widthMeasureSpec, heightMeasureSpec) if (ignoreLastView && childCount > 0) { val lastView = getChildAt(childCount - 1) - val lp = lastView.layoutParams as MarginLayoutParams - if (orientation == VERTICAL) { - val height = lastView.measuredHeight + lp.bottomMargin + lp.topMargin - setMeasuredDimension(measuredWidth, measuredHeight - height) - } else { - val width = lastView.measuredWidth + lp.leftMargin + lp.rightMargin - setMeasuredDimension(measuredWidth - width, measuredHeight) + if (lastView.visibility != GONE) { + val lp = lastView.layoutParams as MarginLayoutParams + if (orientation == VERTICAL) { + val height = lastView.measuredHeight + lp.bottomMargin + lp.topMargin + setMeasuredDimension(measuredWidth, measuredHeight - height) + } else { + val width = lastView.measuredWidth + lp.leftMargin + lp.rightMargin + setMeasuredDimension(measuredWidth - width, measuredHeight) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index 85b835ae7ac9..e46792555101 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -152,7 +152,8 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { @Override protected void handleUpdateState(State state, Object arg) { - state.label = mLabel; + CharSequence label = mQuickAccessWalletClient.getServiceLabel(); + state.label = label == null ? mLabel : label; state.contentDescription = state.label; state.icon = ResourceIcon.get(R.drawable.ic_wallet_lockscreen); boolean isDeviceLocked = !mKeyguardStateController.isUnlocked(); @@ -197,7 +198,8 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { @Override public CharSequence getTileLabel() { - return mLabel; + CharSequence label = mQuickAccessWalletClient.getServiceLabel(); + return label == null ? mLabel : label; } private void queryWalletCards() { @@ -227,7 +229,9 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { } int selectedIndex = response.getSelectedIndex(); if (selectedIndex >= cards.size()) { - Log.d(TAG, "Selected card index out of bounds."); + Log.w(TAG, "Error retrieving cards: Invalid selected card index."); + mSelectedCard = null; + mCardViewDrawable = null; return; } mSelectedCard = cards.get(selectedIndex); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java index a4148eec3d82..32a6c6c20504 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java @@ -142,7 +142,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> mHost.getUserContext().startActivity(intent); return false; }; - mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false); + mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false, false); } private void cancelCountdown() { diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index 4300d3734a20..2f0bbdb88c1c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -189,7 +189,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis // Remove notification mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser); return false; - }, false); + }, false, false); // Close quick shade sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt index 7c0496bd8b72..06c1c6f8cefa 100644 --- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt @@ -181,7 +181,7 @@ class SensorUseStartedActivity @Inject constructor( keyguardDismissUtil.executeWhenUnlocked({ disableSensorPrivacy() false - }, false) + }, false, false) } else { disableSensorPrivacy() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index d96e1ba22ecc..ac6d9e8820ad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -92,4 +92,8 @@ public class FeatureFlags { public boolean isSmartspaceEnabled() { return mFlagReader.isEnabled(R.bool.flag_smartspace); } + + public boolean isSmartspaceDedupingEnabled() { + return isSmartspaceEnabled() && mFlagReader.isEnabled(R.bool.flag_smartspace_deduping); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 5daee6c2b924..91a0e6fedef8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -33,7 +33,6 @@ import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.app.ActivityManager; import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; @@ -147,6 +146,7 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal private long mChargingTimeRemaining; private String mMessageToShowOnScreenOn; protected int mLockScreenMode; + private boolean mInited; private KeyguardUpdateMonitorCallback mUpdateMonitorCallback; @@ -174,7 +174,9 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal IBatteryStats iBatteryStats, UserManager userManager, @Main DelayableExecutor executor, - FalsingManager falsingManager) { + FalsingManager falsingManager, + LockPatternUtils lockPatternUtils, + IActivityManager iActivityManager) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; mDevicePolicyManager = devicePolicyManager; @@ -182,17 +184,26 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal mStatusBarStateController = statusBarStateController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mDockManager = dockManager; - mDockManager.addAlignmentStateListener( - alignState -> mHandler.post(() -> handleAlignStateChanged(alignState))); mWakeLock = new SettableWakeLock( wakeLockBuilder.setTag("Doze:KeyguardIndication").build(), TAG); mBatteryInfo = iBatteryStats; mUserManager = userManager; mExecutor = executor; - mLockPatternUtils = new LockPatternUtils(context); - mIActivityManager = ActivityManager.getService(); + mLockPatternUtils = lockPatternUtils; + mIActivityManager = iActivityManager; mFalsingManager = falsingManager; + } + + /** Call this after construction to finish setting up the instance. */ + public void init() { + if (mInited) { + return; + } + mInited = true; + + mDockManager.addAlignmentStateListener( + alignState -> mHandler.post(() -> handleAlignStateChanged(alignState))); mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback()); mKeyguardUpdateMonitor.registerCallback(mTickReceiver); mStatusBarStateController.addCallback(mStatusBarStateListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java index 1c5df4119a24..5b694973d7f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java @@ -430,7 +430,6 @@ public class NotificationGroupingUtil { private static class LeftIconApplicator implements ResultApplicator { public static final int[] MARGIN_ADJUSTED_VIEWS = { - R.id.notification_headerless_view_column, R.id.text, R.id.big_text, R.id.title, @@ -438,22 +437,31 @@ public class NotificationGroupingUtil { R.id.notification_header}; @Override - public void apply(View parent, View child, boolean apply, boolean reset) { - ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon); + public void apply(View parent, View child, boolean showLeftIcon, boolean reset) { ImageView leftIcon = child.findViewById(com.android.internal.R.id.left_icon); - if (rightIcon == null || leftIcon == null) { + if (leftIcon == null) { return; } - Drawable iconDrawable = rightIcon.getDrawable(); - if (iconDrawable == null) { - return; + ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon); + boolean keepRightIcon = rightIcon != null && Integer.valueOf(1).equals( + rightIcon.getTag(R.id.tag_keep_when_showing_left_icon)); + boolean leftIconUsesRightIconDrawable = Integer.valueOf(1).equals( + leftIcon.getTag(R.id.tag_uses_right_icon_drawable)); + if (leftIconUsesRightIconDrawable) { + // Use the right drawable when showing the left, unless the right is being kept + Drawable rightDrawable = rightIcon == null ? null : rightIcon.getDrawable(); + leftIcon.setImageDrawable(showLeftIcon && !keepRightIcon ? rightDrawable : null); } - rightIcon.setVisibility(apply ? View.GONE : View.VISIBLE); - leftIcon.setVisibility(apply ? View.VISIBLE : View.GONE); - leftIcon.setImageDrawable(apply ? iconDrawable : null); - - for (int viewId : MARGIN_ADJUSTED_VIEWS) { - adjustMargins(!apply, child.findViewById(viewId)); + leftIcon.setVisibility(showLeftIcon ? View.VISIBLE : View.GONE); + + // update the right icon as well + if (rightIcon != null) { + boolean showRightIcon = (keepRightIcon || !showLeftIcon) + && rightIcon.getDrawable() != null; + rightIcon.setVisibility(showRightIcon ? View.VISIBLE : View.GONE); + for (int viewId : MARGIN_ADJUSTED_VIEWS) { + adjustMargins(showRightIcon, child.findViewById(viewId)); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java index ebf7c2d58c2d..9a1a144924e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java @@ -50,6 +50,12 @@ public interface NotificationLockscreenUserManager { void addUserChangedListener(UserChangedListener listener); /** + * Registers a [KeyguardNotificationSuppressor] that will be consulted during + * {@link #shouldShowOnKeyguard(NotificationEntry)} + */ + void addKeyguardNotificationSuppressor(KeyguardNotificationSuppressor suppressor); + + /** * Removes a listener previously registered with * {@link #addUserChangedListener(UserChangedListener)} */ @@ -88,4 +94,9 @@ public interface NotificationLockscreenUserManager { default void onUserChanged(int userId) {} default void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {} } + + /** Used to hide notifications on the lockscreen */ + interface KeyguardNotificationSuppressor { + boolean shouldSuppressOnKeyguard(NotificationEntry entry); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 7eb921b9e6bb..e7e9404c9d3e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -98,6 +98,7 @@ public class NotificationLockscreenUserManagerImpl implements private LockPatternUtils mLockPatternUtils; protected KeyguardManager mKeyguardManager; private int mState = StatusBarState.SHADE; + private List<KeyguardNotificationSuppressor> mKeyguardSuppressors = new ArrayList<>(); protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() { @Override @@ -343,6 +344,11 @@ public class NotificationLockscreenUserManagerImpl implements Log.wtf(TAG, "mEntryManager was null!", new Throwable()); return false; } + for (int i = 0; i < mKeyguardSuppressors.size(); i++) { + if (mKeyguardSuppressors.get(i).shouldSuppressOnKeyguard(entry)) { + return false; + } + } boolean exceedsPriorityThreshold; if (hideSilentNotificationsOnLockscreen()) { exceedsPriorityThreshold = @@ -621,6 +627,11 @@ public class NotificationLockscreenUserManagerImpl implements } @Override + public void addKeyguardNotificationSuppressor(KeyguardNotificationSuppressor suppressor) { + mKeyguardSuppressors.add(suppressor); + } + + @Override public void removeUserChangedListener(UserChangedListener listener) { mListeners.remove(listener); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java index eb7854e63a85..491959320ab7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -61,6 +61,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; +import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.util.DeviceConfigProxy; @@ -243,11 +244,12 @@ public interface StatusBarDependenciesModule { SystemClock systemClock, ActivityStarter activityStarter, @Main Executor mainExecutor, - IActivityManager iActivityManager) { + IActivityManager iActivityManager, + OngoingCallLogger logger) { OngoingCallController ongoingCallController = new OngoingCallController( notifCollection, featureFlags, systemClock, activityStarter, mainExecutor, - iActivityManager); + iActivityManager, logger); ongoingCallController.init(); return ongoingCallController; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt new file mode 100644 index 000000000000..a0d5eef027bc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.lockscreen + +import android.app.PendingIntent +import android.app.smartspace.SmartspaceConfig +import android.app.smartspace.SmartspaceManager +import android.app.smartspace.SmartspaceSession +import android.app.smartspace.SmartspaceTarget +import android.content.ContentResolver +import android.content.Context +import android.content.Intent +import android.content.pm.UserInfo +import android.database.ContentObserver +import android.net.Uri +import android.os.Handler +import android.os.UserHandle +import android.provider.Settings +import android.view.View +import android.view.ViewGroup +import com.android.settingslib.Utils +import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.plugins.BcSmartspaceDataPlugin +import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener +import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.FeatureFlags +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.util.concurrency.Execution +import com.android.systemui.util.settings.SecureSettings +import java.lang.RuntimeException +import java.util.Optional +import java.util.concurrent.Executor +import javax.inject.Inject + +/** + * Controller for managing the smartspace view on the lockscreen + */ +@SysUISingleton +class LockscreenSmartspaceController @Inject constructor( + private val context: Context, + private val featureFlags: FeatureFlags, + private val smartspaceManager: SmartspaceManager, + private val activityStarter: ActivityStarter, + private val falsingManager: FalsingManager, + private val secureSettings: SecureSettings, + private val userTracker: UserTracker, + private val contentResolver: ContentResolver, + private val configurationController: ConfigurationController, + private val statusBarStateController: StatusBarStateController, + private val execution: Execution, + @Main private val uiExecutor: Executor, + @Main private val handler: Handler, + optionalPlugin: Optional<BcSmartspaceDataPlugin> +) { + private var session: SmartspaceSession? = null + private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null) + private lateinit var smartspaceView: SmartspaceView + + lateinit var view: View + private set + + private var showSensitiveContentForCurrentUser = false + private var showSensitiveContentForManagedUser = false + private var managedUserHandle: UserHandle? = null + + fun isEnabled(): Boolean { + execution.assertIsMainThread() + + return featureFlags.isSmartspaceEnabled && plugin != null + } + + /** + * Constructs the smartspace view and connects it to the smartspace service. Subsequent calls + * are idempotent until [disconnect] is called. + */ + fun buildAndConnectView(parent: ViewGroup): View { + execution.assertIsMainThread() + + if (!isEnabled()) { + throw RuntimeException("Cannot build view when not enabled") + } + + buildView(parent) + connectSession() + + return view + } + + private fun buildView(parent: ViewGroup) { + if (plugin == null) { + return + } + if (this::view.isInitialized) { + // Due to some oddities with a singleton smartspace view, allow reparenting + (view.getParent() as ViewGroup?)?.removeView(view) + return + } + + val ssView = plugin.getView(parent) + ssView.registerDataProvider(plugin) + ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter { + override fun startIntent(v: View?, i: Intent?) { + activityStarter.startActivity(i, true /* dismissShade */) + } + + override fun startPendingIntent(pi: PendingIntent?) { + activityStarter.startPendingIntentDismissingKeyguard(pi) + } + }) + ssView.setFalsingManager(falsingManager) + + this.smartspaceView = ssView + this.view = ssView as View + + updateTextColorFromWallpaper() + statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount) + } + + private fun connectSession() { + if (plugin == null || session != null) { + return + } + val session = smartspaceManager.createSmartspaceSession( + SmartspaceConfig.Builder(context, "lockscreen").build()) + session.addOnTargetsAvailableListener(uiExecutor, sessionListener) + + userTracker.addCallback(userTrackerCallback, uiExecutor) + contentResolver.registerContentObserver( + secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), + true, + settingsObserver, + UserHandle.USER_ALL + ) + configurationController.addCallback(configChangeListener) + statusBarStateController.addCallback(statusBarStateListener) + + this.session = session + + reloadSmartspace() + } + + /** + * Disconnects the smartspace view from the smartspace service and cleans up any resources. + * Calling [buildAndConnectView] again will cause the same view to be reconnected to the + * service. + */ + fun disconnect() { + execution.assertIsMainThread() + + if (session == null) { + return + } + + session?.let { + it.removeOnTargetsAvailableListener(sessionListener) + it.close() + } + userTracker.removeCallback(userTrackerCallback) + contentResolver.unregisterContentObserver(settingsObserver) + configurationController.removeCallback(configChangeListener) + statusBarStateController.removeCallback(statusBarStateListener) + session = null + + plugin?.onTargetsAvailable(emptyList()) + } + + fun addListener(listener: SmartspaceTargetListener) { + execution.assertIsMainThread() + plugin?.registerListener(listener) + } + + fun removeListener(listener: SmartspaceTargetListener) { + execution.assertIsMainThread() + plugin?.unregisterListener(listener) + } + + private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets -> + execution.assertIsMainThread() + val filteredTargets = targets.filter(::filterSmartspaceTarget) + plugin?.onTargetsAvailable(filteredTargets) + } + + private val userTrackerCallback = object : UserTracker.Callback { + override fun onUserChanged(newUser: Int, userContext: Context) { + execution.assertIsMainThread() + reloadSmartspace() + } + + override fun onProfilesChanged(profiles: List<UserInfo>) { + } + } + + private val settingsObserver = object : ContentObserver(handler) { + override fun onChange(selfChange: Boolean, uri: Uri?) { + execution.assertIsMainThread() + reloadSmartspace() + } + } + + private val configChangeListener = object : ConfigurationController.ConfigurationListener { + override fun onThemeChanged() { + execution.assertIsMainThread() + updateTextColorFromWallpaper() + } + } + + private val statusBarStateListener = object : StatusBarStateController.StateListener { + override fun onDozeAmountChanged(linear: Float, eased: Float) { + execution.assertIsMainThread() + smartspaceView.setDozeAmount(eased) + } + } + + private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean { + return when (t.userHandle) { + userTracker.userHandle -> { + !t.isSensitive || showSensitiveContentForCurrentUser + } + managedUserHandle -> { + // Really, this should be "if this managed profile is associated with the current + // active user", but we don't have a good way to check that, so instead we cheat: + // Only the primary user can have an associated managed profile, so only show + // content for the managed profile if the primary user is active + userTracker.userHandle.identifier == UserHandle.USER_SYSTEM && + (!t.isSensitive || showSensitiveContentForManagedUser) + } + else -> { + false + } + } + } + + private fun updateTextColorFromWallpaper() { + val wallpaperTextColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor) + smartspaceView.setPrimaryTextColor(wallpaperTextColor) + } + + private fun reloadSmartspace() { + val setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS + + showSensitiveContentForCurrentUser = + secureSettings.getIntForUser(setting, 0, userTracker.userId) == 1 + + managedUserHandle = getWorkProfileUser() + val managedId = managedUserHandle?.identifier + if (managedId != null) { + showSensitiveContentForManagedUser = + secureSettings.getIntForUser(setting, 0, managedId) == 1 + } + + session?.requestSmartspaceUpdate() + } + + private fun getWorkProfileUser(): UserHandle? { + for (userInfo in userTracker.userProfiles) { + if (userInfo.isManagedProfile) { + return userInfo.userHandle + } + } + return null + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 13a8661f94dc..1ab2a9e320b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -46,8 +46,9 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationRemoveInterceptor; import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; +import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRanker; +import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRankerStub; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; @@ -138,12 +139,11 @@ public class NotificationEntryManager implements private final LeakDetector mLeakDetector; private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>(); - private final KeyguardEnvironment mKeyguardEnvironment; private final NotificationGroupManagerLegacy mGroupManager; - private final Lazy<NotificationRankingManager> mRankingManager; private final FeatureFlags mFeatureFlags; private final ForegroundServiceDismissalFeatureController mFgsFeatureController; + private LegacyNotificationRanker mRanker = new LegacyNotificationRankerStub(); private NotificationPresenter mPresenter; private RankingMap mLatestRankingMap; @@ -200,8 +200,6 @@ public class NotificationEntryManager implements public NotificationEntryManager( NotificationEntryManagerLogger logger, NotificationGroupManagerLegacy groupManager, - Lazy<NotificationRankingManager> rankingManager, - KeyguardEnvironment keyguardEnvironment, FeatureFlags featureFlags, Lazy<NotificationRowBinder> notificationRowBinderLazy, Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy, @@ -211,8 +209,6 @@ public class NotificationEntryManager implements ) { mLogger = logger; mGroupManager = groupManager; - mRankingManager = rankingManager; - mKeyguardEnvironment = keyguardEnvironment; mFeatureFlags = featureFlags; mNotificationRowBinderLazy = notificationRowBinderLazy; mRemoteInputManagerLazy = notificationRemoteInputManagerLazy; @@ -226,6 +222,10 @@ public class NotificationEntryManager implements notificationListener.addNotificationHandler(mNotifListener); } + public void setRanker(LegacyNotificationRanker ranker) { + mRanker = ranker; + } + /** Adds a {@link NotificationEntryListener}. */ public void addNotificationEntryListener(NotificationEntryListener listener) { mNotificationEntryListeners.add(listener); @@ -419,7 +419,7 @@ public class NotificationEntryManager implements mActiveNotifications.put(entry.getKey(), entry); mGroupManager.onEntryAdded(entry); - updateRankingAndSort(mRankingManager.get().getRankingMap(), "addEntryInternalInternal"); + updateRankingAndSort(mRanker.getRankingMap(), "addEntryInternalInternal"); } /** @@ -698,13 +698,6 @@ public class NotificationEntryManager implements updateNotifications("updateNotificationInternal"); - if (DEBUG) { - // Is this for you? - boolean isForCurrentUser = mKeyguardEnvironment - .isNotificationForCurrentProfiles(notification); - Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you"); - } - for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onPostEntryUpdated(entry); } @@ -862,8 +855,7 @@ public class NotificationEntryManager implements final int len = mActiveNotifications.size(); for (int i = 0; i < len; i++) { NotificationEntry entry = mActiveNotifications.valueAt(i); - final StatusBarNotification sbn = entry.getSbn(); - if (!mKeyguardEnvironment.isNotificationForCurrentProfiles(sbn)) { + if (!mRanker.isNotificationForCurrentProfiles(entry)) { continue; } filtered.add(entry); @@ -886,13 +878,13 @@ public class NotificationEntryManager implements /** Resorts / filters the current notification set with the current RankingMap */ public void reapplyFilterAndSort(String reason) { - updateRankingAndSort(mRankingManager.get().getRankingMap(), reason); + updateRankingAndSort(mRanker.getRankingMap(), reason); } /** Calls to NotificationRankingManager and updates mSortedAndFiltered */ private void updateRankingAndSort(@NonNull RankingMap rankingMap, String reason) { mSortedAndFiltered.clear(); - mSortedAndFiltered.addAll(mRankingManager.get().updateRanking( + mSortedAndFiltered.addAll(mRanker.updateRanking( rankingMap, mActiveNotifications.values(), reason)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java index f21771a89c9e..1940cb2e170f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java @@ -27,14 +27,13 @@ import android.os.RemoteException; import android.service.notification.StatusBarNotification; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dependency; import com.android.systemui.ForegroundServiceController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.phone.ShadeController; import javax.inject.Inject; @@ -46,68 +45,43 @@ import javax.inject.Inject; public class NotificationFilter { private final StatusBarStateController mStatusBarStateController; + private final KeyguardEnvironment mKeyguardEnvironment; + private final ForegroundServiceController mForegroundServiceController; + private final NotificationLockscreenUserManager mUserManager; private final Boolean mIsMediaFlagEnabled; - private NotificationEntryManager.KeyguardEnvironment mEnvironment; - private ShadeController mShadeController; - private ForegroundServiceController mFsc; - private NotificationLockscreenUserManager mUserManager; - @Inject public NotificationFilter( StatusBarStateController statusBarStateController, + KeyguardEnvironment keyguardEnvironment, + ForegroundServiceController foregroundServiceController, + NotificationLockscreenUserManager userManager, MediaFeatureFlag mediaFeatureFlag) { mStatusBarStateController = statusBarStateController; + mKeyguardEnvironment = keyguardEnvironment; + mForegroundServiceController = foregroundServiceController; + mUserManager = userManager; mIsMediaFlagEnabled = mediaFeatureFlag.getEnabled(); } - private NotificationEntryManager.KeyguardEnvironment getEnvironment() { - if (mEnvironment == null) { - mEnvironment = Dependency.get(NotificationEntryManager.KeyguardEnvironment.class); - } - return mEnvironment; - } - - private ShadeController getShadeController() { - if (mShadeController == null) { - mShadeController = Dependency.get(ShadeController.class); - } - return mShadeController; - } - - private ForegroundServiceController getFsc() { - if (mFsc == null) { - mFsc = Dependency.get(ForegroundServiceController.class); - } - return mFsc; - } - - private NotificationLockscreenUserManager getUserManager() { - if (mUserManager == null) { - mUserManager = Dependency.get(NotificationLockscreenUserManager.class); - } - return mUserManager; - } - - /** * @return true if the provided notification should NOT be shown right now. */ public boolean shouldFilterOut(NotificationEntry entry) { final StatusBarNotification sbn = entry.getSbn(); - if (!(getEnvironment().isDeviceProvisioned() + if (!(mKeyguardEnvironment.isDeviceProvisioned() || showNotificationEvenIfUnprovisioned(sbn))) { return true; } - if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) { + if (!mKeyguardEnvironment.isNotificationForCurrentProfiles(sbn)) { return true; } - if (getUserManager().isLockscreenPublicMode(sbn.getUserId()) + if (mUserManager.isLockscreenPublicMode(sbn.getUserId()) && (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET - || getUserManager().shouldHideNotifications(sbn.getUserId()) - || getUserManager().shouldHideNotifications(sbn.getKey()))) { + || mUserManager.shouldHideNotifications(sbn.getUserId()) + || mUserManager.shouldHideNotifications(sbn.getKey()))) { return true; } @@ -123,8 +97,8 @@ public class NotificationFilter { return true; } - if (getFsc().isDisclosureNotification(sbn) - && !getFsc().isDisclosureNeededForUser(sbn.getUserId())) { + if (mForegroundServiceController.isDisclosureNotification(sbn) + && !mForegroundServiceController.isDisclosureNeededForUser(sbn.getUserId())) { // this is a foreground-service disclosure for a user that does not need to show one return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index 6b96ee4fc8e5..8ae31cba4cfb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -185,6 +185,11 @@ public class NotifCollection implements Dumpable { mBuildListener = buildListener; } + /** @see NotifPipeline#getEntry(String) () */ + NotificationEntry getEntry(String key) { + return mNotificationSet.get(key); + } + /** @see NotifPipeline#getAllNotifs() */ Collection<NotificationEntry> getAllNotifs() { Assert.isMainThread(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java index a1844ff5d221..47939f0579f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.collection; +import androidx.annotation.Nullable; + import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener; @@ -89,6 +91,7 @@ public class NotifPipeline implements CommonNotifCollection { * * The returned collection is read-only, unsorted, unfiltered, and ungrouped. */ + @Override public Collection<NotificationEntry> getAllNotifs() { return mNotifCollection.getAllNotifs(); } @@ -99,6 +102,14 @@ public class NotifPipeline implements CommonNotifCollection { } /** + * Returns the NotificationEntry associated with [key]. + */ + @Nullable + public NotificationEntry getEntry(String key) { + return mNotifCollection.getEntry(key); + } + + /** * Registers a lifetime extender. Lifetime extenders can cause notifications that have been * dismissed or retracted by system server to be temporarily retained in the collection. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt index fad0e49b3637..2b620a499d74 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt @@ -23,9 +23,11 @@ import android.service.notification.NotificationListenerService.Ranking import android.service.notification.NotificationListenerService.RankingMap import android.service.notification.StatusBarNotification import com.android.systemui.statusbar.NotificationMediaManager +import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger import com.android.systemui.statusbar.notification.NotificationFilter import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager +import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationRanker import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier @@ -39,7 +41,6 @@ import com.android.systemui.statusbar.policy.HeadsUpManager import dagger.Lazy import java.util.Objects import javax.inject.Inject -import kotlin.Comparator private const val TAG = "NotifRankingManager" @@ -60,10 +61,11 @@ open class NotificationRankingManager @Inject constructor( private val logger: NotificationEntryManagerLogger, private val sectionsFeatureManager: NotificationSectionsFeatureManager, private val peopleNotificationIdentifier: PeopleNotificationIdentifier, - private val highPriorityProvider: HighPriorityProvider -) { + private val highPriorityProvider: HighPriorityProvider, + private val keyguardEnvironment: KeyguardEnvironment +) : LegacyNotificationRanker { - var rankingMap: RankingMap? = null + override var rankingMap: RankingMap? = null protected set private val mediaManager by lazy { mediaManagerLazy.get() @@ -115,7 +117,7 @@ open class NotificationRankingManager @Inject constructor( } } - fun updateRanking( + override fun updateRanking( newRankingMap: RankingMap?, entries: Collection<NotificationEntry>, reason: String @@ -131,6 +133,12 @@ open class NotificationRankingManager @Inject constructor( } } + override fun isNotificationForCurrentProfiles( + entry: NotificationEntry + ): Boolean { + return keyguardEnvironment.isNotificationForCurrentProfiles(entry.sbn) + } + /** Uses the [rankingComparator] to sort notifications which aren't filtered */ private fun filterAndSortLocked( entries: Collection<NotificationEntry>, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java index c1a11b2f64c8..23570728d27c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator; +import androidx.annotation.NonNull; + import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; @@ -27,5 +29,5 @@ public interface Coordinator { * Called after the NewNotifPipeline is initialized. * Coordinators should register their listeners and {@link Pluggable}s to the pipeline. */ - void attach(NotifPipeline pipeline); + void attach(@NonNull NotifPipeline pipeline); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java index ded5e46593f8..d80cc082aada 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java @@ -60,6 +60,7 @@ public class NotifCoordinators implements Dumpable { ConversationCoordinator conversationCoordinator, PreparationCoordinator preparationCoordinator, MediaCoordinator mediaCoordinator, + SmartspaceDedupingCoordinator smartspaceDedupingCoordinator, VisualStabilityCoordinator visualStabilityCoordinator) { dumpManager.registerDumpable(TAG, this); @@ -70,9 +71,14 @@ public class NotifCoordinators implements Dumpable { mCoordinators.add(appOpsCoordinator); mCoordinators.add(deviceProvisionedCoordinator); mCoordinators.add(bubbleCoordinator); - mCoordinators.add(mediaCoordinator); mCoordinators.add(conversationCoordinator); + mCoordinators.add(mediaCoordinator); mCoordinators.add(visualStabilityCoordinator); + + if (featureFlags.isSmartspaceDedupingEnabled()) { + mCoordinators.add(smartspaceDedupingCoordinator); + } + if (featureFlags.isNewNotifPipelineRenderingEnabled()) { mCoordinators.add(headsUpCoordinator); mCoordinators.add(preparationCoordinator); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt new file mode 100644 index 000000000000..442d9d2bb205 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator + +import android.app.smartspace.SmartspaceTarget +import android.os.Parcelable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.SysuiStatusBarStateController +import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController +import com.android.systemui.statusbar.notification.NotificationEntryManager +import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener +import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.time.SystemClock +import java.util.concurrent.TimeUnit.SECONDS +import javax.inject.Inject + +/** + * Hides notifications on the lockscreen if the content of those notifications is also visible + * in smartspace. This ONLY hides the notifications on the lockscreen: if the user pulls the shade + * down or unlocks the device, then the notifications are unhidden. + * + * In addition, notifications that have recently alerted aren't filtered. Tracking this in a way + * that involves the fewest pipeline invalidations requires some unfortunately complex logic. + */ +// This class is a singleton so that the same instance can be accessed by both the old and new +// pipelines +@SysUISingleton +class SmartspaceDedupingCoordinator @Inject constructor( + private val statusBarStateController: SysuiStatusBarStateController, + private val smartspaceController: LockscreenSmartspaceController, + private val notificationEntryManager: NotificationEntryManager, + private val notificationLockscreenUserManager: NotificationLockscreenUserManager, + private val notifPipeline: NotifPipeline, + @Main private val executor: DelayableExecutor, + private val clock: SystemClock +) : Coordinator { + private var isOnLockscreen = false + + private var trackedSmartspaceTargets = mutableMapOf<String, TrackedSmartspaceTarget>() + + override fun attach(pipeline: NotifPipeline) { + pipeline.addPreGroupFilter(filter) + pipeline.addCollectionListener(collectionListener) + statusBarStateController.addCallback(statusBarStateListener) + smartspaceController.addListener(this::onNewSmartspaceTargets) + + // TODO (b/173126564): Remove this once the old pipeline is no longer necessary + notificationLockscreenUserManager.addKeyguardNotificationSuppressor { entry -> + isDupedWithSmartspaceContent(entry) + } + + recordStatusBarState(statusBarStateController.state) + } + + private fun isDupedWithSmartspaceContent(entry: NotificationEntry): Boolean { + return trackedSmartspaceTargets[entry.key]?.shouldFilter ?: false + } + + private val filter = object : NotifFilter("SmartspaceDedupingFilter") { + override fun shouldFilterOut(entry: NotificationEntry, now: Long): Boolean { + return isOnLockscreen && isDupedWithSmartspaceContent(entry) + } + } + + private val collectionListener = object : NotifCollectionListener { + override fun onEntryAdded(entry: NotificationEntry) { + trackedSmartspaceTargets[entry.key]?.let { + updateFilterStatus(it) + } + } + + override fun onEntryUpdated(entry: NotificationEntry) { + trackedSmartspaceTargets[entry.key]?.let { + updateFilterStatus(it) + } + } + + override fun onEntryRemoved(entry: NotificationEntry, reason: Int) { + trackedSmartspaceTargets[entry.key]?.let { trackedTarget -> + cancelExceptionTimeout(trackedTarget) + } + } + } + + private val statusBarStateListener = object : StatusBarStateController.StateListener { + override fun onStateChanged(newState: Int) { + recordStatusBarState(newState) + } + } + + private fun onNewSmartspaceTargets(targets: List<Parcelable>) { + var changed = false + val newMap = mutableMapOf<String, TrackedSmartspaceTarget>() + val oldMap = trackedSmartspaceTargets + + for (target in targets) { + // For all targets that are SmartspaceTargets and have non-null sourceNotificationKeys + (target as? SmartspaceTarget)?.sourceNotificationKey?.let { key -> + val trackedTarget = oldMap.getOrElse(key) { + TrackedSmartspaceTarget(key) + } + newMap[key] = trackedTarget + changed = changed || updateFilterStatus(trackedTarget) + } + // Currently, only filter out the first target + break + } + + for (prevKey in oldMap.keys) { + if (!newMap.containsKey(prevKey)) { + oldMap[prevKey]?.cancelTimeoutRunnable?.run() + changed = true + } + } + + if (changed) { + filter.invalidateList() + notificationEntryManager.updateNotifications("Smartspace targets changed") + } + + trackedSmartspaceTargets = newMap + } + + /** + * Returns true if the target's alert exception status has changed + */ + private fun updateFilterStatus(target: TrackedSmartspaceTarget): Boolean { + val prevShouldFilter = target.shouldFilter + + val entry = notifPipeline.getEntry(target.key) + if (entry != null) { + updateAlertException(target, entry) + + target.shouldFilter = !hasRecentlyAlerted(entry) + } + + return target.shouldFilter != prevShouldFilter && isOnLockscreen + } + + private fun updateAlertException(target: TrackedSmartspaceTarget, entry: NotificationEntry) { + val now = clock.currentTimeMillis() + val alertExceptionExpires = entry.ranking.lastAudiblyAlertedMillis + ALERT_WINDOW + + if (alertExceptionExpires != target.alertExceptionExpires && + alertExceptionExpires > now) { + // If we got here, the target is subject to a new alert exception window, so we + // should update our timeout to fire at the end of the new window + + target.cancelTimeoutRunnable?.run() + target.alertExceptionExpires = alertExceptionExpires + target.cancelTimeoutRunnable = executor.executeDelayed({ + target.cancelTimeoutRunnable = null + target.shouldFilter = true + filter.invalidateList() + notificationEntryManager.updateNotifications("deduping timeout expired") + }, alertExceptionExpires - now) + } + } + + private fun cancelExceptionTimeout(target: TrackedSmartspaceTarget) { + target.cancelTimeoutRunnable?.run() + target.cancelTimeoutRunnable = null + target.alertExceptionExpires = 0 + } + + private fun recordStatusBarState(newState: Int) { + val wasOnLockscreen = isOnLockscreen + isOnLockscreen = newState == StatusBarState.KEYGUARD + + if (isOnLockscreen != wasOnLockscreen) { + filter.invalidateList() + // No need to call notificationEntryManager.updateNotifications; something else already + // does it for us when the keyguard state changes + } + } + + private fun hasRecentlyAlerted(entry: NotificationEntry): Boolean { + return clock.currentTimeMillis() - entry.ranking.lastAudiblyAlertedMillis <= ALERT_WINDOW + } +} + +private class TrackedSmartspaceTarget( + val key: String +) { + var cancelTimeoutRunnable: Runnable? = null + var alertExceptionExpires: Long = 0 + var shouldFilter = false +} + +private val ALERT_WINDOW = SECONDS.toMillis(30) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRanker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRanker.kt new file mode 100644 index 000000000000..49bc48efda00 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRanker.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.legacy + +import android.service.notification.NotificationListenerService +import com.android.systemui.statusbar.notification.collection.NotificationEntry + +interface LegacyNotificationRanker { + val rankingMap: NotificationListenerService.RankingMap? + + fun updateRanking( + newRankingMap: NotificationListenerService.RankingMap?, + entries: Collection<NotificationEntry>, + reason: String + ): List<NotificationEntry> + + fun isNotificationForCurrentProfiles( + entry: NotificationEntry + ): Boolean +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRankerStub.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRankerStub.java new file mode 100644 index 000000000000..12353f80c0d1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationRankerStub.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.legacy; + +import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; + +/** + * Stub implementation that we use until we get passed the "real" one in the form of + * {@link com.android.systemui.statusbar.notification.collection.NotificationRankingManager} + */ +public class LegacyNotificationRankerStub implements LegacyNotificationRanker { + private RankingMap mRankingMap = new RankingMap(new Ranking[] {}); + + @NonNull + @Override + public List<NotificationEntry> updateRanking( + @Nullable RankingMap newRankingMap, + @NonNull Collection<NotificationEntry> entries, + @NonNull String reason) { + if (newRankingMap != null) { + mRankingMap = newRankingMap; + } + List<NotificationEntry> ranked = new ArrayList<>(entries); + ranked.sort(mEntryComparator); + return ranked; + } + + @Nullable + @Override + public RankingMap getRankingMap() { + return mRankingMap; + } + + private final Comparator<NotificationEntry> mEntryComparator = Comparator.comparingLong( + o -> o.getSbn().getNotification().when); + + @Override + public boolean isNotificationForCurrentProfiles(@NonNull NotificationEntry entry) { + return true; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java index 9edb5fc89d8f..776c7d5eb7f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifFilter.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable; +import androidx.annotation.NonNull; + import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -45,5 +47,5 @@ public abstract class NotifFilter extends Pluggable<NotifFilter> { * various entries against. * @return True if the notif should be removed from the list */ - public abstract boolean shouldFilterOut(NotificationEntry entry, long now); + public abstract boolean shouldFilterOut(@NonNull NotificationEntry entry, long now); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 89bb65278dce..a32b7e3b2836 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -44,7 +44,6 @@ import com.android.systemui.statusbar.notification.NotificationEntryManagerLogge import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl; import com.android.systemui.statusbar.notification.collection.NotifPipeline; -import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator; import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; @@ -100,8 +99,6 @@ public interface NotificationsModule { static NotificationEntryManager provideNotificationEntryManager( NotificationEntryManagerLogger logger, NotificationGroupManagerLegacy groupManager, - Lazy<NotificationRankingManager> rankingManager, - NotificationEntryManager.KeyguardEnvironment keyguardEnvironment, FeatureFlags featureFlags, Lazy<NotificationRowBinder> notificationRowBinderLazy, Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy, @@ -111,8 +108,6 @@ public interface NotificationsModule { return new NotificationEntryManager( logger, groupManager, - rankingManager, - keyguardEnvironment, featureFlags, notificationRowBinderLazy, notificationRemoteInputManagerLazy, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt index fd5128a8213a..88ca86b543f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification.init -import android.content.Context import android.service.notification.StatusBarNotification import com.android.systemui.dagger.SysUISingleton import com.android.systemui.people.widget.PeopleSpaceWidgetManager @@ -30,6 +29,7 @@ import com.android.systemui.statusbar.notification.NotificationClicker import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.notification.NotificationListController import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.NotificationRankingManager import com.android.systemui.statusbar.notification.collection.TargetSdkResolver import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer @@ -59,10 +59,10 @@ import javax.inject.Inject */ @SysUISingleton class NotificationsControllerImpl @Inject constructor( - private val context: Context, private val featureFlags: FeatureFlags, private val notificationListener: NotificationListener, private val entryManager: NotificationEntryManager, + private val legacyRanker: NotificationRankingManager, private val notifPipeline: Lazy<NotifPipeline>, private val targetSdkResolver: TargetSdkResolver, private val newNotifPipeline: Lazy<NotifPipelineInitializer>, @@ -128,6 +128,7 @@ class NotificationsControllerImpl @Inject constructor( groupManagerLegacy.get().setHeadsUpManager(headsUpManager) groupAlertTransferHelper.setHeadsUpManager(headsUpManager) + entryManager.setRanker(legacyRanker) entryManager.attach(notificationListener) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java index 151840afcef1..79f99b85c410 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java @@ -42,12 +42,17 @@ public class NotificationBigPictureTemplateViewWrapper extends NotificationTempl updateImageTag(row.getEntry().getSbn()); } - private void updateImageTag(StatusBarNotification notification) { - final Bundle extras = notification.getNotification().extras; - Icon overriddenIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG); - if (overriddenIcon != null) { - mRightIcon.setTag(ImageTransformState.ICON_TAG, overriddenIcon); - mLeftIcon.setTag(ImageTransformState.ICON_TAG, overriddenIcon); + private void updateImageTag(StatusBarNotification sbn) { + final Bundle extras = sbn.getNotification().extras; + boolean bigLargeIconSet = extras.containsKey(Notification.EXTRA_LARGE_ICON_BIG); + if (bigLargeIconSet) { + Icon bigLargeIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON_BIG); + mRightIcon.setTag(ImageTransformState.ICON_TAG, bigLargeIcon); + mLeftIcon.setTag(ImageTransformState.ICON_TAG, bigLargeIcon); + } else { + // Overwrite in case the superclass populated this tag with the promoted picture, + // which won't be right since this is the expanded state. + mRightIcon.setTag(ImageTransformState.ICON_TAG, getLargeIcon(sbn.getNotification())); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java index 0548611ca2d2..a4f11723b978 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java @@ -20,10 +20,12 @@ import static android.view.View.VISIBLE; import static com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.DEFAULT_HEADER_VISIBLE_AMOUNT; +import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; +import android.graphics.drawable.Icon; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.view.View; @@ -32,6 +34,8 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.Nullable; + import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.NotificationActionListLayout; import com.android.systemui.Dependency; @@ -143,16 +147,14 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp com.android.internal.R.dimen.notification_content_margin_top); } - private void resolveTemplateViews(StatusBarNotification notification) { + private void resolveTemplateViews(StatusBarNotification sbn) { mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon); if (mRightIcon != null) { - mRightIcon.setTag(ImageTransformState.ICON_TAG, - notification.getNotification().getLargeIcon()); + mRightIcon.setTag(ImageTransformState.ICON_TAG, getRightIcon(sbn.getNotification())); } mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon); if (mLeftIcon != null) { - mLeftIcon.setTag(ImageTransformState.ICON_TAG, - notification.getNotification().getLargeIcon()); + mLeftIcon.setTag(ImageTransformState.ICON_TAG, getLargeIcon(sbn.getNotification())); } mTitle = mView.findViewById(com.android.internal.R.id.title); mText = mView.findViewById(com.android.internal.R.id.text); @@ -171,6 +173,27 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp updatePendingIntentCancellations(); } + @Nullable + protected final Icon getLargeIcon(Notification n) { + Icon modernLargeIcon = n.getLargeIcon(); + if (modernLargeIcon == null && n.largeIcon != null) { + return Icon.createWithBitmap(n.largeIcon); + } + return modernLargeIcon; + } + + @Nullable + protected final Icon getRightIcon(Notification n) { + if (n.extras.getBoolean(Notification.EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED) + && n.getNotificationStyle() == Notification.BigPictureStyle.class) { + Icon pictureIcon = Notification.BigPictureStyle.getPictureIcon(n.extras); + if (pictureIcon != null) { + return pictureIcon; + } + } + return getLargeIcon(n); + } + private void updatePendingIntentCancellations() { if (mActions != null) { int numActions = mActions.getChildCount(); 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 120f9732f555..4dbdb133e1a7 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 @@ -1148,12 +1148,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (mOnStackYChanged != null) { mOnStackYChanged.run(); } - - final float stackEndHeight = getHeight() - getEmptyBottomMargin() - mTopPadding; - mAmbientState.setStackEndHeight(stackEndHeight); - mAmbientState.setStackHeight( - MathUtils.lerp(stackEndHeight * StackScrollAlgorithm.START_FRACTION, - stackEndHeight, fraction)); + if (mQsExpansionFraction <= 0) { + final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mSidePaddings; + final float stackEndHeight = Math.max(0f, + getHeight() - getEmptyBottomMargin() - stackY); + mAmbientState.setStackEndHeight(stackEndHeight); + mAmbientState.setStackHeight( + MathUtils.lerp(stackEndHeight * StackScrollAlgorithm.START_FRACTION, + stackEndHeight, fraction)); + } } void setOnStackYChanged(Runnable onStackYChanged) { @@ -2065,18 +2068,22 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void updateContentHeight() { - int height = 0; + final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mSidePaddings; + int height = (int) scrimTopPadding; float previousPaddingRequest = mPaddingBetweenElements; int numShownItems = 0; int numShownNotifs = 0; boolean finish = false; int maxDisplayedNotifications = mMaxDisplayedNotifications; ExpandableView previousView = null; + for (int i = 0; i < getChildCount(); i++) { ExpandableView expandableView = (ExpandableView) getChildAt(i); boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard(); + if (expandableView.getVisibility() != View.GONE && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) { + boolean limitReached = maxDisplayedNotifications != -1 && numShownNotifs >= maxDisplayedNotifications; final float viewHeight; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index d94d030f326e..b2d39a952fe2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -58,6 +58,7 @@ public class StackScrollAlgorithm { private int mStatusBarHeight; private float mHeadsUpInset; private int mPinnedZTranslationExtra; + private float mNotificationScrimPadding; public StackScrollAlgorithm( Context context, @@ -82,6 +83,7 @@ public class StackScrollAlgorithm { mPinnedZTranslationExtra = res.getDimensionPixelSize( R.dimen.heads_up_pinned_elevation); mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height); + mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings); } /** @@ -258,6 +260,9 @@ public class StackScrollAlgorithm { // expanded. Consider updating these states in updateContentView instead so that we don't // have to recalculate in every frame. float currentY = -scrollY; + if (!ambientState.isOnKeyguard()) { + currentY += mNotificationScrimPadding; + } float previousY = 0; state.firstViewInShelf = null; state.viewHeightBeforeShelf = -1; @@ -318,6 +323,9 @@ public class StackScrollAlgorithm { AmbientState ambientState) { // The y coordinate of the current child. float currentYPosition = -algorithmState.scrollY; + if (!ambientState.isOnKeyguard()) { + currentYPosition += mNotificationScrimPadding; + } int childCount = algorithmState.visibleChildren.size(); for (int i = 0; i < childCount; i++) { currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 539909491261..16863f60c6a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -38,7 +38,6 @@ import com.android.keyguard.KeyguardConstants; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.KeyguardViewController; -import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -250,16 +249,20 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp KeyguardUpdateMonitor keyguardUpdateMonitor, @Main Resources resources, KeyguardBypassController keyguardBypassController, DozeParameters dozeParameters, - MetricsLogger metricsLogger, DumpManager dumpManager) { + MetricsLogger metricsLogger, DumpManager dumpManager, + PowerManager powerManager, + NotificationMediaManager notificationMediaManager, + WakefulnessLifecycle wakefulnessLifecycle, + ScreenLifecycle screenLifecycle) { mContext = context; - mPowerManager = context.getSystemService(PowerManager.class); + mPowerManager = powerManager; mShadeController = shadeController; mUpdateMonitor = keyguardUpdateMonitor; mDozeParameters = dozeParameters; mUpdateMonitor.registerCallback(this); - mMediaManager = Dependency.get(NotificationMediaManager.class); - Dependency.get(WakefulnessLifecycle.class).addObserver(mWakefulnessObserver); - Dependency.get(ScreenLifecycle.class).addObserver(mScreenObserver); + mMediaManager = notificationMediaManager; + wakefulnessLifecycle.addObserver(mWakefulnessObserver); + screenLifecycle.addObserver(mScreenObserver); mNotificationShadeWindowController = notificationShadeWindowController; mDozeScrimController = dozeScrimController; @@ -415,7 +418,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp if (!wasDeviceInteractive) { mPendingShowBouncer = true; } else { - showBouncer(); + mPendingShowBouncer = false; mKeyguardViewController.notifyKeyguardAuthenticated( false /* strongAuth */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index b9fe9c4a5710..c5a155efdc86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -326,11 +326,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue // Show the ongoing call chip only if there is an ongoing call *and* notification icons // are allowed. (The ongoing call chip occupies the same area as the notification icons, // so if the icons are disabled then the call chip should be, too.) - if (hasOngoingCall && !disableNotifications) { + boolean showOngoingCallChip = hasOngoingCall && !disableNotifications; + if (showOngoingCallChip) { showOngoingCallChip(animate); } else { hideOngoingCallChip(animate); } + mOngoingCallController.notifyChipVisibilityChanged(showOngoingCallChip); } private boolean shouldHideNotificationIcons() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java index b11329ad0135..f043fcf7815b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java @@ -25,6 +25,8 @@ public interface KeyguardDismissHandler { * Executes an action that requres the screen to be unlocked, showing the keyguard if * necessary. Does not close the notification shade (in case it was open). * @param requiresShadeOpen does the shade need to be forced open when hiding the keyguard? + * @param afterKeyguardGone run the dismiss action after keyguard is gone? */ - void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen); + void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen, + boolean afterKeyguardGone); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java index c0181f448cc1..27b68f2ffb7d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java @@ -50,13 +50,14 @@ public class KeyguardDismissUtil implements KeyguardDismissHandler { * @param requiresShadeOpen does the shade need to be forced open when hiding the keyguard? */ @Override - public void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen) { + public void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen, + boolean afterKeyguardGone) { KeyguardDismissHandler dismissHandler = mDismissHandler; if (dismissHandler == null) { Log.wtf(TAG, "KeyguardDismissHandler not set."); action.onDismiss(); return; } - dismissHandler.executeWhenUnlocked(action, requiresShadeOpen); + dismissHandler.executeWhenUnlocked(action, requiresShadeOpen, afterKeyguardGone); } } 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 16abd12ba93c..12dec14dabd0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -2072,14 +2072,13 @@ public class NotificationPanelViewController extends PanelViewController { final int qsPanelBottomY = calculateQsBottomPosition(getQsExpansionFraction()); final boolean visible = (getQsExpansionFraction() > 0 || qsPanelBottomY > 0) && !mShouldUseSplitNotificationShade; - final float notificationTop = mAmbientState.getStackY() - - mNotificationScrimPadding - - mAmbientState.getScrollY(); + final float notificationTop = mAmbientState.getStackY() - mAmbientState.getScrollY(); setQsExpansionEnabled(mAmbientState.getScrollY() == 0); int radius = mScrimCornerRadius; if (!mShouldUseSplitNotificationShade) { - top = (int) Math.min(qsPanelBottomY, notificationTop); + top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop) + : notificationTop); bottom = getView().getBottom(); left = getView().getLeft(); right = getView().getRight(); @@ -4072,6 +4071,7 @@ public class NotificationPanelViewController extends PanelViewController { // force a call to onThemeChanged mConfigurationListener.onThemeChanged(); mFalsingManager.addTapListener(mFalsingTapListener); + mKeyguardIndicationController.init(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index 388d72da808d..ae018ba4fe76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -61,7 +61,6 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; @@ -85,7 +84,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW private final LayoutParams mLpChanged; private final boolean mKeyguardScreenRotation; private final long mLockScreenDisplayTimeout; - private final Display.Mode mKeyguardDisplayMode; + private final float mKeyguardRefreshRate; private final KeyguardViewMediator mKeyguardViewMediator; private final KeyguardBypassController mKeyguardBypassController; private ViewGroup mNotificationShadeView; @@ -135,14 +134,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW // Running on the highest frame rate available can be expensive. // Let's specify a preferred refresh rate, and allow higher FPS only when we // know that we're not falsing (because we unlocked.) - int keyguardRefreshRate = context.getResources() + mKeyguardRefreshRate = context.getResources() .getInteger(R.integer.config_keyguardRefreshRate); - // Find supported display mode with the same resolution and requested refresh rate. - mKeyguardDisplayMode = Arrays.stream(supportedModes).filter(mode -> - (int) mode.getRefreshRate() == keyguardRefreshRate - && mode.getPhysicalWidth() == currentMode.getPhysicalWidth() - && mode.getPhysicalHeight() == currentMode.getPhysicalHeight()) - .findFirst().orElse(null); } /** @@ -273,16 +266,17 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW mLpChanged.privateFlags &= ~LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; } - if (mKeyguardDisplayMode != null) { + if (mKeyguardRefreshRate > 0) { boolean bypassOnKeyguard = mKeyguardBypassController.getBypassEnabled() && state.mStatusBarState == StatusBarState.KEYGUARD && !state.mKeyguardFadingAway && !state.mKeyguardGoingAway; if (state.mDozing || bypassOnKeyguard) { - mLpChanged.preferredDisplayModeId = mKeyguardDisplayMode.getModeId(); + mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardRefreshRate; } else { - mLpChanged.preferredDisplayModeId = 0; + mLpChanged.preferredMaxDisplayRefreshRate = 0; } - Trace.setCounter("display_mode_id", mLpChanged.preferredDisplayModeId); + Trace.setCounter("display_max_refresh_rate", + (long) mLpChanged.preferredMaxDisplayRefreshRate); } } @@ -669,7 +663,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(TAG + ":"); - pw.println(" mKeyguardDisplayMode=" + mKeyguardDisplayMode); + pw.println(" mKeyguardRefreshRate=" + mKeyguardRefreshRate); pw.println(mCurrentState); if (mNotificationShadeView != null && mNotificationShadeView.getViewRootImpl() != null) { mNotificationShadeView.getViewRootImpl().dump(" ", pw); 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 1469cdab2d62..35dda4426849 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -96,11 +96,14 @@ public enum ScrimState { AUTH_SCRIMMED { @Override public void prepare(ScrimState previousState) { - mFrontTint = Color.BLACK; + mNotifTint = previousState.mNotifTint; + mNotifAlpha = previousState.mNotifAlpha; + + mBehindTint = previousState.mBehindTint; + mBehindAlpha = previousState.mBehindAlpha; - mBehindAlpha = 0f; + mFrontTint = Color.BLACK; mFrontAlpha = .66f; - mBubbleAlpha = 0f; } }, 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 cae7e354465b..ded3be43501a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -909,6 +909,8 @@ public class StatusBar extends SystemUI implements DemoMode, mBroadcastDispatcher.registerReceiver(mTaskbarChangeReceiver, filter); } + mKeyguardIndicationController.init(); + mColorExtractor.addOnColorsChangedListener(this); mStatusBarStateController.addCallback(this, SysuiStatusBarStateController.RANK_STATUS_BAR); @@ -2988,11 +2990,13 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationsController.resetUserExpandedStates(); } - private void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen) { + private void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen, + boolean afterKeyguardGone) { if (mStatusBarKeyguardViewManager.isShowing() && requiresShadeOpen) { mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); } - dismissKeyguardThenExecute(action, null /* cancelAction */, false /* afterKeyguardGone */); + dismissKeyguardThenExecute(action, null /* cancelAction */, + afterKeyguardGone /* afterKeyguardGone */); } protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 6ed375e708e7..41bd71015f80 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -132,6 +132,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void onVisibilityChanged(boolean isVisible) { + if (!isVisible) { + cancelPostAuthActions(); + } if (mAlternateAuthInterceptor != null) { mAlternateAuthInterceptor.onBouncerVisibilityChanged(); } @@ -406,21 +409,25 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return; } + mAfterKeyguardGoneAction = r; + mKeyguardGoneCancelAction = cancelAction; if (mAlternateAuthInterceptor != null) { - mAfterKeyguardGoneAction = r; - mKeyguardGoneCancelAction = cancelAction; if (mAlternateAuthInterceptor.showAlternateAuthBouncer()) { mStatusBar.updateScrimController(); } return; } - if (!afterKeyguardGone) { - mBouncer.showWithDismissAction(r, cancelAction); - } else { - mAfterKeyguardGoneAction = r; - mKeyguardGoneCancelAction = cancelAction; + if (afterKeyguardGone) { + // we'll handle the dismiss action after keyguard is gone, so just show the bouncer mBouncer.show(false /* resetSecuritySelection */); + } else { + // after authentication success, run dismiss action with the option to defer + // hiding the keyguard based on the return value of the OnDismissAction + mBouncer.showWithDismissAction(mAfterKeyguardGoneAction, mKeyguardGoneCancelAction); + // bouncer will handle the dismiss action, so we no longer need to track it here + mAfterKeyguardGoneAction = null; + mKeyguardGoneCancelAction = null; } } updateStates(); @@ -1133,15 +1140,21 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } /** - * Request to show the udfps affordance in a particular color. This can be used if an - * occluding app on the keyguard would like to request udfps. This method does nothing if - * {@link KeyguardUpdateMonitor#shouldListenForFingerprint} is false. + * Request to authenticate using face. */ - public void requestUdfps(boolean request, int color) { - if (mAlternateAuthInterceptor == null) { - return; + public void requestFace(boolean request) { + mKeyguardUpdateManager.requestFaceAuthOnOccludingApp(request); + } + + /** + * Request to authenticate using the fingerprint sensor. If the fingerprint sensor is udfps, + * uses the color provided by udfpsColor for the fingerprint icon. + */ + public void requestFp(boolean request, int udfpsColor) { + mKeyguardUpdateManager.requestFingerprintAuthOnOccludingApp(request); + if (mAlternateAuthInterceptor != null) { + mAlternateAuthInterceptor.requestUdfps(request, udfpsColor); } - mAlternateAuthInterceptor.requestUdfps(request, color); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 2b5caf91e024..75c544d3aa4b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -194,6 +194,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, } }; + mKeyguardIndicationController.init(); mViewHierarchyManager.setUpWithPresenter(this, stackScrollerController.getNotificationListContainer()); mEntryManager.setUpWithPresenter(this); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt index 1fe77fd441bc..6e27caec9365 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt @@ -56,6 +56,7 @@ class OngoingCallChronometer @JvmOverloads constructor( // call starts. minimumTextWidth = 0 shouldHideText = false + visibility = VISIBLE super.setBase(base) } @@ -76,6 +77,9 @@ class OngoingCallChronometer @JvmOverloads constructor( if (desiredTextWidth > enforcedTextWidth) { shouldHideText = true + // Changing visibility ensures that the content description is not read aloud when the + // time isn't displayed. + visibility = GONE setMeasuredDimension(0, 0) } else { // It's possible that the current text could fit in a smaller width, but we don't want diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index 6d1df5b5fa19..e9d256cc5ea8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -49,7 +49,8 @@ class OngoingCallController @Inject constructor( private val systemClock: SystemClock, private val activityStarter: ActivityStarter, @Main private val mainExecutor: Executor, - private val iActivityManager: IActivityManager + private val iActivityManager: IActivityManager, + private val logger: OngoingCallLogger ) : CallbackController<OngoingCallListener> { /** Null if there's no ongoing call. */ @@ -104,7 +105,7 @@ class OngoingCallController @Inject constructor( /** * Sets the chip view that will contain ongoing call information. * - * Should only be called from [CollapedStatusBarFragment]. + * Should only be called from [CollapsedStatusBarFragment]. */ fun setChipView(chipView: ViewGroup) { this.chipView = chipView @@ -113,6 +114,16 @@ class OngoingCallController @Inject constructor( } } + + /** + * Called when the chip's visibility may have changed. + * + * Should only be called from [CollapsedStatusBarFragment]. + */ + fun notifyChipVisibilityChanged(chipIsVisible: Boolean) { + logger.logChipVisibilityChanged(chipIsVisible) + } + /** * Returns true if there's an active ongoing call that should be displayed in a status bar chip. */ @@ -150,6 +161,7 @@ class OngoingCallController @Inject constructor( timeView.start() currentChipView.setOnClickListener { + logger.logChipClicked() activityStarter.postStartActivityDismissingKeyguard( currentOngoingCallInfo.intent, 0, ActivityLaunchAnimator.Controller.fromView(it)) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt new file mode 100644 index 000000000000..177f21537ec9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone.ongoingcall + +import androidx.annotation.VisibleForTesting +import com.android.internal.logging.UiEvent +import com.android.internal.logging.UiEventLogger +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject + +/** A class to log events for the ongoing call chip. */ +@SysUISingleton +class OngoingCallLogger @Inject constructor(private val logger: UiEventLogger) { + + private var chipIsVisible: Boolean = false + + /** Logs that the ongoing call chip was clicked. */ + fun logChipClicked() { + logger.log(OngoingCallEvents.ONGOING_CALL_CLICKED) + } + + /** + * If needed, logs that the ongoing call chip's visibility has changed. + * + * For now, only logs when the chip changes from not visible to visible. + */ + fun logChipVisibilityChanged(chipIsVisible: Boolean) { + if (chipIsVisible && chipIsVisible != this.chipIsVisible) { + logger.log(OngoingCallEvents.ONGOING_CALL_VISIBLE) + } + this.chipIsVisible = chipIsVisible + } + + @VisibleForTesting + enum class OngoingCallEvents(val metricId: Int) : UiEventLogger.UiEventEnum { + @UiEvent(doc = "The ongoing call chip became visible") + ONGOING_CALL_VISIBLE(813), + + @UiEvent(doc = "The ongoing call chip was clicked") + ONGOING_CALL_CLICKED(814); + + override fun getId() = metricId + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt index e3e2572b9b4c..b563d86f65b1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt @@ -482,7 +482,7 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) private fun KeyguardDismissUtil.executeWhenUnlocked( requiresShadeOpen: Boolean, onDismissAction: () -> Boolean -) = executeWhenUnlocked(onDismissAction, requiresShadeOpen) +) = executeWhenUnlocked(onDismissAction, requiresShadeOpen, false) // convenience function that swaps parameter order so that lambda can be placed at the end private fun ActivityStarter.startPendingIntentDismissingKeyguard( diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java index 3c1e12327d8f..865aa23f69e6 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java @@ -66,6 +66,13 @@ public class ThemeOverlayApplier implements Dumpable { "android.theme.customization.accent_color"; static final String OVERLAY_CATEGORY_SYSTEM_PALETTE = "android.theme.customization.system_palette"; + + static final String OVERLAY_COLOR_SOURCE = "android.theme.customization.color_source"; + + static final String COLOR_SOURCE_PRESET = "preset"; + + static final String TIMESTAMP_FIELD = "_applied_timestamp"; + @VisibleForTesting static final String OVERLAY_CATEGORY_FONT = "android.theme.customization.font"; @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 9bdd8c009531..195114f0f19f 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -15,8 +15,11 @@ */ package com.android.systemui.theme; +import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE; +import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_SOURCE; +import static com.android.systemui.theme.ThemeOverlayApplier.TIMESTAMP_FIELD; import android.annotation.Nullable; import android.app.WallpaperColors; @@ -90,12 +93,12 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { private final UserManager mUserManager; private final BroadcastDispatcher mBroadcastDispatcher; private final Executor mBgExecutor; - private final SecureSettings mSecureSettings; + private SecureSettings mSecureSettings; private final Executor mMainExecutor; private final Handler mBgHandler; private final WallpaperManager mWallpaperManager; private final boolean mIsMonetEnabled; - private final UserTracker mUserTracker; + private UserTracker mUserTracker; private DeviceProvisionedController mDeviceProvisionedController; private WallpaperColors mSystemColors; // If fabricated overlays were already created for the current theme. @@ -112,6 +115,8 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { private boolean mAcceptColorEvents = true; // Defers changing themes until Setup Wizard is done. private boolean mDeferredThemeEvaluation; + // Determines if we should ignore THEME_CUSTOMIZATION_OVERLAY_PACKAGES setting changes. + private boolean mSkipSettingChange; private final DeviceProvisionedListener mDeviceProvisionedListener = new DeviceProvisionedListener() { @@ -162,6 +167,35 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { } } } + // Check if we need to reset to default colors (if a color override was set that is sourced + // from the wallpaper) + int currentUser = mUserTracker.getUserId(); + String overlayPackageJson = mSecureSettings.getStringForUser( + Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, + currentUser); + if (!TextUtils.isEmpty(overlayPackageJson)) { + try { + JSONObject jsonObject = new JSONObject(overlayPackageJson); + if ((jsonObject.has(OVERLAY_CATEGORY_ACCENT_COLOR) + || jsonObject.has(OVERLAY_CATEGORY_SYSTEM_PALETTE)) + && !COLOR_SOURCE_PRESET.equals( + jsonObject.optString(OVERLAY_COLOR_SOURCE))) { + mSkipSettingChange = true; + jsonObject.remove(OVERLAY_CATEGORY_ACCENT_COLOR); + jsonObject.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE); + jsonObject.remove(OVERLAY_COLOR_SOURCE); + jsonObject.put(TIMESTAMP_FIELD, System.currentTimeMillis()); + if (DEBUG) { + Log.d(TAG, "Updating theme setting from " + + overlayPackageJson + " to " + jsonObject.toString()); + } + mSecureSettings.putString(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, + jsonObject.toString()); + } + } catch (JSONException e) { + Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e); + } + } reevaluateSystemTheme(false /* forceReload */); }; @@ -232,6 +266,11 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { mDeferredThemeEvaluation = true; return; } + if (mSkipSettingChange) { + if (DEBUG) Log.d(TAG, "Skipping setting change"); + mSkipSettingChange = false; + return; + } reevaluateSystemTheme(true /* forceReload */); } }, diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java index 66bd48b9d188..4cba432a66be 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java @@ -99,6 +99,7 @@ public class WalletActivity extends LifecycleActivity { getActionBar().setHomeAsUpIndicator(getHomeIndicatorDrawable()); getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close); WalletView walletView = requireViewById(R.id.wallet_view); + mWalletScreenController = new WalletScreenController( this, walletView, @@ -116,20 +117,31 @@ public class WalletActivity extends LifecycleActivity { && mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { return; } - mActivityStarter.startActivity( - mQuickAccessWalletClient.createWalletIntent(), true); - finish(); + + if (mKeyguardStateController.isUnlocked()) { + mActivityStarter.startActivity( + mQuickAccessWalletClient.createWalletIntent(), true); + finish(); + } else { + mKeyguardDismissUtil.executeWhenUnlocked(() -> { + mActivityStarter.startActivity( + mQuickAccessWalletClient.createWalletIntent(), true); + finish(); + return false; + }, false, true); + } }); + // Click the action button to re-render the screen when the device is unlocked. - if (!mKeyguardStateController.isUnlocked()) { - walletView.getActionButton().setOnClickListener( - v -> { - if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { - return; - } - mKeyguardDismissUtil.executeWhenUnlocked(() -> false, false); - }); - } + walletView.setDeviceLockedActionOnClickListener( + v -> { + if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + return; + } + + mKeyguardDismissUtil.executeWhenUnlocked(() -> false, false, + false); + }); } @Override @@ -142,13 +154,15 @@ public class WalletActivity extends LifecycleActivity { protected void onResume() { super.onResume(); mWalletScreenController.queryWalletCards(); - mKeyguardViewManager.requestUdfps(true, Color.BLACK); + mKeyguardViewManager.requestFp(true, Color.BLACK); + mKeyguardViewManager.requestFace(true); } @Override protected void onPause() { super.onPause(); - mKeyguardViewManager.requestUdfps(false, -1); + mKeyguardViewManager.requestFp(false, -1); + mKeyguardViewManager.requestFace(false); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java index b57d93762381..d0662e7301d8 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java @@ -131,8 +131,14 @@ public class WalletScreenController implements if (data.isEmpty()) { showEmptyStateView(); } else { - mWalletView.showCardCarousel( - data, response.getSelectedIndex(), !mKeyguardStateController.isUnlocked()); + int selectedIndex = response.getSelectedIndex(); + if (selectedIndex >= data.size()) { + Log.w(TAG, "Invalid selected card index, showing empty state."); + showEmptyStateView(); + } else { + mWalletView.showCardCarousel( + data, selectedIndex, !mKeyguardStateController.isUnlocked()); + } } removeMinHeightAndRecordHeightOnLayout(); }); @@ -178,6 +184,11 @@ public class WalletScreenController implements } @Override + public void onUnlockedChanged() { + queryWalletCards(); + } + + @Override public void onCardSelected(@NonNull WalletCardViewInfo card) { if (mIsDismissed) { return; diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java index e42ce6ac50f3..bf146b6cd02f 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java @@ -63,6 +63,7 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard private final ViewGroup mEmptyStateView; private CharSequence mCenterCardText; private boolean mIsDeviceLocked = false; + private OnClickListener mDeviceLockedActionOnClickListener; public WalletView(Context context) { this(context, null); @@ -178,6 +179,10 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard mEmptyStateView.setVisibility(GONE); } + void setDeviceLockedActionOnClickListener(OnClickListener onClickListener) { + mDeviceLockedActionOnClickListener = onClickListener; + } + void hide() { setVisibility(GONE); } @@ -238,6 +243,7 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard if (isDeviceLocked) { mActionButton.setVisibility(VISIBLE); mActionButton.setText(R.string.wallet_action_button_label_unlock); + mActionButton.setOnClickListener(mDeviceLockedActionOnClickListener); } else if (actionButtonText != null) { mActionButton.setText(actionButtonText); mActionButton.setVisibility(VISIBLE); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 8c5f74dc7c93..98467d4816b8 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -19,30 +19,20 @@ package com.android.keyguard; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.smartspace.SmartspaceTarget; -import android.content.Context; -import android.content.pm.UserInfo; import android.content.res.Resources; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.UserHandle; -import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; -import android.util.AttributeSet; import android.view.View; import android.widget.FrameLayout; import android.widget.RelativeLayout; -import androidx.annotation.Nullable; +import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.clock.ClockManager; @@ -50,21 +40,14 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.BcSmartspaceDataPlugin; -import com.android.systemui.plugins.BcSmartspaceDataPlugin.IntentStarter; import com.android.systemui.plugins.ClockPlugin; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.settings.UserTracker; -import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.NotificationIconContainer; import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.util.settings.SecureSettings; import org.junit.Before; import org.junit.Test; @@ -74,78 +57,54 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.verification.VerificationMode; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.Executor; - @SmallTest @RunWith(AndroidTestingRunner.class) public class KeyguardClockSwitchControllerTest extends SysuiTestCase { @Mock + private KeyguardClockSwitch mView; + @Mock private StatusBarStateController mStatusBarStateController; @Mock private SysuiColorExtractor mColorExtractor; @Mock private ClockManager mClockManager; @Mock - private KeyguardClockSwitch mView; - @Mock - private NotificationIconContainer mNotificationIcons; - @Mock - private ClockPlugin mClockPlugin; - @Mock - ColorExtractor.GradientColors mGradientColors; - @Mock KeyguardSliceViewController mKeyguardSliceViewController; @Mock - Resources mResources; - @Mock NotificationIconAreaController mNotificationIconAreaController; @Mock BroadcastDispatcher mBroadcastDispatcher; @Mock - private FeatureFlags mFeatureFlags; - @Mock - private Executor mExecutor; - @Mock - private AnimatableClockView mClockView; - @Mock - private AnimatableClockView mLargeClockView; - @Mock - private FrameLayout mLargeClockFrame; - @Mock BatteryController mBatteryController; @Mock - ConfigurationController mConfigurationController; - @Mock - Optional<BcSmartspaceDataPlugin> mOptionalSmartspaceDataProvider; + KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock - BcSmartspaceDataPlugin mSmartspaceDataProvider; + KeyguardBypassController mBypassController; @Mock - SmartspaceView mSmartspaceView; + LockscreenSmartspaceController mSmartspaceController; + @Mock - ActivityStarter mActivityStarter; + Resources mResources; @Mock - FalsingManager mFalsingManager; + private ClockPlugin mClockPlugin; @Mock - KeyguardUpdateMonitor mKeyguardUpdateMonitor; + ColorExtractor.GradientColors mGradientColors; + @Mock - KeyguardBypassController mBypassController; + private NotificationIconContainer mNotificationIcons; @Mock - Handler mHandler; + private AnimatableClockView mClockView; @Mock - UserTracker mUserTracker; + private AnimatableClockView mLargeClockView; @Mock - SecureSettings mSecureSettings; + private FrameLayout mLargeClockFrame; + + private final View mFakeSmartspaceView = new View(mContext); private KeyguardClockSwitchController mController; private View mStatusArea; - private static final int USER_ID = 5; - private static final int MANAGED_USER_ID = 15; - @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -162,9 +121,9 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { when(mClockView.getContext()).thenReturn(getContext()); when(mLargeClockView.getContext()).thenReturn(getContext()); - when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true); when(mView.isAttachedToWindow()).thenReturn(true); when(mResources.getString(anyInt())).thenReturn("h:mm"); + when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); mController = new KeyguardClockSwitchController( mView, mStatusBarStateController, @@ -173,28 +132,16 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { mKeyguardSliceViewController, mNotificationIconAreaController, mBroadcastDispatcher, - mFeatureFlags, - mExecutor, mBatteryController, - mConfigurationController, - mActivityStarter, - mFalsingManager, mKeyguardUpdateMonitor, mBypassController, - mHandler, - mUserTracker, - mSecureSettings, - mOptionalSmartspaceDataProvider - ); + mSmartspaceController); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors); mStatusArea = new View(getContext()); when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea); - when(mOptionalSmartspaceDataProvider.isPresent()).thenReturn(true); - when(mOptionalSmartspaceDataProvider.get()).thenReturn(mSmartspaceDataProvider); - when(mSmartspaceDataProvider.getView(any())).thenReturn(mSmartspaceView); } @Test @@ -255,119 +202,34 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { @Test public void testSmartspaceEnabledRemovesKeyguardStatusArea() { - when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true); + when(mSmartspaceController.isEnabled()).thenReturn(true); + when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); mController.init(); assertEquals(View.GONE, mStatusArea.getVisibility()); } @Test - public void testSmartspaceEnabledNoDataProviderShowsKeyguardStatusArea() { - when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true); - when(mOptionalSmartspaceDataProvider.isPresent()).thenReturn(false); - mController.init(); - - assertEquals(View.VISIBLE, mStatusArea.getVisibility()); - } - - @Test public void testSmartspaceDisabledShowsKeyguardStatusArea() { - when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(false); + when(mSmartspaceController.isEnabled()).thenReturn(false); mController.init(); assertEquals(View.VISIBLE, mStatusArea.getVisibility()); } @Test - public void testThemeChangeNotifiesSmartspace() { + public void testDetachRemovesSmartspaceView() { + when(mSmartspaceController.isEnabled()).thenReturn(true); + when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); mController.init(); - verify(mSmartspaceView).setPrimaryTextColor(anyInt()); + verify(mView).addView(eq(mFakeSmartspaceView), anyInt(), any()); - mController.getConfigurationListener().onThemeChanged(); - verify(mSmartspaceView, times(2)).setPrimaryTextColor(anyInt()); - } - - @Test - public void doNotFilterRegularTarget() { - setupPrimaryAndManagedUser(); - mController.init(); - - when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(0); - when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID))) - .thenReturn(0); - - mController.getSettingsObserver().onChange(true, null); - - SmartspaceTarget t = mock(SmartspaceTarget.class); - when(t.isSensitive()).thenReturn(false); - when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID)); - assertEquals(false, mController.filterSmartspaceTarget(t)); - - reset(t); - when(t.isSensitive()).thenReturn(false); - when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID)); - assertEquals(false, mController.filterSmartspaceTarget(t)); - } - - @Test - public void filterAllSensitiveTargetsAllUsers() { - setupPrimaryAndManagedUser(); - mController.init(); - - when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(0); - when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID))) - .thenReturn(0); - - mController.getSettingsObserver().onChange(true, null); - - SmartspaceTarget t = mock(SmartspaceTarget.class); - when(t.isSensitive()).thenReturn(true); - when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID)); - assertEquals(true, mController.filterSmartspaceTarget(t)); - - reset(t); - when(t.isSensitive()).thenReturn(true); - when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID)); - assertEquals(true, mController.filterSmartspaceTarget(t)); - } - - @Test - public void filterSensitiveManagedUserTargets() { - setupPrimaryAndManagedUser(); - mController.init(); - - when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(1); - when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID))) - .thenReturn(0); - - mController.getSettingsObserver().onChange(true, null); - - SmartspaceTarget t = mock(SmartspaceTarget.class); - when(t.isSensitive()).thenReturn(true); - when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID)); - assertEquals(false, mController.filterSmartspaceTarget(t)); - - reset(t); - when(t.isSensitive()).thenReturn(true); - when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID)); - assertEquals(true, mController.filterSmartspaceTarget(t)); - } - - private void setupPrimaryAndManagedUser() { - UserInfo userInfo = mock(UserInfo.class); - when(userInfo.isManagedProfile()).thenReturn(true); - when(userInfo.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID)); - when(mUserTracker.getUserProfiles()).thenReturn(List.of(userInfo)); - - when(mUserTracker.getUserId()).thenReturn(USER_ID); - when(mUserTracker.getUserHandle()).thenReturn(new UserHandle(USER_ID)); - } - - private void setupPrimaryAndNoManagedUser() { - when(mUserTracker.getUserProfiles()).thenReturn(Collections.emptyList()); + ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); + verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture()); - when(mUserTracker.getUserId()).thenReturn(USER_ID); - when(mUserTracker.getUserHandle()).thenReturn(new UserHandle(USER_ID)); + listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView); + verify(mView).removeView(mFakeSmartspaceView); } private void verifyAttachment(VerificationMode times) { @@ -377,25 +239,4 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { any(ColorExtractor.OnColorsChangedListener.class)); verify(mView, times).updateColors(mGradientColors); } - - private static class SmartspaceView extends View - implements BcSmartspaceDataPlugin.SmartspaceView { - SmartspaceView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public void registerDataProvider(BcSmartspaceDataPlugin plugin) { } - - public void setPrimaryTextColor(int color) { } - - public void setDozeAmount(float amount) { } - - public void setIntentStarter(IntentStarter intentStarter) { } - - public void setFalsingManager(FalsingManager falsingManager) { } - - public void setDnd(@Nullable Drawable dndIcon, @Nullable String description) { } - - public void setNextAlarm(@Nullable Drawable dndIcon, @Nullable String description) { } - } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 77582bd2e7af..f779305b75ab 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -842,10 +842,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testStartUdfpsServiceBeginsOnKeyguard() { // GIVEN - // - bouncer isn't showing // - status bar state is on the keyguard // - user has authenticated since boot - setKeyguardBouncerVisibility(false /* isVisible */); mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD); when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true); @@ -854,11 +852,44 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test + public void testOccludingAppFingerprintListeningState() { + // GIVEN keyguard isn't visible (app occluding) + mKeyguardUpdateMonitor.dispatchStartedWakingUp(); + mKeyguardUpdateMonitor.setKeyguardOccluded(true); + mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(false); + when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true); + + // THEN we shouldn't listen for fingerprints + assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isEqualTo(false); + + // THEN we should listen for udfps (hiding of mechanism to actually auth is + // controlled by UdfpsKeyguardViewController) + assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(true); + } + + @Test + public void testOccludingAppRequestsFingerprint() { + // GIVEN keyguard isn't visible (app occluding) + mKeyguardUpdateMonitor.dispatchStartedWakingUp(); + mKeyguardUpdateMonitor.setKeyguardOccluded(true); + mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(false); + + // WHEN an occluding app requests fp + mKeyguardUpdateMonitor.requestFingerprintAuthOnOccludingApp(true); + + // THEN we should listen for fingerprints + assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isEqualTo(true); + + // WHEN an occluding app stops requesting fp + mKeyguardUpdateMonitor.requestFingerprintAuthOnOccludingApp(false); + + // THEN we shouldn't listen for fingeprints + assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isEqualTo(false); + } + + @Test public void testStartUdfpsServiceNoAuthenticationSinceLastBoot() { - // GIVEN - // - bouncer isn't showing - // - status bar state is on the keyguard - setKeyguardBouncerVisibility(false /* isVisible */); + // GIVEN status bar state is on the keyguard mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD); // WHEN user hasn't authenticated since last boot @@ -871,7 +902,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testShouldNotListenForUdfps_whenTrustEnabled() { // GIVEN a "we should listen for udfps" state - setKeyguardBouncerVisibility(false /* isVisible */); mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD); when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true); @@ -886,7 +916,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testShouldNotListenForUdfps_whenFaceAuthenticated() { // GIVEN a "we should listen for udfps" state - setKeyguardBouncerVisibility(false /* isVisible */); mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD); when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true); 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 3a657c816937..a1f283b9a26b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -18,10 +18,12 @@ package com.android.systemui.biometrics; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.Context; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; @@ -55,6 +57,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { @Mock private UdfpsKeyguardView mView; @Mock + private Context mResourceContext; + @Mock private StatusBarStateController mStatusBarStateController; @Mock private StatusBar mStatusBar; @@ -90,6 +94,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mView.getContext()).thenReturn(mResourceContext); + when(mResourceContext.getString(anyInt())).thenReturn("test string"); when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false); when(mKeyguardUpdateMonitor.isKeyguardVisible()).thenReturn(true); mController = new UdfpsKeyguardViewController( diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index e62fa912e610..e1f1dc15badb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -325,7 +325,8 @@ public class MediaControlPanelTest : SysuiTestCase() { assertThat(dismiss.isEnabled).isEqualTo(true) dismiss.callOnClick() val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java) - verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean()) + verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean(), + eq(false)) captor.value.onDismiss() verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java index c050b628e98d..ba2b37c38820 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java @@ -30,6 +30,7 @@ import android.testing.TestableLooper.RunWithLooper; import android.testing.ViewUtils; import android.view.LayoutInflater; import android.view.View; +import android.widget.FrameLayout; import androidx.test.filters.SmallTest; @@ -58,24 +59,26 @@ public class QSDetailTest extends SysuiTestCase { private DetailAdapter mMockDetailAdapter; private TestableLooper mTestableLooper; private UiEventLoggerFake mUiEventLogger; + private FrameLayout mParent; @Before public void setup() throws Exception { mTestableLooper = TestableLooper.get(this); mUiEventLogger = QSEvents.INSTANCE.setLoggerForTesting(); - mTestableLooper.runWithLooper(() -> { - mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class); - mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class); - mQsDetail = (QSDetail) LayoutInflater.from(mContext).inflate(R.layout.qs_detail, null); - mQsPanelController = mock(QSPanelController.class); - mQuickHeader = mock(QuickStatusBarHeader.class); - mQsDetail.setQsPanel(mQsPanelController, mQuickHeader, mock(QSFooter.class)); + mParent = new FrameLayout(mContext); + mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class); + mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class); + LayoutInflater.from(mContext).inflate(R.layout.qs_detail, mParent); + mQsDetail = (QSDetail) mParent.getChildAt(0); - mMockDetailAdapter = mock(DetailAdapter.class); - when(mMockDetailAdapter.createDetailView(any(), any(), any())) - .thenReturn(mock(View.class)); - }); + mQsPanelController = mock(QSPanelController.class); + mQuickHeader = mock(QuickStatusBarHeader.class); + mQsDetail.setQsPanel(mQsPanelController, mQuickHeader, mock(QSFooter.class)); + + mMockDetailAdapter = mock(DetailAdapter.class); + when(mMockDetailAdapter.createDetailView(any(), any(), any())) + .thenReturn(new View(mContext)); // Only detail in use is the user detail when(mMockDetailAdapter.openDetailEvent()) @@ -84,16 +87,18 @@ public class QSDetailTest extends SysuiTestCase { .thenReturn(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE); when(mMockDetailAdapter.moreSettingsEvent()) .thenReturn(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS); + ViewUtils.attachView(mParent); } @After public void tearDown() { QSEvents.INSTANCE.resetLogger(); + mTestableLooper.processAllMessages(); + ViewUtils.detachView(mParent); } @Test public void testShowDetail_Metrics() { - ViewUtils.attachView(mQsDetail); mTestableLooper.processAllMessages(); mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false); @@ -107,14 +112,10 @@ public class QSDetailTest extends SysuiTestCase { assertEquals(1, mUiEventLogger.numLogs()); assertEquals(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE.getId(), mUiEventLogger.eventId(0)); - - ViewUtils.detachView(mQsDetail); - mTestableLooper.processAllMessages(); } @Test public void testMoreSettingsButton() { - ViewUtils.attachView(mQsDetail); mTestableLooper.processAllMessages(); mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false); @@ -127,9 +128,6 @@ public class QSDetailTest extends SysuiTestCase { assertEquals(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS.getId(), mUiEventLogger.eventId(0)); verify(mActivityStarter).postStartActivityDismissingKeyguard(any(), anyInt()); - - ViewUtils.detachView(mQsDetail); - mTestableLooper.processAllMessages(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index e894b7bb4c77..7533cf1310de 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -92,6 +92,7 @@ import java.util.Collections; public class QuickAccessWalletTileTest extends SysuiTestCase { private static final String CARD_ID = "card_id"; + private static final String LABEL = "QAW"; private static final Icon CARD_IMAGE = Icon.createWithBitmap(Bitmap.createBitmap(70, 50, Bitmap.Config.ARGB_8888)); @@ -141,6 +142,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { when(mHost.getContext()).thenReturn(mSpiedContext); when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger); when(mFeatureFlags.isQuickAccessWalletEnabled()).thenReturn(true); + when(mQuickAccessWalletClient.getServiceLabel()).thenReturn(LABEL); when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true); when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true); @@ -248,13 +250,19 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mTile.handleUpdateState(state, null); - assertEquals(mContext.getString(R.string.wallet_title), state.label.toString()); + assertEquals(LABEL, state.label.toString()); assertTrue(state.label.toString().contentEquals(state.contentDescription)); assertEquals(icon, state.icon); } @Test - public void testGetTileLabel() { + public void testGetTileLabel_serviceLabelExists() { + assertEquals(LABEL, mTile.getTileLabel().toString()); + } + + @Test + public void testGetTileLabel_serviceLabelDoesNotExist() { + when(mQuickAccessWalletClient.getServiceLabel()).thenReturn(null); assertEquals(mContext.getString(R.string.wallet_title), mTile.getTileLabel().toString()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 6f0ae223540d..b8db1156b85d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -46,6 +46,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.IActivityManager; import android.app.Instrumentation; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; @@ -69,6 +70,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.app.IBatteryStats; +import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.Utils; import com.android.settingslib.fuelgauge.BatteryStatus; @@ -141,6 +143,10 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { private KeyguardIndicationRotateTextViewController mRotateTextViewController; @Mock private FalsingManager mFalsingManager; + @Mock + private LockPatternUtils mLockPatternUtils; + @Mock + private IActivityManager mIActivityManager; @Captor private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener; @Captor @@ -208,7 +214,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mController = new KeyguardIndicationController(mContext, mWakeLockBuilder, mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor, mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats, - mUserManager, mExecutor, mFalsingManager); + mUserManager, mExecutor, mFalsingManager, mLockPatternUtils, mIActivityManager); + mController.init(); mController.setIndicationArea(mIndicationArea); verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index 2a5027309aec..0c65830ea455 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -16,9 +16,11 @@ package com.android.systemui.statusbar; +import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.content.Intent.ACTION_USER_SWITCHED; +import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_ALERTING; import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_MEDIA_CONTROLS; import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_PEOPLE; import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT; @@ -55,6 +57,7 @@ import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.NotificationLockscreenUserManager.KeyguardNotificationSuppressor; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; @@ -383,13 +386,44 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry)); } + @Test + public void testKeyguardNotificationSuppressors() { + // GIVEN a notification that should be shown on the lockscreen + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + final NotificationEntry entry = new NotificationEntryBuilder() + .setImportance(IMPORTANCE_HIGH) + .build(); + entry.setBucket(BUCKET_ALERTING); + + // WHEN a suppressor is added that filters out all entries + FakeKeyguardSuppressor suppressor = new FakeKeyguardSuppressor(); + mLockscreenUserManager.addKeyguardNotificationSuppressor(suppressor); + + // THEN it's filtered out + assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(entry)); + + // WHEN the suppressor no longer filters out entries + suppressor.setShouldSuppress(false); + + // THEN it's no longer filtered out + assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry)); + } + private class TestNotificationLockscreenUserManager extends NotificationLockscreenUserManagerImpl { public TestNotificationLockscreenUserManager(Context context) { - super(context, mBroadcastDispatcher, mDevicePolicyManager, mUserManager, - mClickNotifier, NotificationLockscreenUserManagerTest.this.mKeyguardManager, - mStatusBarStateController, Handler.createAsync(Looper.myLooper()), - mDeviceProvisionedController, mKeyguardStateController); + super( + context, + mBroadcastDispatcher, + mDevicePolicyManager, + mUserManager, + mClickNotifier, + NotificationLockscreenUserManagerTest.this.mKeyguardManager, + mStatusBarStateController, + Handler.createAsync(Looper.myLooper()), + mDeviceProvisionedController, + mKeyguardStateController); } public BroadcastReceiver getBaseBroadcastReceiverForTest() { @@ -404,4 +438,17 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { return mSettingsObserver; } } + + private static class FakeKeyguardSuppressor implements KeyguardNotificationSuppressor { + private boolean mShouldSuppress = true; + + @Override + public boolean shouldSuppressOnKeyguard(NotificationEntry entry) { + return mShouldSuppress; + } + + public void setShouldSuppress(boolean shouldSuppress) { + mShouldSuppress = shouldSuppress; + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt new file mode 100644 index 000000000000..9b5c33d64eb9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt @@ -0,0 +1,518 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.lockscreen + +import android.app.smartspace.SmartspaceManager +import android.app.smartspace.SmartspaceSession +import android.app.smartspace.SmartspaceSession.OnTargetsAvailableListener +import android.app.smartspace.SmartspaceTarget +import android.content.ComponentName +import android.content.ContentResolver +import android.content.pm.UserInfo +import android.database.ContentObserver +import android.graphics.drawable.Drawable +import android.net.Uri +import android.os.Handler +import android.os.UserHandle +import android.provider.Settings +import android.view.View +import android.widget.FrameLayout +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.plugins.BcSmartspaceDataPlugin +import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener +import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener +import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.FeatureFlags +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener +import com.android.systemui.util.concurrency.FakeExecution +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.util.time.FakeSystemClock +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import java.util.Optional + +@SmallTest +class LockscreenSmartspaceControllerTest : SysuiTestCase() { + @Mock + private lateinit var featureFlags: FeatureFlags + @Mock + private lateinit var smartspaceManager: SmartspaceManager + @Mock + private lateinit var smartspaceSession: SmartspaceSession + @Mock + private lateinit var activityStarter: ActivityStarter + @Mock + private lateinit var falsingManager: FalsingManager + @Mock + private lateinit var secureSettings: SecureSettings + @Mock + private lateinit var userTracker: UserTracker + @Mock + private lateinit var contentResolver: ContentResolver + @Mock + private lateinit var configurationController: ConfigurationController + @Mock + private lateinit var statusBarStateController: StatusBarStateController + @Mock + private lateinit var handler: Handler + + @Mock + private lateinit var plugin: BcSmartspaceDataPlugin + @Mock + private lateinit var controllerListener: SmartspaceTargetListener + + @Captor + private lateinit var sessionListenerCaptor: ArgumentCaptor<OnTargetsAvailableListener> + @Captor + private lateinit var userTrackerCaptor: ArgumentCaptor<UserTracker.Callback> + @Captor + private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver> + @Captor + private lateinit var configChangeListenerCaptor: ArgumentCaptor<ConfigurationListener> + @Captor + private lateinit var statusBarStateListenerCaptor: ArgumentCaptor<StateListener> + + private lateinit var sessionListener: OnTargetsAvailableListener + private lateinit var userListener: UserTracker.Callback + private lateinit var settingsObserver: ContentObserver + private lateinit var configChangeListener: ConfigurationListener + private lateinit var statusBarStateListener: StateListener + + private val clock = FakeSystemClock() + private val executor = FakeExecutor(clock) + private val execution = FakeExecution() + private val fakeParent = FrameLayout(context) + private val fakePrivateLockscreenSettingUri = Uri.Builder().appendPath("test").build() + + private val userHandlePrimary: UserHandle = UserHandle(0) + private val userHandleManaged: UserHandle = UserHandle(2) + private val userHandleSecondary: UserHandle = UserHandle(3) + + private val userList = listOf( + mockUserInfo(userHandlePrimary, isManagedProfile = false), + mockUserInfo(userHandleManaged, isManagedProfile = true), + mockUserInfo(userHandleSecondary, isManagedProfile = false) + ) + + private lateinit var controller: LockscreenSmartspaceController + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + `when`(featureFlags.isSmartspaceEnabled).thenReturn(true) + + `when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING)) + .thenReturn(fakePrivateLockscreenSettingUri) + `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession) + `when`(plugin.getView(any())).thenReturn(fakeSmartspaceView) + `when`(userTracker.userProfiles).thenReturn(userList) + `when`(statusBarStateController.dozeAmount).thenReturn(0.5f) + + setActiveUser(userHandlePrimary) + setAllowPrivateNotifications(userHandlePrimary, true) + setAllowPrivateNotifications(userHandleManaged, true) + setAllowPrivateNotifications(userHandleSecondary, true) + + controller = LockscreenSmartspaceController( + context, + featureFlags, + smartspaceManager, + activityStarter, + falsingManager, + secureSettings, + userTracker, + contentResolver, + configurationController, + statusBarStateController, + execution, + executor, + handler, + Optional.of(plugin) + ) + } + + @Test(expected = RuntimeException::class) + fun testThrowsIfFlagIsDisabled() { + // GIVEN the feature flag is disabled + `when`(featureFlags.isSmartspaceEnabled).thenReturn(false) + + // WHEN we try to build the view + controller.buildAndConnectView(fakeParent) + + // THEN an exception is thrown + } + + @Test + fun testListenersAreRegistered() { + // GIVEN a listener is added after a session is created + connectSession() + + // WHEN a listener is registered + controller.addListener(controllerListener) + + // THEN the listener is registered to the underlying plugin + verify(plugin).registerListener(controllerListener) + } + + @Test + fun testEarlyRegisteredListenersAreAttachedAfterConnected() { + // GIVEN a listener that is registered before the session is created + controller.addListener(controllerListener) + + // WHEN the session is created + connectSession() + + // THEN the listener is subsequently registered + verify(plugin).registerListener(controllerListener) + } + + @Test + fun testEmptyListIsEmittedAfterDisconnect() { + // GIVEN a registered listener on an active session + connectSession() + clearInvocations(plugin) + + // WHEN the session is closed + controller.disconnect() + + // THEN the listener receives an empty list of targets + verify(plugin).onTargetsAvailable(emptyList()) + } + + @Test + fun testUserChangeReloadsSmartspace() { + // GIVEN a connected smartspace session + connectSession() + + // WHEN the active user changes + userListener.onUserChanged(-1, context) + + // THEN we request a new smartspace update + verify(smartspaceSession).requestSmartspaceUpdate() + } + + @Test + fun testSettingsChangeReloadsSmartspace() { + // GIVEN a connected smartspace session + connectSession() + + // WHEN the lockscreen privacy setting changes + settingsObserver.onChange(true, null) + + // THEN we request a new smartspace update + verify(smartspaceSession).requestSmartspaceUpdate() + } + + @Test + fun testThemeChangeUpdatesTextColor() { + // GIVEN a connected smartspace session + connectSession() + + // WHEN the theme changes + configChangeListener.onThemeChanged() + + // We update the new text color to match the wallpaper color + verify(fakeSmartspaceView).setPrimaryTextColor(anyInt()) + } + + @Test + fun testDozeAmountChangeUpdatesView() { + // GIVEN a connected smartspace session + connectSession() + + // WHEN the doze amount changes + statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f) + + // We pass that along to the view + verify(fakeSmartspaceView).setDozeAmount(0.7f) + } + + @Test + fun testSensitiveTargetsAreNotFilteredIfAllowed() { + // GIVEN the active and managed users allow sensitive content + connectSession() + + // WHEN we receive a list of targets + val targets = listOf( + makeTarget(1, userHandlePrimary, isSensitive = true), + makeTarget(2, userHandleManaged, isSensitive = true), + makeTarget(3, userHandlePrimary, isSensitive = true) + ) + sessionListener.onTargetsAvailable(targets) + + // THEN all sensitive content is still shown + verify(plugin).onTargetsAvailable(eq(targets)) + } + + @Test + fun testNonSensitiveTargetsAreNeverFiltered() { + // GIVEN the active user doesn't allow sensitive lockscreen content + setAllowPrivateNotifications(userHandlePrimary, false) + connectSession() + + // WHEN we receive a list of targets + val targets = listOf( + makeTarget(1, userHandlePrimary), + makeTarget(2, userHandlePrimary), + makeTarget(3, userHandlePrimary) + ) + sessionListener.onTargetsAvailable(targets) + + // THEN all non-sensitive content is still shown + verify(plugin).onTargetsAvailable(eq(targets)) + } + + @Test + fun testSensitiveTargetsAreFilteredOutForAppropriateUsers() { + // GIVEN the active and managed users don't allow sensitive lockscreen content + setAllowPrivateNotifications(userHandlePrimary, false) + setAllowPrivateNotifications(userHandleManaged, false) + connectSession() + + // WHEN we receive a list of targets + val targets = listOf( + makeTarget(0, userHandlePrimary), + makeTarget(1, userHandlePrimary, isSensitive = true), + makeTarget(2, userHandleManaged, isSensitive = true), + makeTarget(3, userHandleManaged), + makeTarget(4, userHandlePrimary, isSensitive = true), + makeTarget(5, userHandlePrimary), + makeTarget(6, userHandleSecondary, isSensitive = true) + ) + sessionListener.onTargetsAvailable(targets) + + // THEN only non-sensitive content from those accounts is shown + verify(plugin).onTargetsAvailable(eq(listOf( + targets[0], + targets[3], + targets[5] + ))) + } + + @Test + fun testSettingsAreReloaded() { + // GIVEN a connected session where the privacy settings later flip to false + connectSession() + setAllowPrivateNotifications(userHandlePrimary, false) + setAllowPrivateNotifications(userHandleManaged, false) + settingsObserver.onChange(true, fakePrivateLockscreenSettingUri) + + // WHEN we receive a new list of targets + val targets = listOf( + makeTarget(1, userHandlePrimary, isSensitive = true), + makeTarget(2, userHandleManaged, isSensitive = true), + makeTarget(4, userHandlePrimary, isSensitive = true) + ) + sessionListener.onTargetsAvailable(targets) + + // THEN we filter based on the new settings values + verify(plugin).onTargetsAvailable(emptyList()) + } + + @Test + fun testRecognizeSwitchToSecondaryUser() { + // GIVEN an inactive secondary user that doesn't allow sensitive content + setAllowPrivateNotifications(userHandleSecondary, false) + connectSession() + + // WHEN the secondary user becomes the active user + setActiveUser(userHandleSecondary) + userListener.onUserChanged(userHandleSecondary.identifier, context) + + // WHEN we receive a new list of targets + val targets = listOf( + makeTarget(0, userHandlePrimary), + makeTarget(1, userHandleSecondary), + makeTarget(2, userHandleSecondary, isSensitive = true), + makeTarget(3, userHandleManaged), + makeTarget(4, userHandleSecondary), + makeTarget(5, userHandleManaged), + makeTarget(6, userHandlePrimary) + ) + sessionListener.onTargetsAvailable(targets) + + // THEN only non-sensitive content from the secondary user is shown + verify(plugin).onTargetsAvailable(listOf( + targets[1], + targets[4] + )) + } + + @Test + fun testUnregisterListenersOnCleanup() { + // GIVEN a connected session + connectSession() + + // WHEN we are told to cleanup + controller.disconnect() + + // THEN we disconnect from the session and unregister any listeners + verify(smartspaceSession).removeOnTargetsAvailableListener(sessionListener) + verify(smartspaceSession).close() + verify(userTracker).removeCallback(userListener) + verify(contentResolver).unregisterContentObserver(settingsObserver) + verify(configurationController).removeCallback(configChangeListener) + verify(statusBarStateController).removeCallback(statusBarStateListener) + } + + @Test + fun testBuildViewIsIdempotent() { + // GIVEN a connected session + connectSession() + clearInvocations(plugin) + + // WHEN we disconnect and then reconnect + controller.disconnect() + controller.buildAndConnectView(fakeParent) + + // THEN the view is not rebuilt + verify(plugin, never()).getView(any()) + assertEquals(fakeSmartspaceView, controller.view) + } + + @Test + fun testDoubleConnectIsIgnored() { + // GIVEN a connected session + connectSession() + clearInvocations(smartspaceManager) + clearInvocations(plugin) + + // WHEN we're asked to connect a second time and add to a parent + val view = controller.buildAndConnectView(fakeParent) + fakeParent.addView(view) + + // THEN the existing view and session are reused + verify(smartspaceManager, never()).createSmartspaceSession(any()) + verify(plugin, never()).getView(any()) + assertEquals(fakeSmartspaceView, controller.view) + } + + private fun connectSession() { + controller.buildAndConnectView(fakeParent) + + verify(smartspaceSession) + .addOnTargetsAvailableListener(any(), capture(sessionListenerCaptor)) + sessionListener = sessionListenerCaptor.value + + verify(userTracker).addCallback(capture(userTrackerCaptor), any()) + userListener = userTrackerCaptor.value + + verify(contentResolver).registerContentObserver( + eq(fakePrivateLockscreenSettingUri), + eq(true), + capture(settingsObserverCaptor), + eq(UserHandle.USER_ALL)) + settingsObserver = settingsObserverCaptor.value + + verify(configurationController).addCallback(configChangeListenerCaptor.capture()) + configChangeListener = configChangeListenerCaptor.value + + verify(statusBarStateController).addCallback(statusBarStateListenerCaptor.capture()) + statusBarStateListener = statusBarStateListenerCaptor.value + + verify(smartspaceSession).requestSmartspaceUpdate() + clearInvocations(smartspaceSession) + + verify(fakeSmartspaceView).setPrimaryTextColor(anyInt()) + verify(fakeSmartspaceView).setDozeAmount(0.5f) + clearInvocations(fakeSmartspaceView) + + fakeParent.addView(fakeSmartspaceView) + } + + private fun setActiveUser(userHandle: UserHandle) { + `when`(userTracker.userId).thenReturn(userHandle.identifier) + `when`(userTracker.userHandle).thenReturn(userHandle) + } + + private fun mockUserInfo(userHandle: UserHandle, isManagedProfile: Boolean): UserInfo { + val userInfo = mock(UserInfo::class.java) + `when`(userInfo.userHandle).thenReturn(userHandle) + `when`(userInfo.isManagedProfile).thenReturn(isManagedProfile) + return userInfo + } + + fun makeTarget( + id: Int, + userHandle: UserHandle, + isSensitive: Boolean = false + ): SmartspaceTarget { + return SmartspaceTarget.Builder( + "target$id", + ComponentName("testpackage", "testclass$id"), + userHandle) + .setSensitive(isSensitive) + .build() + } + + private fun setAllowPrivateNotifications(user: UserHandle, value: Boolean) { + `when`(secureSettings.getIntForUser( + eq(PRIVATE_LOCKSCREEN_SETTING), + anyInt(), + eq(user.identifier)) + ).thenReturn(if (value) 1 else 0) + } + + private val fakeSmartspaceView = spy(object : View(context), SmartspaceView { + override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) { + } + + override fun setPrimaryTextColor(color: Int) { + } + + override fun setDozeAmount(amount: Float) { + } + + override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) { + } + + override fun setFalsingManager(falsingManager: FalsingManager?) { + } + + override fun setDnd(image: Drawable?, description: String?) { + } + + override fun setNextAlarm(image: Drawable?, description: String?) { + } + }) +} + +private const val PRIVATE_LOCKSCREEN_SETTING = + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 6459c0c9f441..1be14b66e968 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -190,16 +190,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mEntryManager = new NotificationEntryManager( mLogger, mGroupManager, - () -> new NotificationRankingManager( - () -> mNotificationMediaManager, - mGroupManager, - mHeadsUpManager, - mock(NotificationFilter.class), - mLogger, - mock(NotificationSectionsFeatureManager.class), - mock(PeopleNotificationIdentifier.class), - mock(HighPriorityProvider.class)), - mEnvironment, mFeatureFlags, () -> mNotificationRowBinder, () -> mRemoteInputManager, @@ -207,6 +197,17 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mock(ForegroundServiceDismissalFeatureController.class), mock(IStatusBarService.class) ); + mEntryManager.setRanker( + new NotificationRankingManager( + () -> mNotificationMediaManager, + mGroupManager, + mHeadsUpManager, + mock(NotificationFilter.class), + mLogger, + mock(NotificationSectionsFeatureManager.class), + mock(PeopleNotificationIdentifier.class), + mock(HighPriorityProvider.class), + mEnvironment)); mEntryManager.setUpWithPresenter(mPresenter); mEntryManager.addNotificationEntryListener(mEntryListener); mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java index b493b9a6bd65..b1eef4b67a6f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java @@ -77,13 +77,16 @@ public class NotificationFilterTest extends SysuiTestCase { mock(StatusBarNotification.class); @Mock - ForegroundServiceController mFsc; + StatusBarStateController mStatusBarStateController; @Mock KeyguardEnvironment mEnvironment; @Mock - MediaFeatureFlag mMediaFeatureFlag; + ForegroundServiceController mFsc; @Mock - StatusBarStateController mStatusBarStateController; + NotificationLockscreenUserManager mUserManager; + @Mock + MediaFeatureFlag mMediaFeatureFlag; + private final IPackageManager mMockPackageManager = mock(IPackageManager.class); private NotificationFilter mNotificationFilter; @@ -127,7 +130,12 @@ public class NotificationFilterTest extends SysuiTestCase { mDependency, TestableLooper.get(this)); mRow = testHelper.createRow(); - mNotificationFilter = new NotificationFilter(mStatusBarStateController, mMediaFeatureFlag); + mNotificationFilter = new NotificationFilter( + mStatusBarStateController, + mEnvironment, + mFsc, + mUserManager, + mMediaFeatureFlag); } @After @@ -195,7 +203,11 @@ public class NotificationFilterTest extends SysuiTestCase { public void shouldFilterOtherNotificationWhenDisabled() { // GIVEN that the media feature is disabled when(mMediaFeatureFlag.getEnabled()).thenReturn(false); - NotificationFilter filter = new NotificationFilter(mStatusBarStateController, + NotificationFilter filter = new NotificationFilter( + mStatusBarStateController, + mEnvironment, + mFsc, + mUserManager, mMediaFeatureFlag); // WHEN the media filter is asked about an entry NotificationEntry otherEntry = new NotificationEntryBuilder().build(); @@ -208,7 +220,11 @@ public class NotificationFilterTest extends SysuiTestCase { public void shouldFilterOtherNotificationWhenEnabled() { // GIVEN that the media feature is enabled when(mMediaFeatureFlag.getEnabled()).thenReturn(true); - NotificationFilter filter = new NotificationFilter(mStatusBarStateController, + NotificationFilter filter = new NotificationFilter( + mStatusBarStateController, + mEnvironment, + mFsc, + mUserManager, mMediaFeatureFlag); // WHEN the media filter is asked about an entry NotificationEntry otherEntry = new NotificationEntryBuilder().build(); @@ -221,7 +237,11 @@ public class NotificationFilterTest extends SysuiTestCase { public void shouldFilterMediaNotificationWhenDisabled() { // GIVEN that the media feature is disabled when(mMediaFeatureFlag.getEnabled()).thenReturn(false); - NotificationFilter filter = new NotificationFilter(mStatusBarStateController, + NotificationFilter filter = new NotificationFilter( + mStatusBarStateController, + mEnvironment, + mFsc, + mUserManager, mMediaFeatureFlag); // WHEN the media filter is asked about a media entry final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry); @@ -233,7 +253,11 @@ public class NotificationFilterTest extends SysuiTestCase { public void shouldFilterMediaNotificationWhenEnabled() { // GIVEN that the media feature is enabled when(mMediaFeatureFlag.getEnabled()).thenReturn(true); - NotificationFilter filter = new NotificationFilter(mStatusBarStateController, + NotificationFilter filter = new NotificationFilter( + mStatusBarStateController, + mEnvironment, + mFsc, + mUserManager, mMediaFeatureFlag); // WHEN the media filter is asked about a media entry final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt index bdd4a01a9766..c51c628c5457 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt @@ -30,9 +30,11 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking import com.android.systemui.statusbar.NotificationMediaManager +import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger import com.android.systemui.statusbar.notification.NotificationFilter import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON @@ -42,7 +44,6 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.policy.HeadsUpManager import com.google.common.truth.Truth.assertThat import dagger.Lazy @@ -79,9 +80,11 @@ class NotificationRankingManagerTest : SysuiTestCase() { mock(NotificationEntryManagerLogger::class.java), sectionsManager, personNotificationIdentifier, - HighPriorityProvider(personNotificationIdentifier, - mock(NotificationGroupManagerLegacy::class.java)) - ) + HighPriorityProvider( + personNotificationIdentifier, + mock(NotificationGroupManagerLegacy::class.java)), + mock(KeyguardEnvironment::class.java) + ) } @Test @@ -486,7 +489,8 @@ class NotificationRankingManagerTest : SysuiTestCase() { logger: NotificationEntryManagerLogger, sectionsFeatureManager: NotificationSectionsFeatureManager, peopleNotificationIdentifier: PeopleNotificationIdentifier, - highPriorityProvider: HighPriorityProvider + highPriorityProvider: HighPriorityProvider, + keyguardEnvironment: KeyguardEnvironment ) : NotificationRankingManager( mediaManager, groupManager, @@ -495,7 +499,8 @@ class NotificationRankingManagerTest : SysuiTestCase() { logger, sectionsFeatureManager, peopleNotificationIdentifier, - highPriorityProvider + highPriorityProvider, + keyguardEnvironment ) { fun applyTestRankingMap(r: RankingMap) { rankingMap = r diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt new file mode 100644 index 000000000000..a8db8d7617b7 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator + +import android.app.smartspace.SmartspaceTarget +import android.content.ComponentName +import android.os.UserHandle +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.SysuiStatusBarStateController +import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController +import com.android.systemui.statusbar.notification.NotificationEntryManager +import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.time.FakeSystemClock +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.anyString +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import java.util.concurrent.TimeUnit + +@SmallTest +class SmartspaceDedupingCoordinatorTest : SysuiTestCase() { + + @Mock + private lateinit var statusBarStateController: SysuiStatusBarStateController + @Mock + private lateinit var smartspaceController: LockscreenSmartspaceController + @Mock + private lateinit var notificationEntryManager: NotificationEntryManager + @Mock + private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager + @Mock + private lateinit var notifPipeline: NotifPipeline + @Mock + private lateinit var pluggableListener: Pluggable.PluggableListener<NotifFilter> + + @Captor + private lateinit var filterCaptor: ArgumentCaptor<NotifFilter> + @Captor + private lateinit var collectionListenerCaptor: ArgumentCaptor<NotifCollectionListener> + @Captor + private lateinit var stateListenerCaptor: ArgumentCaptor<StatusBarStateController.StateListener> + @Captor + private lateinit var smartspaceListenerCaptor: ArgumentCaptor<SmartspaceTargetListener> + + private lateinit var filter: NotifFilter + private lateinit var collectionListener: NotifCollectionListener + private lateinit var statusBarListener: StatusBarStateController.StateListener + private lateinit var newTargetListener: SmartspaceTargetListener + + private lateinit var entry1HasRecentlyAlerted: NotificationEntry + private lateinit var entry2HasNotRecentlyAlerted: NotificationEntry + private lateinit var entry3NotAssociatedWithTarget: NotificationEntry + private lateinit var entry4HasNotRecentlyAlerted: NotificationEntry + private lateinit var target1: SmartspaceTarget + private lateinit var target2: SmartspaceTarget + private lateinit var target4: SmartspaceTarget + + private val clock = FakeSystemClock() + private val executor = FakeExecutor(clock) + private val now = clock.currentTimeMillis() + + private lateinit var deduper: SmartspaceDedupingCoordinator + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + // Mock out some behavior + `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + + // Build the deduper + deduper = SmartspaceDedupingCoordinator( + statusBarStateController, + smartspaceController, + notificationEntryManager, + notificationLockscreenUserManager, + notifPipeline, + executor, + clock + ) + + // Attach the deduper and capture the listeners/filters that it registers + deduper.attach(notifPipeline) + + verify(notifPipeline).addPreGroupFilter(filterCaptor.capture()) + filter = filterCaptor.value + filter.setInvalidationListener(pluggableListener) + + verify(notifPipeline).addCollectionListener(capture(collectionListenerCaptor)) + collectionListener = collectionListenerCaptor.value + + verify(statusBarStateController).addCallback(capture(stateListenerCaptor)) + statusBarListener = stateListenerCaptor.value + + verify(smartspaceController).addListener(capture(smartspaceListenerCaptor)) + newTargetListener = smartspaceListenerCaptor.value + + // Initialize some test data + entry1HasRecentlyAlerted = NotificationEntryBuilder() + .setPkg(PACKAGE_1) + .setId(11) + .setLastAudiblyAlertedMs(now - 10000) + .build() + entry2HasNotRecentlyAlerted = NotificationEntryBuilder() + .setPkg(PACKAGE_2) + .setId(22) + .build() + entry3NotAssociatedWithTarget = NotificationEntryBuilder() + .setPkg("com.test.package.3") + .setId(33) + .setLastAudiblyAlertedMs(now - 10000) + .build() + entry4HasNotRecentlyAlerted = NotificationEntryBuilder() + .setPkg(PACKAGE_2) + .setId(44) + .build() + + target1 = buildTargetFor(entry1HasRecentlyAlerted) + target2 = buildTargetFor(entry2HasNotRecentlyAlerted) + target4 = buildTargetFor(entry4HasNotRecentlyAlerted) + } + + @Test + fun testBasicFiltering() { + // GIVEN a few notifications + addEntries( + entry2HasNotRecentlyAlerted, + entry3NotAssociatedWithTarget, + entry4HasNotRecentlyAlerted) + + // WHEN we receive smartspace targets associated with entry 2 and 3 + sendTargets(target2, target4) + + // THEN both pipelines are rerun + verifyPipelinesInvalidated() + + // THEN the first target is filtered out, but the other ones aren't + assertTrue(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now)) + assertFalse(filter.shouldFilterOut(entry3NotAssociatedWithTarget, now)) + assertFalse(filter.shouldFilterOut(entry4HasNotRecentlyAlerted, now)) + } + + @Test + fun testDoNotFilterRecentlyAlertedNotifs() { + // GIVEN one notif that recently alerted and a second that hasn't + addEntries(entry1HasRecentlyAlerted, entry2HasNotRecentlyAlerted) + + // WHEN they become associated with smartspace targets + sendTargets(target1, target2) + + // THEN neither is filtered (the first because it's recently alerted and the second + // because it's not in the first position + verifyPipelinesNotInvalidated() + assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, now)) + assertFalse(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now)) + } + + @Test + fun testFilterAlertedButNotRecentNotifs() { + // GIVEN a notification that alerted, but a very long time ago + val entryOldAlert = NotificationEntryBuilder(entry1HasRecentlyAlerted) + .setLastAudiblyAlertedMs(now - 40000) + .build() + addEntries(entryOldAlert) + + // WHEN it becomes part of smartspace + val target = buildTargetFor(entryOldAlert) + sendTargets(target) + + // THEN it's still filtered out (because it's not in the alert window) + verifyPipelinesInvalidated() + assertTrue(filter.shouldFilterOut(entryOldAlert, now)) + } + + @Test + fun testExceptionExpires() { + // GIVEN a recently-alerted notif that is the primary smartspace target + addEntries(entry1HasRecentlyAlerted) + sendTargets(target1) + clearPipelineInvocations() + + // WHEN we go beyond the target's exception window + clock.advanceTime(20000) + + // THEN the pipeline is invalidated + verifyPipelinesInvalidated() + assertExecutorIsClear() + } + + @Test + fun testExceptionIsEventuallyFiltered() { + // GIVEN a notif that has recently alerted + addEntries(entry1HasRecentlyAlerted) + + // WHEN it becomes the primary smartspace target + sendTargets(target1) + + // THEN it isn't filtered out (because it recently alerted) + assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, now)) + + // WHEN we pass the alert window + clock.advanceTime(20000) + + // THEN the notif is once again filtered + assertTrue(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis())) + } + + @Test + fun testExceptionIsUpdated() { + // GIVEN a notif that has recently alerted and is the primary smartspace target + addEntries(entry1HasRecentlyAlerted) + sendTargets(target1) + clearPipelineInvocations() + assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis())) + + // GIVEN the notif is updated with a much more recent alert time + NotificationEntryBuilder(entry1HasRecentlyAlerted) + .setLastAudiblyAlertedMs(clock.currentTimeMillis() - 500) + .apply(entry1HasRecentlyAlerted) + updateEntries(entry1HasRecentlyAlerted) + assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis())) + + // WHEN we advance beyond the original exception window + clock.advanceTime(25000) + + // THEN the original exception window doesn't fire + verifyPipelinesNotInvalidated() + assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis())) + + // WHEN we advance beyond the new exception window + clock.advanceTime(4500) + + // THEN the pipelines are invalidated and no more timeouts are scheduled + verifyPipelinesInvalidated() + assertExecutorIsClear() + assertTrue(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis())) + } + + @Test + fun testReplacementIsCanceled() { + // GIVEN a single notif and smartspace target + addEntries(entry1HasRecentlyAlerted) + sendTargets(target1) + clearPipelineInvocations() + + // WHEN a higher-ranked target arrives + val newerEntry = NotificationEntryBuilder() + .setPkg(PACKAGE_2) + .setId(55) + .setLastAudiblyAlertedMs(now - 1000) + .build() + val newerTarget = buildTargetFor(newerEntry) + sendTargets(newerTarget, target1) + + // THEN the timeout of the other target is canceled and it is no longer filtered + assertExecutorIsClear() + assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, clock.uptimeMillis())) + verifyPipelinesInvalidated() + clearPipelineInvocations() + + // WHEN the entry associated with the newer target later arrives + addEntries(newerEntry) + + // THEN the entry is not filtered out (because it recently alerted) + assertFalse(filter.shouldFilterOut(newerEntry, clock.uptimeMillis())) + + // WHEN its exception window passes + clock.advanceTime(ALERT_WINDOW) + + // THEN we go back to filtering it + verifyPipelinesInvalidated() + assertExecutorIsClear() + assertTrue(filter.shouldFilterOut(newerEntry, clock.uptimeMillis())) + } + + @Test + fun testRetractedIsCanceled() { + // GIVEN A recently alerted target + addEntries(entry1HasRecentlyAlerted) + sendTargets(target1) + + // WHEN the entry is removed + removeEntries(entry1HasRecentlyAlerted) + + // THEN its pending timeout is canceled + assertExecutorIsClear() + clock.advanceTime(ALERT_WINDOW) + verifyPipelinesNotInvalidated() + } + + @Test + fun testTargetBeforeEntryFunctionsProperly() { + // WHEN targets are added before their entries exist + sendTargets(target2, target1) + + // THEN neither is filtered out + assertFalse(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now)) + assertFalse(filter.shouldFilterOut(entry1HasRecentlyAlerted, now)) + + // WHEN the entries are later added + addEntries(entry2HasNotRecentlyAlerted, entry1HasRecentlyAlerted) + + // THEN the pipelines are not invalidated (because they're already going to be rerun) + // but the first entry is still filtered out properly. + verifyPipelinesNotInvalidated() + assertTrue(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now)) + } + + @Test + fun testLockscreenTracking() { + // GIVEN a couple of smartspace targets that haven't alerted recently + addEntries(entry2HasNotRecentlyAlerted, entry4HasNotRecentlyAlerted) + sendTargets(target2, target4) + clearPipelineInvocations() + + assertTrue(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now)) + + // WHEN we are no longer on the keyguard + statusBarListener.onStateChanged(StatusBarState.SHADE) + + // THEN the new pipeline is invalidated (but the old one isn't because it's not + // necessary) because the notif should no longer be filtered out + verify(pluggableListener).onPluggableInvalidated(filter) + verify(notificationEntryManager, never()).updateNotifications(anyString()) + assertFalse(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now)) + } + + private fun buildTargetFor(entry: NotificationEntry): SmartspaceTarget { + return SmartspaceTarget + .Builder("test", ComponentName("test", "class"), UserHandle.CURRENT) + .setSourceNotificationKey(entry.key) + .build() + } + + private fun addEntries(vararg entries: NotificationEntry) { + for (entry in entries) { + `when`(notifPipeline.getEntry(entry.key)).thenReturn(entry) + collectionListener.onEntryAdded(entry) + } + } + + private fun updateEntries(vararg entries: NotificationEntry) { + for (entry in entries) { + `when`(notifPipeline.getEntry(entry.key)).thenReturn(entry) + collectionListener.onEntryUpdated(entry) + } + } + + private fun removeEntries(vararg entries: NotificationEntry) { + for (entry in entries) { + `when`(notifPipeline.getEntry(entry.key)).thenReturn(null) + collectionListener.onEntryRemoved(entry, 0) + } + } + + private fun sendTargets(vararg targets: SmartspaceTarget) { + newTargetListener.onSmartspaceTargetsUpdated(targets.toMutableList()) + } + + private fun verifyPipelinesInvalidated() { + verify(pluggableListener).onPluggableInvalidated(filter) + verify(notificationEntryManager).updateNotifications(anyString()) + } + + private fun assertExecutorIsClear() { + assertEquals(0, executor.numPending()) + } + + private fun verifyPipelinesNotInvalidated() { + verify(pluggableListener, never()).onPluggableInvalidated(filter) + verify(notificationEntryManager, never()).updateNotifications(anyString()) + } + + private fun clearPipelineInvocations() { + clearInvocations(pluggableListener) + clearInvocations(notificationEntryManager) + } +} + +private val ALERT_WINDOW = TimeUnit.SECONDS.toMillis(30) +private const val PACKAGE_1 = "com.test.package.1" +private const val PACKAGE_2 = "com.test.package.2" diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 7b0c06736979..cea49b71f009 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -181,16 +181,6 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mEntryManager = new NotificationEntryManager( mock(NotificationEntryManagerLogger.class), mGroupMembershipManager, - () -> new NotificationRankingManager( - () -> mock(NotificationMediaManager.class), - mGroupMembershipManager, - mHeadsUpManager, - mock(NotificationFilter.class), - mock(NotificationEntryManagerLogger.class), - mock(NotificationSectionsFeatureManager.class), - mock(PeopleNotificationIdentifier.class), - mock(HighPriorityProvider.class)), - mEnvironment, mFeatureFlags, () -> mRowBinder, () -> mRemoteInputManager, @@ -198,6 +188,17 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mock(ForegroundServiceDismissalFeatureController.class), mock(IStatusBarService.class) ); + mEntryManager.setRanker( + new NotificationRankingManager( + () -> mock(NotificationMediaManager.class), + mGroupMembershipManager, + mHeadsUpManager, + mock(NotificationFilter.class), + mock(NotificationEntryManagerLogger.class), + mock(NotificationSectionsFeatureManager.class), + mock(PeopleNotificationIdentifier.class), + mock(HighPriorityProvider.class), + mEnvironment)); NotifRemoteViewCache cache = new NotifRemoteViewCacheImpl(mEntryManager); NotifBindPipeline pipeline = new NotifBindPipeline( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index a63d509b0b53..b54f9234188f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -42,6 +42,8 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -90,6 +92,12 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { private DozeParameters mDozeParameters; @Mock private MetricsLogger mMetricsLogger; + @Mock + private NotificationMediaManager mNotificationMediaManager; + @Mock + private WakefulnessLifecycle mWakefulnessLifecycle; + @Mock + private ScreenLifecycle mScreenLifecycle; private BiometricUnlockController mBiometricUnlockController; @Before @@ -109,7 +117,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mKeyguardViewMediator, mScrimController, mShadeController, mNotificationShadeWindowController, mKeyguardStateController, mHandler, mUpdateMonitor, res.getResources(), mKeyguardBypassController, mDozeParameters, - mMetricsLogger, mDumpManager); + mMetricsLogger, mDumpManager, mPowerManager, + mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle); mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager); mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener); } @@ -121,8 +130,6 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */); verify(mStatusBarKeyguardViewManager).showBouncer(eq(false)); - verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(), - anyFloat()); verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean()); assertThat(mBiometricUnlockController.getMode()) .isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER); @@ -161,7 +168,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { } @Test - public void onBiometricAuthenticated_whenFingerprint_dismissKeyguard() { + public void onBiometricAuthenticated_whenFingerprint_notifyKeyguardAuthenticated() { when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); // the value of isStrongBiometric doesn't matter here since we only care about the returned // value of isUnlockingWithBiometricAllowed() @@ -169,8 +176,6 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */); verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); - verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(), - anyFloat()); verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false)); assertThat(mBiometricUnlockController.getMode()) .isEqualTo(BiometricUnlockController.MODE_UNLOCK_COLLAPSING); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt index 896e33073cc3..9a7ab2870905 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt @@ -29,6 +29,7 @@ import android.testing.TestableLooper import android.view.LayoutInflater import android.widget.LinearLayout import androidx.test.filters.SmallTest +import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.ActivityStarter @@ -70,6 +71,7 @@ class OngoingCallControllerTest : SysuiTestCase() { private val clock = FakeSystemClock() private val mainExecutor = FakeExecutor(clock) + private val uiEventLoggerFake = UiEventLoggerFake() private lateinit var controller: OngoingCallController private lateinit var notifCollectionListener: NotifCollectionListener @@ -99,7 +101,8 @@ class OngoingCallControllerTest : SysuiTestCase() { clock, mockActivityStarter, mainExecutor, - mockIActivityManager) + mockIActivityManager, + OngoingCallLogger(uiEventLoggerFake)) controller.init() controller.addCallback(mockOngoingCallListener) controller.setChipView(chipView) @@ -256,6 +259,28 @@ class OngoingCallControllerTest : SysuiTestCase() { .onOngoingCallStateChanged(anyBoolean()) } + @Test + fun chipClicked_clickEventLogged() { + notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) + + chipView.performClick() + + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1) + assertThat(uiEventLoggerFake.eventId(0)) + .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id) + } + + @Test + fun notifyChipVisibilityChanged_visibleEventLogged() { + controller.notifyChipVisibilityChanged(true) + + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1) + assertThat(uiEventLoggerFake.eventId(0)) + .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id) + } + // Other tests for notifyChipVisibilityChanged are in [OngoingCallLogger], since + // [OngoingCallController.notifyChipVisibilityChanged] just delegates to that class. + private fun createOngoingCallNotifEntry(): NotificationEntry { val notificationEntryBuilder = NotificationEntryBuilder() notificationEntryBuilder.modifyNotification(context).style = ongoingCallStyle diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt new file mode 100644 index 000000000000..ecec124e83e2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone.ongoingcall + +import androidx.test.filters.SmallTest +import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +@SmallTest +class OngoingCallLoggerTest : SysuiTestCase() { + private val uiEventLoggerFake = UiEventLoggerFake() + private val ongoingCallLogger = OngoingCallLogger(uiEventLoggerFake) + + @Test + fun logChipClicked_clickEventLogged() { + ongoingCallLogger.logChipClicked() + + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1) + assertThat(uiEventLoggerFake.eventId(0)) + .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id) + } + + @Test + fun logChipVisibilityChanged_changeFromInvisibleToVisible_visibleEventLogged() { + ongoingCallLogger.logChipVisibilityChanged(false) + ongoingCallLogger.logChipVisibilityChanged(true) + + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1) + assertThat(uiEventLoggerFake.eventId(0)) + .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id) + } + + @Test + fun logChipVisibilityChanged_changeFromVisibleToInvisible_eventNotLogged() { + // Setting the chip to visible here will trigger a log + ongoingCallLogger.logChipVisibilityChanged(true) + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1) + + ongoingCallLogger.logChipVisibilityChanged(false) + + // Expect that there were no new logs + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1) + } + + @Test + fun logChipVisibilityChanged_visibleThenVisibleAgain_eventNotLogged() { + // Setting the chip to visible here will trigger a log + ongoingCallLogger.logChipVisibilityChanged(true) + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1) + + ongoingCallLogger.logChipVisibilityChanged(true) + + // Expect that there were no new logs + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index e5b7e3f71ac1..32aee2bd6554 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -120,7 +120,7 @@ public class SmartReplyViewTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mReceiver = new BlockingQueueIntentReceiver(); mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION)); - mKeyguardDismissUtil.setDismissHandler((action, unused) -> action.onDismiss()); + mKeyguardDismissUtil.setDismissHandler((action, unused, afterKgGone) -> action.onDismiss()); mDependency.injectMockDependency(KeyguardUpdateMonitor.class); mDependency.injectMockDependency(ShadeController.class); mDependency.injectMockDependency(NotificationRemoteInputManager.class); @@ -183,7 +183,7 @@ public class SmartReplyViewTest extends SysuiTestCase { @Test public void testSendSmartReply_keyguardCancelled() throws InterruptedException { - mKeyguardDismissUtil.setDismissHandler((action, unused) -> { }); + mKeyguardDismissUtil.setDismissHandler((action, unused, afterKgGone) -> { }); setSmartReplies(TEST_CHOICES); mView.getChildAt(2).performClick(); @@ -195,7 +195,8 @@ public class SmartReplyViewTest extends SysuiTestCase { public void testSendSmartReply_waitsForKeyguard() throws InterruptedException { AtomicReference<OnDismissAction> actionRef = new AtomicReference<>(); - mKeyguardDismissUtil.setDismissHandler((action, unused) -> actionRef.set(action)); + mKeyguardDismissUtil.setDismissHandler((action, unused, afterKgGone) + -> actionRef.set(action)); setSmartReplies(TEST_CHOICES); mView.getChildAt(2).performClick(); 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 2d9d7154d229..9f1dad84b6ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -185,7 +185,8 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { Color.valueOf(Color.BLUE), null); String jsonString = - "{\"android.theme.customization.system_palette\":\"override.package.name\"}"; + "{\"android.theme.customization.system_palette\":\"override.package.name\"," + + "\"android.theme.customization.color_source\":\"preset\"}"; when(mSecureSettings.getStringForUser( eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt())) .thenReturn(jsonString); @@ -203,6 +204,32 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { } @Test + public void onWallpaperColorsChanged_resetThemeIfNotPreset() { + // Should ask for a new theme when wallpaper colors change + WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED), + Color.valueOf(Color.BLUE), null); + + String jsonString = + "{\"android.theme.customization.system_palette\":\"override.package.name\"," + + "\"android.theme.customization.color_source\":\"home_wallpaper\"}"; + when(mSecureSettings.getStringForUser( + eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt())) + .thenReturn(jsonString); + + mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM); + + ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class); + verify(mSecureSettings).putString( + eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture()); + + assertThat(updatedSetting.getValue().contains("android.theme.customization.system_palette")) + .isFalse(); + + 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/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java index ac5da17c6358..cd1eb1c4468e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java @@ -182,6 +182,80 @@ public class WalletScreenControllerTest extends SysuiTestCase { } @Test + public void queryCards_hasCards_showCarousel_badCard_parseLabel_notCrash() { + GetWalletCardsResponse response = + new GetWalletCardsResponse( + Collections.singletonList(createCrazyWalletCard(mContext, true)), 0); + + mController.queryWalletCards(); + mTestableLooper.processAllMessages(); + + verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture()); + + QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback = + mCallbackCaptor.getValue(); + + assertEquals(mController, callback); + + callback.onWalletCardsRetrieved(response); + mTestableLooper.processAllMessages(); + + assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility()); + assertEquals("This\nis\ncrazy!!", mWalletView.getCardLabel().getText().toString()); + assertEquals(GONE, mWalletView.getActionButton().getVisibility()); + assertEquals(GONE, mWalletView.getErrorView().getVisibility()); + } + + @Test + public void queryCards_hasCards_showCarousel_badCard_noLabel_notCrash() { + GetWalletCardsResponse response = + new GetWalletCardsResponse( + Collections.singletonList(createCrazyWalletCard(mContext, false)), 0); + + mController.queryWalletCards(); + mTestableLooper.processAllMessages(); + + verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture()); + + QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback = + mCallbackCaptor.getValue(); + + assertEquals(mController, callback); + + callback.onWalletCardsRetrieved(response); + mTestableLooper.processAllMessages(); + + assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility()); + assertEquals("", mWalletView.getCardLabel().getText().toString()); + assertEquals(GONE, mWalletView.getActionButton().getVisibility()); + assertEquals(GONE, mWalletView.getErrorView().getVisibility()); + } + + @Test + public void queryCards_hasCards_showCarousel_invalidSelectedIndex_notCrash() { + GetWalletCardsResponse response = + new GetWalletCardsResponse( + Collections.singletonList(createCrazyWalletCard(mContext, true)), 8); + + mController.queryWalletCards(); + mTestableLooper.processAllMessages(); + + verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture()); + + QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback = + mCallbackCaptor.getValue(); + + assertEquals(mController, callback); + + callback.onWalletCardsRetrieved(response); + mTestableLooper.processAllMessages(); + + assertEquals(GONE, mWalletView.getCardCarouselContainer().getVisibility()); + assertEquals(VISIBLE, mWalletView.getEmptyStateView().getVisibility()); + assertEquals(GONE, mWalletView.getErrorView().getVisibility()); + } + + @Test public void queryCards_noCards_showEmptyState() { GetWalletCardsResponse response = new GetWalletCardsResponse(Collections.EMPTY_LIST, 0); @@ -329,6 +403,15 @@ public class WalletScreenControllerTest extends SysuiTestCase { .build(); } + private WalletCard createCrazyWalletCard(Context context, boolean hasLabel) { + PendingIntent pendingIntent = + PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE); + return new WalletCard.Builder("BadCard", createIcon(), "•••• 1234", pendingIntent) + .setCardIcon(null) + .setCardLabel(hasLabel ? "This\nis\ncrazy!!" : null) + .build(); + } + private static Icon createIcon() { return Icon.createWithBitmap(Bitmap.createBitmap(70, 44, Bitmap.Config.ARGB_8888)); } diff --git a/services/core/Android.bp b/services/core/Android.bp index 5dc11c588ef2..44307ef73fe0 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -21,7 +21,6 @@ filegroup { name: "services.core-sources", srcs: ["java/**/*.java"], exclude_srcs: [ - ":connectivity-service-srcs", ":services.core-sources-am-wm" ], path: "java", @@ -214,32 +213,3 @@ filegroup { ], } -// TODO: Move connectivity service sources to independent directory. -filegroup { - name: "connectivity-service-srcs", - srcs: [ - "java/com/android/server/ConnectivityService.java", - "java/com/android/server/ConnectivityServiceInitializer.java", - "java/com/android/server/NetIdManager.java", - "java/com/android/server/TestNetworkService.java", - "java/com/android/server/connectivity/AutodestructReference.java", - "java/com/android/server/connectivity/DnsManager.java", - "java/com/android/server/connectivity/FullScore.java", - "java/com/android/server/connectivity/KeepaliveTracker.java", - "java/com/android/server/connectivity/LingerMonitor.java", - "java/com/android/server/connectivity/MockableSystemProperties.java", - "java/com/android/server/connectivity/Nat464Xlat.java", - "java/com/android/server/connectivity/NetworkAgentInfo.java", - "java/com/android/server/connectivity/NetworkDiagnostics.java", - "java/com/android/server/connectivity/NetworkNotificationManager.java", - "java/com/android/server/connectivity/NetworkOffer.java", - "java/com/android/server/connectivity/NetworkRanker.java", - "java/com/android/server/connectivity/OsCompat.java", - "java/com/android/server/connectivity/PermissionMonitor.java", - "java/com/android/server/connectivity/ProfileNetworkPreferences.java", - "java/com/android/server/connectivity/ProxyTracker.java", - "java/com/android/server/connectivity/QosCallbackAgentConnection.java", - "java/com/android/server/connectivity/QosCallbackTracker.java", - "java/com/android/server/connectivity/TcpKeepaliveController.java", - ], -} diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index a05eb6818b17..ca59ce3acac7 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -20,12 +20,17 @@ import static android.Manifest.permission.MANAGE_SENSOR_PRIVACY; import static android.app.ActivityManager.RunningServiceInfo; import static android.app.ActivityManager.RunningTaskInfo; import static android.app.ActivityManager.getCurrentUser; +import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_IGNORED; import static android.app.AppOpsManager.OP_CAMERA; +import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA; +import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD; import static android.content.Intent.EXTRA_PACKAGE_NAME; +import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS; import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR; import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; @@ -60,6 +65,7 @@ import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; @@ -141,6 +147,7 @@ public final class SensorPrivacyService extends SystemService { private static final int VER0_INDIVIDUAL_ENABLED = 1; private static final int VER1_ENABLED = 0; private static final int VER1_INDIVIDUAL_ENABLED = 1; + public static final int REMINDER_DIALOG_DELAY_MILLIS = 500; private final Context mContext; private final SensorPrivacyServiceImpl mSensorPrivacyServiceImpl; @@ -206,6 +213,36 @@ public final class SensorPrivacyService extends SystemService { private ArrayMap<Pair<String, UserHandle>, ArrayList<IBinder>> mSuppressReminders = new ArrayMap<>(); + private final ArrayMap<SensorUseReminderDialogInfo, ArraySet<Integer>> + mQueuedSensorUseReminderDialogs = new ArrayMap<>(); + + private class SensorUseReminderDialogInfo { + private int mTaskId; + private UserHandle mUser; + private String mPackageName; + + SensorUseReminderDialogInfo(int taskId, UserHandle user, String packageName) { + mTaskId = taskId; + mUser = user; + mPackageName = packageName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof SensorUseReminderDialogInfo)) return false; + SensorUseReminderDialogInfo that = (SensorUseReminderDialogInfo) o; + return mTaskId == that.mTaskId + && Objects.equals(mUser, that.mUser) + && Objects.equals(mPackageName, that.mPackageName); + } + + @Override + public int hashCode() { + return Objects.hash(mTaskId, mUser, mPackageName); + } + } + SensorPrivacyServiceImpl() { mHandler = new SensorPrivacyHandler(FgThread.get().getLooper(), mContext); File sensorPrivacyFile = new File(Environment.getDataSystemDirectory(), @@ -228,7 +265,8 @@ public final class SensorPrivacyService extends SystemService { } } - int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_CAMERA}; + int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE, + OP_CAMERA, OP_PHONE_CALL_CAMERA}; mAppOpsManager.startWatchingNoted(micAndCameraOps, this); mAppOpsManager.startWatchingStarted(micAndCameraOps, this); @@ -254,15 +292,29 @@ public final class SensorPrivacyService extends SystemService { public void onOpNoted(int code, int uid, String packageName, String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) { - if (result != MODE_IGNORED || (flags & AppOpsManager.OP_FLAGS_ALL_TRUSTED) == 0) { + if ((flags & AppOpsManager.OP_FLAGS_ALL_TRUSTED) == 0) { return; } int sensor; - if (code == OP_RECORD_AUDIO) { - sensor = MICROPHONE; + if (result == MODE_IGNORED) { + if (code == OP_RECORD_AUDIO) { + sensor = MICROPHONE; + } else if (code == OP_CAMERA) { + sensor = CAMERA; + } else { + return; + } + } else if (result == MODE_ALLOWED) { + if (code == OP_PHONE_CALL_MICROPHONE) { + sensor = MICROPHONE; + } else if (code == OP_PHONE_CALL_CAMERA) { + sensor = CAMERA; + } else { + return; + } } else { - sensor = CAMERA; + return; } long token = Binder.clearCallingIdentity(); @@ -294,6 +346,11 @@ public final class SensorPrivacyService extends SystemService { } } + if (uid == Process.SYSTEM_UID) { + enqueueSensorUseReminderDialogAsync(-1, user, packageName, sensor); + return; + } + // TODO: Handle reminders with multiple sensors // - If we have a likely activity that triggered the sensor use overlay a dialog over @@ -312,7 +369,7 @@ public final class SensorPrivacyService extends SystemService { if (task.isVisible && task.topActivity.getPackageName().equals(packageName)) { if (task.isFocused) { // There is the one focused activity - showSensorUseReminderDialog(task.taskId, user, packageName, sensor); + enqueueSensorUseReminderDialogAsync(task.taskId, user, packageName, sensor); return; } @@ -323,7 +380,7 @@ public final class SensorPrivacyService extends SystemService { // TODO: Test this case // There is one or more non-focused activity if (tasksOfPackageUsingSensor.size() == 1) { - showSensorUseReminderDialog(tasksOfPackageUsingSensor.get(0).taskId, user, + enqueueSensorUseReminderDialogAsync(tasksOfPackageUsingSensor.get(0).taskId, user, packageName, sensor); return; } else if (tasksOfPackageUsingSensor.size() > 1) { @@ -360,21 +417,60 @@ public final class SensorPrivacyService extends SystemService { * @param packageName The name of the package using the sensor. * @param sensor The sensor that is being used. */ - private void showSensorUseReminderDialog(int taskId, @NonNull UserHandle user, + private void enqueueSensorUseReminderDialogAsync(int taskId, @NonNull UserHandle user, @NonNull String packageName, int sensor) { + mHandler.sendMessage(PooledLambda.obtainMessage( + this:: enqueueSensorUseReminderDialog, taskId, user, packageName, sensor)); + } + + private void enqueueSensorUseReminderDialog(int taskId, @NonNull UserHandle user, + @NonNull String packageName, int sensor) { + SensorUseReminderDialogInfo info = + new SensorUseReminderDialogInfo(taskId, user, packageName); + if (!mQueuedSensorUseReminderDialogs.containsKey(info)) { + ArraySet<Integer> sensors = new ArraySet<Integer>(); + sensors.add(sensor); + mQueuedSensorUseReminderDialogs.put(info, sensors); + mHandler.sendMessageDelayed( + PooledLambda.obtainMessage(this::showSensorUserReminderDialog, info), + REMINDER_DIALOG_DELAY_MILLIS); + return; + } + ArraySet<Integer> sensors = mQueuedSensorUseReminderDialogs.get(info); + sensors.add(sensor); + } + + private void showSensorUserReminderDialog(@NonNull SensorUseReminderDialogInfo info) { + ArraySet<Integer> sensors = mQueuedSensorUseReminderDialogs.get(info); + mQueuedSensorUseReminderDialogs.remove(info); + if (sensors == null) { + Log.e(TAG, "Unable to show sensor use dialog because sensor set is null." + + " Was the dialog queue modified from outside the handler thread?"); + return; + } Intent dialogIntent = new Intent(); dialogIntent.setComponent(ComponentName.unflattenFromString( mContext.getResources().getString( R.string.config_sensorUseStartedActivity))); ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchTaskId(taskId); + options.setLaunchTaskId(info.mTaskId); options.setTaskOverlay(true, true); - dialogIntent.putExtra(EXTRA_PACKAGE_NAME, packageName); - dialogIntent.putExtra(EXTRA_SENSOR, sensor); + dialogIntent.addFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - mContext.startActivityAsUser(dialogIntent, options.toBundle(), user); + dialogIntent.putExtra(EXTRA_PACKAGE_NAME, info.mPackageName); + if (sensors.size() == 1) { + dialogIntent.putExtra(EXTRA_SENSOR, sensors.valueAt(0)); + } else if (sensors.size() == 2) { + dialogIntent.putExtra(EXTRA_ALL_SENSORS, true); + } else { + // Currently the only cases can be 1 or two + Log.e(TAG, "Attempted to show sensor use dialog for " + sensors.size() + + " sensors"); + return; + } + mContext.startActivityAsUser(dialogIntent, options.toBundle(), info.mUser); } /** diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 2bb90846d292..e7e3ce9d2d61 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -167,7 +167,6 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull private final VcnNetworkProvider mNetworkProvider; @NonNull private final TelephonySubscriptionTrackerCallback mTelephonySubscriptionTrackerCb; @NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker; - @NonNull private final VcnContext mVcnContext; @NonNull private final BroadcastReceiver mPkgChangeReceiver; @NonNull @@ -212,7 +211,6 @@ public class VcnManagementService extends IVcnManagementService.Stub { mContext, mLooper, mTelephonySubscriptionTrackerCb); mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE); - mVcnContext = mDeps.newVcnContext(mContext, mLooper, mNetworkProvider); mPkgChangeReceiver = new BroadcastReceiver() { @Override @@ -336,8 +334,9 @@ public class VcnManagementService extends IVcnManagementService.Stub { public VcnContext newVcnContext( @NonNull Context context, @NonNull Looper looper, - @NonNull VcnNetworkProvider vcnNetworkProvider) { - return new VcnContext(context, looper, vcnNetworkProvider); + @NonNull VcnNetworkProvider vcnNetworkProvider, + boolean isInTestMode) { + return new VcnContext(context, looper, vcnNetworkProvider, isInTestMode); } /** Creates a new Vcn instance using the provided configuration */ @@ -419,6 +418,14 @@ public class VcnManagementService extends IVcnManagementService.Stub { "Carrier privilege required for subscription group to set VCN Config"); } + private void enforceManageTestNetworksForTestMode(@NonNull VcnConfig vcnConfig) { + if (vcnConfig.isTestModeProfile()) { + mContext.enforceCallingPermission( + android.Manifest.permission.MANAGE_TEST_NETWORKS, + "Test-mode require the MANAGE_TEST_NETWORKS permission"); + } + } + private class VcnSubscriptionTrackerCallback implements TelephonySubscriptionTrackerCallback { /** * Handles subscription group changes, as notified by {@link TelephonySubscriptionTracker} @@ -542,8 +549,11 @@ public class VcnManagementService extends IVcnManagementService.Stub { final VcnCallbackImpl vcnCallback = new VcnCallbackImpl(subscriptionGroup); + final VcnContext vcnContext = + mDeps.newVcnContext( + mContext, mLooper, mNetworkProvider, config.isTestModeProfile()); final Vcn newInstance = - mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback); + mDeps.newVcn(vcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback); mVcns.put(subscriptionGroup, newInstance); // Now that a new VCN has started, notify all registered listeners to refresh their @@ -587,6 +597,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { mContext.getSystemService(AppOpsManager.class) .checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName()); + enforceManageTestNetworksForTestMode(config); enforceCallingUserAndCarrierPrivilege(subscriptionGroup, opPkgName); Binder.withCleanCallingIdentity(() -> { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index aadb25c7ef7f..f480cbc26ff4 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -16,6 +16,8 @@ package com.android.server.am; +import static android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND; +import static android.Manifest.permission.REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND; import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND; import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT; @@ -5831,6 +5833,26 @@ public final class ActiveServices { } } + // Check for CDM apps with either REQUEST_COMPANION_RUN_IN_BACKGROUND or + // REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND. + // Note: When a CDM app has REQUEST_COMPANION_RUN_IN_BACKGROUND, the app is also put + // in the user-allowlist. However, in this case, we want to use the reason code + // REASON_COMPANION_DEVICE_MANAGER, so this check needs to be before the + // isAllowlistedForFgsStartLOSP check. + if (ret == REASON_DENIED) { + final boolean isCompanionApp = mAm.mInternal.isAssociatedCompanionApp( + UserHandle.getUserId(callingUid), callingUid); + if (isCompanionApp) { + if (isPermissionGranted( + REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND, + callingPid, callingUid) + || isPermissionGranted(REQUEST_COMPANION_RUN_IN_BACKGROUND, + callingPid, callingUid)) { + ret = REASON_COMPANION_DEVICE_MANAGER; + } + } + } + if (ret == REASON_DENIED) { ActivityManagerService.FgsTempAllowListItem item = mAm.isAllowlistedForFgsStartLOSP(callingUid); @@ -5858,14 +5880,6 @@ public final class ActiveServices { } if (ret == REASON_DENIED) { - final boolean isCompanionApp = mAm.mInternal.isAssociatedCompanionApp( - UserHandle.getUserId(callingUid), callingUid); - if (isCompanionApp) { - ret = REASON_COMPANION_DEVICE_MANAGER; - } - } - - if (ret == REASON_DENIED) { final AppOpsManager appOpsManager = mAm.getAppOpsManager(); if (appOpsManager.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, callingUid, callingPackage) == AppOpsManager.MODE_ALLOWED) { @@ -5884,6 +5898,10 @@ public final class ActiveServices { return ret; } + private boolean isPermissionGranted(String permission, int callingPid, int callingUid) { + return mAm.checkPermission(permission, callingPid, callingUid) == PERMISSION_GRANTED; + } + private static boolean isFgsBgStart(@ReasonCode int code) { return code != REASON_PROC_STATE_PERSISTENT && code != REASON_PROC_STATE_PERSISTENT_UI @@ -5957,7 +5975,7 @@ public final class ActiveServices { } FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, - r.shortInstanceName, + null, state, r.mAllowWhileInUsePermissionInFgs, r.mAllowStartForeground, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 83cff1578d08..6661f88f3fdf 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -14621,7 +14621,9 @@ public class ActivityManagerService extends IActivityManager.Stub String reason, @TempAllowListType int type, int callingUid) { synchronized (mProcLock) { // The temp allowlist type could change according to the reasonCode. - type = mLocalDeviceIdleController.getTempAllowListType(reasonCode, type); + if (mLocalDeviceIdleController != null) { + type = mLocalDeviceIdleController.getTempAllowListType(reasonCode, type); + } if (type == TEMPORARY_ALLOW_LIST_TYPE_NONE) { return; } diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index 6429b79f6111..801e3824d7b0 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -25,7 +25,6 @@ import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.PowerWhitelistManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; import static android.os.PowerWhitelistManager.REASON_BACKGROUND_FGS_PERMISSION; -import static android.os.PowerWhitelistManager.REASON_COMPANION_DEVICE_MANAGER; import static android.os.PowerWhitelistManager.REASON_DENIED; import static android.os.PowerWhitelistManager.REASON_DEVICE_OWNER; import static android.os.PowerWhitelistManager.REASON_PROFILE_OWNER; @@ -1217,6 +1216,7 @@ final class ProcessStateRecord { mAllowStartFgs = mAllowStartFgsByPermission = ret; } + // TODO(b/188063200) Clean up this method. Why do we need to duplicate only some of the checks? @GuardedBy("mService") void setAllowStartFgs() { if (mAllowStartFgs != REASON_DENIED) { @@ -1238,16 +1238,6 @@ final class ProcessStateRecord { } if (mAllowStartFgs == REASON_DENIED) { - if (mService.mInternal != null) { - final boolean isCompanionApp = mService.mInternal.isAssociatedCompanionApp( - UserHandle.getUserId(mApp.info.uid), mApp.info.uid); - if (isCompanionApp) { - mAllowStartFgs = REASON_COMPANION_DEVICE_MANAGER; - } - } - } - - if (mAllowStartFgs == REASON_DENIED) { // Is the calling UID a profile owner app? if (mService.mInternal != null) { if (mService.mInternal.isProfileOwner(mApp.info.uid)) { diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index a23b5eb50b15..13dc444dc44b 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -1085,7 +1085,7 @@ public class AppOpsService extends IAppOpsService.Stub { } // no need to record a paused event finishing. - InProgressStartOpEvent event = mInProgressEvents.valueAt(indexOfToken); + InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(indexOfToken); event.numUnfinishedStarts--; if (event.numUnfinishedStarts == 0) { mPausedInProgressEvents.removeAt(indexOfToken); @@ -1282,6 +1282,10 @@ public class AppOpsService extends IAppOpsService.Stub { return mInProgressEvents != null; } + public boolean isPaused() { + return mPausedInProgressEvents != null; + } + boolean hasAnyTime() { return (mAccessEvents != null && mAccessEvents.size() > 0) || (mRejectEvents != null && mRejectEvents.size() > 0); @@ -3953,7 +3957,7 @@ public class AppOpsService extends IAppOpsService.Stub { return; } - if (attributedOp.isRunning()) { + if (attributedOp.isRunning() || attributedOp.isPaused()) { attributedOp.finished(clientId); } else { Slog.e(TAG, "Operation not started: uid=" + uid + " pkg=" + packageName + "(" diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 96bb73f3107c..8961a5a05546 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -1034,6 +1034,11 @@ import java.util.concurrent.atomic.AtomicBoolean; } } + /*package*/ void clearAvrcpAbsoluteVolumeSupported() { + setAvrcpAbsoluteVolumeSupported(false); + mAudioService.setAvrcpAbsoluteVolumeSupported(false); + } + /*package*/ boolean getBluetoothA2dpEnabled() { synchronized (mDeviceStateLock) { return mBluetoothA2dpEnabled; diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 18d04e94b36f..5944a63bd5e6 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -1015,7 +1015,7 @@ public class AudioDeviceInventory { } // device to remove was visible by APM, update APM - mDeviceBroker.setAvrcpAbsoluteVolumeSupported(false); + mDeviceBroker.clearAvrcpAbsoluteVolumeSupported(); final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index a2722cbf286e..a3c590409d44 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -7647,8 +7647,12 @@ public class AudioService extends IAudioService.Stub // address is not used for now, but may be used when multiple a2dp devices are supported sVolumeLogger.log(new AudioEventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr=" + address + " support=" + support)); - mAvrcpAbsVolSupported = support; mDeviceBroker.setAvrcpAbsoluteVolumeSupported(support); + setAvrcpAbsoluteVolumeSupported(support); + } + + /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean support) { + mAvrcpAbsVolSupported = support; sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, mStreamStates[AudioSystem.STREAM_MUSIC], 0); diff --git a/services/core/java/com/android/server/audio/FadeOutManager.java b/services/core/java/com/android/server/audio/FadeOutManager.java index e08bd673948f..bb627e5a21fb 100644 --- a/services/core/java/com/android/server/audio/FadeOutManager.java +++ b/services/core/java/com/android/server/audio/FadeOutManager.java @@ -18,6 +18,7 @@ package com.android.server.audio; import android.annotation.NonNull; import android.media.AudioAttributes; +import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.VolumeShaper; import android.util.Log; @@ -35,15 +36,15 @@ public final class FadeOutManager { public static final String TAG = "AudioService.FadeOutManager"; - /*package*/ static final long FADE_OUT_DURATION_MS = 2500; + /*package*/ static final long FADE_OUT_DURATION_MS = 2000; private static final boolean DEBUG = PlaybackActivityMonitor.DEBUG; private static final VolumeShaper.Configuration FADEOUT_VSHAPE = new VolumeShaper.Configuration.Builder() .setId(PlaybackActivityMonitor.VOLUME_SHAPER_SYSTEM_FADEOUT_ID) - .setCurve(new float[]{0.f, 1.0f} /* times */, - new float[]{1.f, 0.0f} /* volumes */) + .setCurve(new float[]{0.f, 0.25f, 1.0f} /* times */, + new float[]{1.f, 0.65f, 0.0f} /* volumes */) .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME) .setDuration(FADE_OUT_DURATION_MS) .build(); @@ -70,6 +71,30 @@ public final class FadeOutManager { private static final VolumeShaper.Operation PLAY_SKIP_RAMP = new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build(); + + // TODO explore whether a shorter fade out would be a better UX instead of not fading out at all + // (legacy behavior) + /** + * Determine whether the focus request would trigger a fade out, given the parameters of the + * requester and those of the focus loser + * @param requester the parameters for the focus request + * @return true if there can be a fade out over the requester starting to play + */ + static boolean canCauseFadeOut(@NonNull FocusRequester requester, + @NonNull FocusRequester loser) { + if (requester.getAudioAttributes().getContentType() == AudioAttributes.CONTENT_TYPE_SPEECH) + { + if (DEBUG) { Log.i(TAG, "not fading out: new focus is for speech"); } + return false; + } + if ((loser.getGrantFlags() & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0) { + if (DEBUG) { Log.i(TAG, "not fading out: loser has PAUSES_ON_DUCKABLE_LOSS"); } + return false; + } + + return true; + } + /** * Evaluates whether the player associated with this configuration can and should be faded out * @param apc the configuration of the player diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java index cc60fe1200b1..ab8b795c52ca 100644 --- a/services/core/java/com/android/server/audio/FocusRequester.java +++ b/services/core/java/com/android/server/audio/FocusRequester.java @@ -182,7 +182,7 @@ public class FocusRequester { return mGrantFlags; } - AudioAttributes getAudioAttributes() { + @NonNull AudioAttributes getAudioAttributes() { return mAttributes; } diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index 03102155952d..e6c4abfa2086 100644 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java @@ -888,6 +888,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer { mEventLogger.log((new AudioEventLogger.StringEvent( "requestAudioFocus() from uid/pid " + uid + "/" + Binder.getCallingPid() + + " AA=" + aa.usageToString() + "/" + aa.contentTypeToString() + " clientId=" + clientId + " callingPack=" + callingPackageName + " req=" + focusChangeHint + " flags=0x" + Integer.toHexString(flags) diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index e71219fd3d26..af9a14ec80cd 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -707,6 +707,9 @@ public final class PlaybackActivityMonitor if (DEBUG) { Log.v(TAG, "no players to fade out"); } return false; } + if (!FadeOutManager.canCauseFadeOut(winner, loser)) { + return false; + } // check if this UID needs to be faded out (return false if not), and gather list of // eligible players to fade out final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator(); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index dd3057e721be..6a7d201d9f25 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -149,7 +149,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { prop.supportsDetectInteraction, prop.halControlsPreview, false /* resetLockoutRequiresChallenge */); final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler, - internalProp); + internalProp, lockoutResetDispatcher); mSensors.put(sensorId, sensor); Slog.d(getTag(), "Added: " + internalProp); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java index f551930aee49..1e1b532961df 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java @@ -80,11 +80,26 @@ public class FaceResetLockoutClient extends HalClientMonitor<ISession> implement } void onLockoutCleared() { - mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_NONE); - mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId()); + resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache, + mLockoutResetDispatcher); mCallback.onClientFinished(this, true /* success */); } + /** + * Reset the local lockout state and notify any listeners. + * + * This should only be called when the HAL sends a reset request directly to the + * framework (i.e. time based reset, etc.). When the HAL is responding to a + * resetLockout request from an instance of this client {@link #onLockoutCleared()} should + * be used instead. + */ + static void resetLocalLockoutStateToNone(int sensorId, int userId, + @NonNull LockoutCache lockoutTracker, + @NonNull LockoutResetDispatcher lockoutResetDispatcher) { + lockoutTracker.setLockoutModeForUser(userId, LockoutTracker.LOCKOUT_NONE); + lockoutResetDispatcher.notifyLockoutResetCallbacks(sensorId); + } + @Override public int getProtoEnum() { return BiometricsProto.CM_RESET_LOCKOUT; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java index 724531ebcf42..0e6a0f72206c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java @@ -57,6 +57,7 @@ import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.Interruptable; import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutConsumer; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.RemovalConsumer; import com.android.server.biometrics.sensors.StartUserClient; import com.android.server.biometrics.sensors.StopUserClient; @@ -124,10 +125,16 @@ public class Sensor { private final int mSensorId; private final int mUserId; @NonNull + private final LockoutCache mLockoutCache; + @NonNull + private final LockoutResetDispatcher mLockoutResetDispatcher; + @NonNull private final Callback mCallback; HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag, @NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId, + @NonNull LockoutCache lockoutTracker, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull Callback callback) { mContext = context; mHandler = handler; @@ -135,6 +142,8 @@ public class Sensor { mScheduler = scheduler; mSensorId = sensorId; mUserId = userId; + mLockoutCache = lockoutTracker; + mLockoutResetDispatcher = lockoutResetDispatcher; mCallback = callback; } @@ -327,13 +336,15 @@ public class Sensor { mHandler.post(() -> { final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FaceResetLockoutClient)) { - Slog.e(mTag, "onLockoutCleared for non-resetLockout client: " - + Utils.getClientName(client)); - return; + Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL"); + FaceResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId, + mLockoutCache, mLockoutResetDispatcher); + } else { + Slog.d(mTag, "onLockoutCleared after resetLockout"); + final FaceResetLockoutClient resetLockoutClient = + (FaceResetLockoutClient) client; + resetLockoutClient.onLockoutCleared(); } - - final FaceResetLockoutClient resetLockoutClient = (FaceResetLockoutClient) client; - resetLockoutClient.onLockoutCleared(); }); } @@ -465,7 +476,8 @@ public class Sensor { } Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context, - @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties) { + @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties, + @NonNull LockoutResetDispatcher lockoutResetDispatcher) { mTag = tag; mProvider = provider; mContext = context; @@ -493,7 +505,8 @@ public class Sensor { final int sensorId = mSensorProperties.sensorId; final HalSessionCallback resultController = new HalSessionCallback(mContext, - mHandler, mTag, mScheduler, sensorId, newUserId, callback); + mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache, + lockoutResetDispatcher, callback); final StartUserClient.UserStartedCallback<ISession> userStartedCallback = (userIdStarted, newSession) -> { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 20d6ee24d327..e5fafcd1bd3b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -159,7 +159,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi prop.sensorLocations[0].sensorLocationY, prop.sensorLocations[0].sensorRadius); final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler, - internalProp, gestureAvailabilityDispatcher); + internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher); mSensors.put(sensorId, sensor); Slog.d(getTag(), "Added: " + internalProp); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java index bab950691b08..878ef46d2b2e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java @@ -80,11 +80,26 @@ class FingerprintResetLockoutClient extends HalClientMonitor<ISession> implement } void onLockoutCleared() { - mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_NONE); - mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId()); + resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache, + mLockoutResetDispatcher); mCallback.onClientFinished(this, true /* success */); } + /** + * Reset the local lockout state and notify any listeners. + * + * This should only be called when the HAL sends a reset request directly to the + * framework (i.e. time based reset, etc.). When the HAL is responding to a + * resetLockout request from an instance of this client {@link #onLockoutCleared()} should + * be used instead. + */ + static void resetLocalLockoutStateToNone(int sensorId, int userId, + @NonNull LockoutCache lockoutTracker, + @NonNull LockoutResetDispatcher lockoutResetDispatcher) { + lockoutTracker.setLockoutModeForUser(userId, LockoutTracker.LOCKOUT_NONE); + lockoutResetDispatcher.notifyLockoutResetCallbacks(sensorId); + } + @Override public int getProtoEnum() { return BiometricsProto.CM_RESET_LOCKOUT; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java index cf915ad78509..10137b5a28c9 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java @@ -54,6 +54,7 @@ import com.android.server.biometrics.sensors.ErrorConsumer; import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutConsumer; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.RemovalConsumer; import com.android.server.biometrics.sensors.StartUserClient; import com.android.server.biometrics.sensors.StopUserClient; @@ -116,16 +117,27 @@ class Sensor { void onHardwareUnavailable(); } - @NonNull private final Context mContext; - @NonNull private final Handler mHandler; - @NonNull private final String mTag; - @NonNull private final UserAwareBiometricScheduler mScheduler; + @NonNull + private final Context mContext; + @NonNull + private final Handler mHandler; + @NonNull + private final String mTag; + @NonNull + private final UserAwareBiometricScheduler mScheduler; private final int mSensorId; private final int mUserId; - @NonNull private final Callback mCallback; + @NonNull + private final LockoutCache mLockoutCache; + @NonNull + private final LockoutResetDispatcher mLockoutResetDispatcher; + @NonNull + private final Callback mCallback; HalSessionCallback(@NonNull Context context, @NonNull Handler handler, @NonNull String tag, @NonNull UserAwareBiometricScheduler scheduler, int sensorId, int userId, + @NonNull LockoutCache lockoutTracker, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull Callback callback) { mContext = context; mHandler = handler; @@ -133,6 +145,8 @@ class Sensor { mScheduler = scheduler; mSensorId = sensorId; mUserId = userId; + mLockoutCache = lockoutTracker; + mLockoutResetDispatcher = lockoutResetDispatcher; mCallback = callback; } @@ -303,14 +317,15 @@ class Sensor { mHandler.post(() -> { final BaseClientMonitor client = mScheduler.getCurrentClient(); if (!(client instanceof FingerprintResetLockoutClient)) { - Slog.e(mTag, "onLockoutCleared for non-resetLockout client: " - + Utils.getClientName(client)); - return; + Slog.d(mTag, "onLockoutCleared outside of resetLockout by HAL"); + FingerprintResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId, + mLockoutCache, mLockoutResetDispatcher); + } else { + Slog.d(mTag, "onLockoutCleared after resetLockout"); + final FingerprintResetLockoutClient resetLockoutClient = + (FingerprintResetLockoutClient) client; + resetLockoutClient.onLockoutCleared(); } - - final FingerprintResetLockoutClient resetLockoutClient = - (FingerprintResetLockoutClient) client; - resetLockoutClient.onLockoutCleared(); }); } @@ -415,6 +430,7 @@ class Sensor { Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context, @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { mTag = tag; mProvider = provider; @@ -422,6 +438,7 @@ class Sensor { mToken = new Binder(); mHandler = handler; mSensorProperties = sensorProperties; + mLockoutCache = new LockoutCache(); mScheduler = new UserAwareBiometricScheduler(tag, gestureAvailabilityDispatcher, () -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL, new UserAwareBiometricScheduler.UserSwitchCallback() { @@ -443,7 +460,8 @@ class Sensor { final int sensorId = mSensorProperties.sensorId; final HalSessionCallback resultController = new HalSessionCallback(mContext, - mHandler, mTag, mScheduler, sensorId, newUserId, callback); + mHandler, mTag, mScheduler, sensorId, newUserId, mLockoutCache, + lockoutResetDispatcher, callback); final StartUserClient.UserStartedCallback<ISession> userStartedCallback = (userIdStarted, newSession) -> { @@ -466,7 +484,6 @@ class Sensor { resultController, userStartedCallback); } }); - mLockoutCache = new LockoutCache(); mAuthenticatorIds = new HashMap<>(); mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null; } @@ -529,6 +546,9 @@ class Sensor { proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId); proto.write(SensorStateProto.MODALITY, SensorStateProto.FINGERPRINT); + if (mSensorProperties.isAnyUdfpsType()) { + proto.write(SensorStateProto.MODALITY_FLAGS, SensorStateProto.FINGERPRINT_UDFPS); + } proto.write(SensorStateProto.CURRENT_STRENGTH, Utils.getCurrentStrength(mSensorProperties.sensorId)); proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer)); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 274652096624..33532643b719 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -760,6 +760,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId); proto.write(SensorStateProto.MODALITY, SensorStateProto.FINGERPRINT); + if (mSensorProperties.isAnyUdfpsType()) { + proto.write(SensorStateProto.MODALITY_FLAGS, SensorStateProto.FINGERPRINT_UDFPS); + } proto.write(SensorStateProto.CURRENT_STRENGTH, Utils.getCurrentStrength(mSensorProperties.sensorId)); proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer)); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index f75f3e1f2f79..d4920f56a27e 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1498,8 +1498,8 @@ public final class DisplayManagerService extends SystemService { } private void setDisplayPropertiesInternal(int displayId, boolean hasContent, - float requestedRefreshRate, int requestedModeId, boolean preferMinimalPostProcessing, - boolean inTraversal) { + float requestedRefreshRate, int requestedModeId, float requestedMaxRefreshRate, + boolean preferMinimalPostProcessing, boolean inTraversal) { synchronized (mSyncRoot) { final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId); if (display == null) { @@ -1523,8 +1523,8 @@ public final class DisplayManagerService extends SystemService { requestedModeId = display.getDisplayInfoLocked().findDefaultModeByRefreshRate( requestedRefreshRate).getModeId(); } - mDisplayModeDirector.getAppRequestObserver().setAppRequestedMode( - displayId, requestedModeId); + mDisplayModeDirector.getAppRequestObserver().setAppRequest( + displayId, requestedModeId, requestedMaxRefreshRate); if (display.getDisplayInfoLocked().minimalPostProcessingSupported) { boolean mppRequest = mMinimalPostProcessingAllowed && preferMinimalPostProcessing; @@ -3189,10 +3189,11 @@ public final class DisplayManagerService extends SystemService { @Override public void setDisplayProperties(int displayId, boolean hasContent, - float requestedRefreshRate, int requestedMode, + float requestedRefreshRate, int requestedMode, float requestedMaxRefreshRate, boolean requestedMinimalPostProcessing, boolean inTraversal) { setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate, - requestedMode, requestedMinimalPostProcessing, inTraversal); + requestedMode, requestedMaxRefreshRate, requestedMinimalPostProcessing, + inTraversal); } @Override diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 67779a2e1685..997f0e5aa70c 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -198,6 +198,8 @@ public class DisplayModeDirector { public float maxRefreshRate; public int width; public int height; + public boolean disableRefreshRateSwitching; + public float baseModeRefreshRate; VoteSummary() { reset(); @@ -208,6 +210,8 @@ public class DisplayModeDirector { maxRefreshRate = Float.POSITIVE_INFINITY; width = Vote.INVALID_SIZE; height = Vote.INVALID_SIZE; + disableRefreshRateSwitching = false; + baseModeRefreshRate = 0f; } } @@ -229,13 +233,20 @@ public class DisplayModeDirector { // For refresh rates, just use the tightest bounds of all the votes summary.minRefreshRate = Math.max(summary.minRefreshRate, vote.refreshRateRange.min); summary.maxRefreshRate = Math.min(summary.maxRefreshRate, vote.refreshRateRange.max); - // For display size, use only the first vote we come across (i.e. the highest - // priority vote that includes the width / height). + // For display size, disable refresh rate switching and base mode refresh rate use only + // the first vote we come across (i.e. the highest priority vote that includes the + // attribute). if (summary.height == Vote.INVALID_SIZE && summary.width == Vote.INVALID_SIZE && vote.height > 0 && vote.width > 0) { summary.width = vote.width; summary.height = vote.height; } + if (!summary.disableRefreshRateSwitching && vote.disableRefreshRateSwitching) { + summary.disableRefreshRateSwitching = true; + } + if (summary.baseModeRefreshRate == 0f && vote.baseModeRefreshRate > 0f) { + summary.baseModeRefreshRate = vote.baseModeRefreshRate; + } } } @@ -260,13 +271,14 @@ public class DisplayModeDirector { return new DesiredDisplayModeSpecs(); } - int[] availableModes = new int[]{defaultMode.getModeId()}; + ArrayList<Display.Mode> availableModes = new ArrayList<>(); + availableModes.add(defaultMode); VoteSummary primarySummary = new VoteSummary(); int lowestConsideredPriority = Vote.MIN_PRIORITY; int highestConsideredPriority = Vote.MAX_PRIORITY; if (mAlwaysRespectAppRequest) { - lowestConsideredPriority = Vote.PRIORITY_APP_REQUEST_REFRESH_RATE; + lowestConsideredPriority = Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE; highestConsideredPriority = Vote.PRIORITY_APP_REQUEST_SIZE; } @@ -286,16 +298,19 @@ public class DisplayModeDirector { } availableModes = filterModes(modes, primarySummary); - if (availableModes.length > 0) { + if (!availableModes.isEmpty()) { if (mLoggingEnabled) { - Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes) + Slog.w(TAG, "Found available modes=" + availableModes + " with lowest priority considered " + Vote.priorityToString(lowestConsideredPriority) + " and constraints: " + "width=" + primarySummary.width + ", height=" + primarySummary.height + ", minRefreshRate=" + primarySummary.minRefreshRate - + ", maxRefreshRate=" + primarySummary.maxRefreshRate); + + ", maxRefreshRate=" + primarySummary.maxRefreshRate + + ", disableRefreshRateSwitching=" + + primarySummary.disableRefreshRateSwitching + + ", baseModeRefreshRate=" + primarySummary.baseModeRefreshRate); } break; } @@ -307,7 +322,10 @@ public class DisplayModeDirector { + "width=" + primarySummary.width + ", height=" + primarySummary.height + ", minRefreshRate=" + primarySummary.minRefreshRate - + ", maxRefreshRate=" + primarySummary.maxRefreshRate); + + ", maxRefreshRate=" + primarySummary.maxRefreshRate + + ", disableRefreshRateSwitching=" + + primarySummary.disableRefreshRateSwitching + + ", baseModeRefreshRate=" + primarySummary.baseModeRefreshRate); } // If we haven't found anything with the current set of votes, drop the @@ -332,26 +350,38 @@ public class DisplayModeDirector { appRequestSummary.maxRefreshRate)); } - int baseModeId = INVALID_DISPLAY_MODE_ID; + // Select the base mode id based on the base mode refresh rate, if available, since this + // will be the mode id the app voted for. + Display.Mode baseMode = null; + for (Display.Mode availableMode : availableModes) { + if (primarySummary.baseModeRefreshRate + >= availableMode.getRefreshRate() - FLOAT_TOLERANCE + && primarySummary.baseModeRefreshRate + <= availableMode.getRefreshRate() + FLOAT_TOLERANCE) { + baseMode = availableMode; + } + } // Select the default mode if available. This is important because SurfaceFlinger // can do only seamless switches by default. Some devices (e.g. TV) don't support // seamless switching so the mode we select here won't be changed. - for (int availableMode : availableModes) { - if (availableMode == defaultMode.getModeId()) { - baseModeId = defaultMode.getModeId(); - break; + if (baseMode == null) { + for (Display.Mode availableMode : availableModes) { + if (availableMode.getModeId() == defaultMode.getModeId()) { + baseMode = defaultMode; + break; + } } } // If the application requests a display mode by setting // LayoutParams.preferredDisplayModeId, it will be the only available mode and it'll // be stored as baseModeId. - if (baseModeId == INVALID_DISPLAY_MODE_ID && availableModes.length > 0) { - baseModeId = availableModes[0]; + if (baseMode == null && !availableModes.isEmpty()) { + baseMode = availableModes.get(0); } - if (baseModeId == INVALID_DISPLAY_MODE_ID) { + if (baseMode == null) { Slog.w(TAG, "Can't find a set of allowed modes which satisfies the votes. Falling" + " back to the default mode. Display = " + displayId + ", votes = " + votes + ", supported modes = " + Arrays.toString(modes)); @@ -363,31 +393,19 @@ public class DisplayModeDirector { new RefreshRateRange(fps, fps)); } - if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) { - Display.Mode baseMode = null; - for (Display.Mode mode : modes) { - if (mode.getModeId() == baseModeId) { - baseMode = mode; - break; - } - } - if (baseMode == null) { - // This should never happen. - throw new IllegalStateException( - "The base mode with id " + baseModeId - + " is not in the list of supported modes."); - } + if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE + || primarySummary.disableRefreshRateSwitching) { float fps = baseMode.getRefreshRate(); - return new DesiredDisplayModeSpecs(baseModeId, - /*allowGroupSwitching */ false, - new RefreshRateRange(fps, fps), - new RefreshRateRange(fps, fps)); + primarySummary.minRefreshRate = primarySummary.maxRefreshRate = fps; + if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) { + appRequestSummary.minRefreshRate = appRequestSummary.maxRefreshRate = fps; + } } boolean allowGroupSwitching = mModeSwitchingType == DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS; - return new DesiredDisplayModeSpecs(baseModeId, + return new DesiredDisplayModeSpecs(baseMode.getModeId(), allowGroupSwitching, new RefreshRateRange( primarySummary.minRefreshRate, primarySummary.maxRefreshRate), @@ -396,8 +414,10 @@ public class DisplayModeDirector { } } - private int[] filterModes(Display.Mode[] supportedModes, VoteSummary summary) { + private ArrayList<Display.Mode> filterModes(Display.Mode[] supportedModes, + VoteSummary summary) { ArrayList<Display.Mode> availableModes = new ArrayList<>(); + boolean missingBaseModeRefreshRate = summary.baseModeRefreshRate > 0f; for (Display.Mode mode : supportedModes) { if (mode.getPhysicalWidth() != summary.width || mode.getPhysicalHeight() != summary.height) { @@ -426,13 +446,16 @@ public class DisplayModeDirector { continue; } availableModes.add(mode); + if (mode.getRefreshRate() >= summary.baseModeRefreshRate - FLOAT_TOLERANCE + && mode.getRefreshRate() <= summary.baseModeRefreshRate + FLOAT_TOLERANCE) { + missingBaseModeRefreshRate = false; + } } - final int size = availableModes.size(); - int[] availableModeIds = new int[size]; - for (int i = 0; i < size; i++) { - availableModeIds[i] = availableModes.get(i).getModeId(); + if (missingBaseModeRefreshRate) { + return new ArrayList<>(); } - return availableModeIds; + + return availableModes; } /** @@ -912,37 +935,52 @@ public class DisplayModeDirector { // by all other considerations. It acts to set a default frame rate for a device. public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0; - // FLICKER votes for a single refresh rate like [60,60], [90,90] or null. - // If the higher voters result is a range, it will fix the rate to a single choice. - // It's used to avoid refresh rate switches in certain conditions which may result in the - // user seeing the display flickering when the switches occur. - public static final int PRIORITY_FLICKER = 1; + // PRIORITY_FLICKER_REFRESH_RATE votes for a single refresh rate like [60,60], [90,90] or + // null. It is used to set a preferred refresh rate value in case the higher priority votes + // result is a range. + public static final int PRIORITY_FLICKER_REFRESH_RATE = 1; // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate. // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY] public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2; + // APP_REQUEST_MAX_REFRESH_RATE is used to for internal apps to limit the refresh + // rate in certain cases, mostly to preserve power. + // It votes to [0, APP_REQUEST_MAX_REFRESH_RATE]. + public static final int PRIORITY_APP_REQUEST_MAX_REFRESH_RATE = 3; + // We split the app request into different priorities in case we can satisfy one desire // without the other. // Application can specify preferred refresh rate with below attrs. // @see android.view.WindowManager.LayoutParams#preferredRefreshRate // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId - // System also forces some apps like denylisted app to run at a lower refresh rate. + // These translates into votes for the base mode refresh rate and resolution to be + // used by SurfaceFlinger as the policy of choosing the display mode. The system also + // forces some apps like denylisted app to run at a lower refresh rate. // @see android.R.array#config_highRefreshRateBlacklist - public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3; - public static final int PRIORITY_APP_REQUEST_SIZE = 4; + // The preferred refresh rate is set on the main surface of the app outside of + // DisplayModeDirector. + // @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded + public static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 4; + public static final int PRIORITY_APP_REQUEST_SIZE = 5; // SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest // of low priority voters. It votes [0, max(PEAK, MIN)] - public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 5; + public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 6; // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on. - public static final int PRIORITY_LOW_POWER_MODE = 6; + public static final int PRIORITY_LOW_POWER_MODE = 7; + + // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the + // higher priority voters' result is a range, it will fix the rate to a single choice. + // It's used to avoid refresh rate switches in certain conditions which may result in the + // user seeing the display flickering when the switches occur. + public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8; // 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 = 7; + public static final int PRIORITY_UDFPS = 9; // 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. @@ -953,7 +991,7 @@ public class DisplayModeDirector { // The cutoff for the app request refresh rate range. Votes with priorities lower than this // value will not be considered when constructing the app request refresh rate range. public static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF = - PRIORITY_APP_REQUEST_REFRESH_RATE; + PRIORITY_APP_REQUEST_MAX_REFRESH_RATE; /** * A value signifying an invalid width or height in a vote. @@ -973,32 +1011,64 @@ public class DisplayModeDirector { */ public final RefreshRateRange refreshRateRange; + /** + * Whether refresh rate switching should be disabled (i.e. the refresh rate range is + * a single value). + */ + public final boolean disableRefreshRateSwitching; + + /** + * The base mode refresh rate to be used for this display. This would be used when deciding + * the base mode id. + */ + public final float baseModeRefreshRate; + public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) { - return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate); + return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate, + minRefreshRate == maxRefreshRate, 0f); } public static Vote forSize(int width, int height) { - return new Vote(width, height, 0f, Float.POSITIVE_INFINITY); + return new Vote(width, height, 0f, Float.POSITIVE_INFINITY, false, + 0f); + } + + public static Vote forDisableRefreshRateSwitching() { + return new Vote(INVALID_SIZE, INVALID_SIZE, 0f, Float.POSITIVE_INFINITY, true, + 0f); + } + + public static Vote forBaseModeRefreshRate(float baseModeRefreshRate) { + return new Vote(INVALID_SIZE, INVALID_SIZE, 0f, Float.POSITIVE_INFINITY, false, + baseModeRefreshRate); } private Vote(int width, int height, - float minRefreshRate, float maxRefreshRate) { + float minRefreshRate, float maxRefreshRate, + boolean disableRefreshRateSwitching, + float baseModeRefreshRate) { this.width = width; this.height = height; this.refreshRateRange = new RefreshRateRange(minRefreshRate, maxRefreshRate); + this.disableRefreshRateSwitching = disableRefreshRateSwitching; + this.baseModeRefreshRate = baseModeRefreshRate; } public static String priorityToString(int priority) { switch (priority) { case PRIORITY_DEFAULT_REFRESH_RATE: return "PRIORITY_DEFAULT_REFRESH_RATE"; - case PRIORITY_FLICKER: - return "PRIORITY_FLICKER"; + 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_REFRESH_RATE: - return "PRIORITY_APP_REQUEST_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: @@ -1018,7 +1088,9 @@ public class DisplayModeDirector { return "Vote{" + "width=" + width + ", height=" + height + ", minRefreshRate=" + refreshRateRange.min - + ", maxRefreshRate=" + refreshRateRange.max + "}"; + + ", maxRefreshRate=" + refreshRateRange.max + + ", disableRefreshRateSwitching=" + disableRefreshRateSwitching + + ", baseModeRefreshRate=" + baseModeRefreshRate + "}"; } } @@ -1182,14 +1254,17 @@ public class DisplayModeDirector { final class AppRequestObserver { private final SparseArray<Display.Mode> mAppRequestedModeByDisplay; + private final SparseArray<Float> mAppPreferredMaxRefreshRateByDisplay; AppRequestObserver() { mAppRequestedModeByDisplay = new SparseArray<>(); + mAppPreferredMaxRefreshRateByDisplay = new SparseArray<>(); } - public void setAppRequestedMode(int displayId, int modeId) { + public void setAppRequest(int displayId, int modeId, float requestedMaxRefreshRate) { synchronized (mLock) { setAppRequestedModeLocked(displayId, modeId); + setAppPreferredMaxRefreshRateLocked(displayId, requestedMaxRefreshRate); } } @@ -1199,24 +1274,48 @@ public class DisplayModeDirector { return; } - final Vote refreshRateVote; + final Vote baseModeRefreshRateVote; final Vote sizeVote; if (requestedMode != null) { mAppRequestedModeByDisplay.put(displayId, requestedMode); - float refreshRate = requestedMode.getRefreshRate(); - refreshRateVote = Vote.forRefreshRates(refreshRate, refreshRate); + baseModeRefreshRateVote = + Vote.forBaseModeRefreshRate(requestedMode.getRefreshRate()); sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(), requestedMode.getPhysicalHeight()); } else { mAppRequestedModeByDisplay.remove(displayId); - refreshRateVote = null; + baseModeRefreshRateVote = null; sizeVote = null; } - updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, refreshRateVote); + updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + baseModeRefreshRateVote); updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote); } + private void setAppPreferredMaxRefreshRateLocked(int displayId, + float requestedMaxRefreshRate) { + final Vote vote; + final Float requestedMaxRefreshRateVote = + requestedMaxRefreshRate > 0 + ? new Float(requestedMaxRefreshRate) : null; + if (Objects.equals(requestedMaxRefreshRateVote, + mAppPreferredMaxRefreshRateByDisplay.get(displayId))) { + return; + } + + if (requestedMaxRefreshRate > 0) { + mAppPreferredMaxRefreshRateByDisplay.put(displayId, requestedMaxRefreshRateVote); + vote = Vote.forRefreshRates(0, requestedMaxRefreshRate); + } else { + mAppPreferredMaxRefreshRateByDisplay.remove(displayId); + vote = null; + } + synchronized (mLock) { + updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, vote); + } + } + private Display.Mode findModeByIdLocked(int displayId, int modeId) { Display.Mode[] modes = mSupportedModesByDisplay.get(displayId); if (modes == null) { @@ -1238,6 +1337,12 @@ public class DisplayModeDirector { final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i); pw.println(" " + id + " -> " + mode); } + pw.println(" mAppPreferredMaxRefreshRateByDisplay:"); + for (int i = 0; i < mAppPreferredMaxRefreshRateByDisplay.size(); i++) { + final int id = mAppPreferredMaxRefreshRateByDisplay.keyAt(i); + final Float refreshRate = mAppPreferredMaxRefreshRateByDisplay.valueAt(i); + pw.println(" " + id + " -> " + refreshRate); + } } } @@ -1486,7 +1591,8 @@ public class DisplayModeDirector { updateSensorStatus(); if (!changeable) { // Revoke previous vote from BrightnessObserver - updateVoteLocked(Vote.PRIORITY_FLICKER, null); + updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE, null); + updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, null); } } } @@ -1734,7 +1840,8 @@ public class DisplayModeDirector { return false; } private void onBrightnessChangedLocked() { - Vote vote = null; + Vote refreshRateVote = null; + Vote refreshRateSwitchingVote = null; if (mBrightness < 0) { // Either the setting isn't available or we shouldn't be observing yet anyways. @@ -1744,20 +1851,25 @@ public class DisplayModeDirector { boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux); if (insideLowZone) { - vote = Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone); + refreshRateVote = + Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone); + refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching(); } boolean insideHighZone = hasValidHighZone() && isInsideHighZone(mBrightness, mAmbientLux); if (insideHighZone) { - vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone); + refreshRateVote = + Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone); + refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching(); } if (mLoggingEnabled) { Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " + mAmbientLux - + ", Vote " + vote); + + ", Vote " + refreshRateVote); } - updateVoteLocked(Vote.PRIORITY_FLICKER, vote); + updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE, refreshRateVote); + updateVoteLocked(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, refreshRateSwitchingVote); } private boolean hasValidLowZone() { diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java index 609bd8b0fcfd..dc3dc468f763 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -21,19 +21,18 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.graphics.Typeface; -import android.graphics.fonts.Font; import android.graphics.fonts.FontFamily; -import android.graphics.fonts.FontFileUtil; import android.graphics.fonts.FontManager; import android.graphics.fonts.FontUpdateRequest; import android.graphics.fonts.SystemFonts; +import android.os.ParcelFileDescriptor; import android.os.ResultReceiver; import android.os.SharedMemory; import android.os.ShellCallback; import android.system.ErrnoException; import android.text.FontConfig; -import android.text.TextUtils; import android.util.AndroidException; +import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.Slog; @@ -47,19 +46,17 @@ import com.android.server.SystemService; import java.io.File; import java.io.FileDescriptor; -import java.io.FileInputStream; import java.io.IOException; import java.io.PrintWriter; import java.nio.ByteBuffer; +import java.nio.DirectByteBuffer; import java.nio.NioUtils; -import java.nio.channels.FileChannel; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; /** A service for managing system fonts. */ -// TODO(b/173619554): Add API to update fonts. public final class FontManagerService extends IFontManager.Stub { private static final String TAG = "FontManagerService"; @@ -73,23 +70,6 @@ public final class FontManagerService extends IFontManager.Stub { } @Override - public int updateFontFile(@NonNull FontUpdateRequest request, int baseVersion) { - Preconditions.checkArgumentNonnegative(baseVersion); - Objects.requireNonNull(request); - Objects.requireNonNull(request.getFd()); - Objects.requireNonNull(request.getSignature()); - getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS, - "UPDATE_FONTS permission required."); - try { - update(baseVersion, Collections.singletonList(request)); - return FontManager.RESULT_SUCCESS; - } catch (SystemFontException e) { - Slog.e(TAG, "Failed to update font file", e); - return e.getErrorCode(); - } - } - - @Override public int updateFontFamily(@NonNull List<FontUpdateRequest> requests, int baseVersion) { Preconditions.checkArgumentNonnegative(baseVersion); Objects.requireNonNull(requests); @@ -101,6 +81,17 @@ public final class FontManagerService extends IFontManager.Stub { } catch (SystemFontException e) { Slog.e(TAG, "Failed to update font family", e); return e.getErrorCode(); + } finally { + for (FontUpdateRequest request : requests) { + ParcelFileDescriptor fd = request.getFd(); + if (fd != null) { + try { + fd.close(); + } catch (IOException e) { + Slog.w(TAG, "Failed to close fd", e); + } + } + } } } @@ -149,67 +140,6 @@ public final class FontManagerService extends IFontManager.Stub { } } - /* package */ static class OtfFontFileParser implements UpdatableFontDir.FontFileParser { - @Override - public String getPostScriptName(File file) throws IOException { - ByteBuffer buffer = mmap(file); - try { - return FontFileUtil.getPostScriptName(buffer, 0); - } finally { - NioUtils.freeDirectBuffer(buffer); - } - } - - @Override - public String buildFontFileName(File file) throws IOException { - ByteBuffer buffer = mmap(file); - try { - String psName = FontFileUtil.getPostScriptName(buffer, 0); - int isType1Font = FontFileUtil.isPostScriptType1Font(buffer, 0); - int isCollection = FontFileUtil.isCollectionFont(buffer); - - if (TextUtils.isEmpty(psName) || isType1Font == -1 || isCollection == -1) { - return null; - } - - String extension; - if (isCollection == 1) { - extension = isType1Font == 1 ? ".otc" : ".ttc"; - } else { - extension = isType1Font == 1 ? ".otf" : ".ttf"; - } - return psName + extension; - } finally { - NioUtils.freeDirectBuffer(buffer); - } - - } - - @Override - public long getRevision(File file) throws IOException { - ByteBuffer buffer = mmap(file); - try { - return FontFileUtil.getRevision(buffer, 0); - } finally { - NioUtils.freeDirectBuffer(buffer); - } - } - - @Override - public void tryToCreateTypeface(File file) throws IOException { - Font font = new Font.Builder(file).build(); - FontFamily family = new FontFamily.Builder(font).build(); - new Typeface.CustomFallbackBuilder(family).build(); - } - - private static ByteBuffer mmap(File file) throws IOException { - try (FileInputStream in = new FileInputStream(file)) { - FileChannel fileChannel = in.getChannel(); - return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); - } - } - } - private static class FsverityUtilImpl implements UpdatableFontDir.FsverityUtil { @Override public boolean hasFsverity(String filePath) { @@ -262,13 +192,7 @@ public final class FontManagerService extends IFontManager.Stub { private void initialize() { synchronized (mUpdatableFontDirLock) { if (mUpdatableFontDir == null) { - synchronized (mSerializedFontMapLock) { - try { - mSerializedFontMap = Typeface.serializeFontMap(Typeface.getSystemFontMap()); - } catch (IOException | ErrnoException e) { - mSerializedFontMap = null; - } - } + setSerializedFontMap(serializeSystemServerFontMap()); return; } mUpdatableFontDir.loadFontFileMap(); @@ -364,36 +288,57 @@ public final class FontManagerService extends IFontManager.Stub { /** * Makes new serialized font map data and updates mSerializedFontMap. */ - public void updateSerializedFontMap() { + private void updateSerializedFontMap() { + SharedMemory serializedFontMap = serializeFontMap(getSystemFontConfig()); + if (serializedFontMap == null) { + // Fallback to the preloaded config. + serializedFontMap = serializeSystemServerFontMap(); + } + setSerializedFontMap(serializedFontMap); + } + + @Nullable + private static SharedMemory serializeFontMap(FontConfig fontConfig) { + final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>(); try { - final FontConfig fontConfig = getSystemFontConfig(); - final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig); + final Map<String, FontFamily[]> fallback = + SystemFonts.buildSystemFallback(fontConfig, bufferCache); final Map<String, Typeface> typefaceMap = SystemFonts.buildSystemTypefaces(fontConfig, fallback); - - SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap); - synchronized (mSerializedFontMapLock) { - mSerializedFontMap = serializeFontMap; - } - return; + return Typeface.serializeFontMap(typefaceMap); } catch (IOException | ErrnoException e) { Slog.w(TAG, "Failed to serialize updatable font map. " + "Retrying with system image fonts.", e); + return null; + } finally { + // Unmap buffers promptly, as we map a lot of files and may hit mmap limit before + // GC collects ByteBuffers and unmaps them. + for (ByteBuffer buffer : bufferCache.values()) { + if (buffer instanceof DirectByteBuffer) { + NioUtils.freeDirectBuffer(buffer); + } + } } + } + @Nullable + private static SharedMemory serializeSystemServerFontMap() { try { - final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig(); - final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig); - final Map<String, Typeface> typefaceMap = - SystemFonts.buildSystemTypefaces(fontConfig, fallback); - - SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap); - synchronized (mSerializedFontMapLock) { - mSerializedFontMap = serializeFontMap; - } + return Typeface.serializeFontMap(Typeface.getSystemFontMap()); } catch (IOException | ErrnoException e) { Slog.e(TAG, "Failed to serialize SystemServer system font map", e); + return null; } } + private void setSerializedFontMap(SharedMemory serializedFontMap) { + SharedMemory oldFontMap = null; + synchronized (mSerializedFontMapLock) { + oldFontMap = mSerializedFontMap; + mSerializedFontMap = serializedFontMap; + } + if (oldFontMap != null) { + oldFontMap.close(); + } + } } diff --git a/services/core/java/com/android/server/graphics/fonts/OtfFontFileParser.java b/services/core/java/com/android/server/graphics/fonts/OtfFontFileParser.java new file mode 100644 index 000000000000..1ed3972ed309 --- /dev/null +++ b/services/core/java/com/android/server/graphics/fonts/OtfFontFileParser.java @@ -0,0 +1,130 @@ +/* + * 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.graphics.fonts; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Typeface; +import android.graphics.fonts.Font; +import android.graphics.fonts.FontFamily; +import android.graphics.fonts.FontFileUtil; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.DirectByteBuffer; +import java.nio.NioUtils; +import java.nio.channels.FileChannel; + +/* package */ class OtfFontFileParser implements UpdatableFontDir.FontFileParser { + @Override + public String getPostScriptName(File file) throws IOException { + ByteBuffer buffer = mmap(file); + try { + return FontFileUtil.getPostScriptName(buffer, 0); + } finally { + unmap(buffer); + } + } + + @Override + public String buildFontFileName(File file) throws IOException { + ByteBuffer buffer = mmap(file); + try { + String psName = FontFileUtil.getPostScriptName(buffer, 0); + int isType1Font = FontFileUtil.isPostScriptType1Font(buffer, 0); + int isCollection = FontFileUtil.isCollectionFont(buffer); + + if (TextUtils.isEmpty(psName) || isType1Font == -1 || isCollection == -1) { + return null; + } + + String extension; + if (isCollection == 1) { + extension = isType1Font == 1 ? ".otc" : ".ttc"; + } else { + extension = isType1Font == 1 ? ".otf" : ".ttf"; + } + return psName + extension; + } finally { + unmap(buffer); + } + + } + + @Override + public long getRevision(File file) throws IOException { + ByteBuffer buffer = mmap(file); + try { + return FontFileUtil.getRevision(buffer, 0); + } finally { + unmap(buffer); + } + } + + @Override + public void tryToCreateTypeface(File file) throws Throwable { + ByteBuffer buffer = mmap(file); + try { + Font font = new Font.Builder(buffer).build(); + FontFamily family = new FontFamily.Builder(font).build(); + Typeface typeface = new Typeface.CustomFallbackBuilder(family).build(); + + TextPaint p = new TextPaint(); + p.setTextSize(24f); + p.setTypeface(typeface); + + // Test string to try with the passed font. + // TODO: Good to extract from font file. + String testTextToDraw = "abcXYZ@- " + + "\uD83E\uDED6" // Emoji E13.0 + + "\uD83C\uDDFA\uD83C\uDDF8" // Emoji Flags + + "\uD83D\uDC8F\uD83C\uDFFB" // Emoji Skin tone Sequence + // ZWJ Sequence + + "\uD83D\uDC68\uD83C\uDFFC\u200D\u2764\uFE0F\u200D\uD83D\uDC8B\u200D" + + "\uD83D\uDC68\uD83C\uDFFF"; + + int width = (int) Math.ceil(Layout.getDesiredWidth(testTextToDraw, p)); + StaticLayout layout = StaticLayout.Builder.obtain( + testTextToDraw, 0, testTextToDraw.length(), p, width).build(); + Bitmap bmp = Bitmap.createBitmap( + layout.getWidth(), layout.getHeight(), Bitmap.Config.ALPHA_8); + Canvas canvas = new Canvas(bmp); + layout.draw(canvas); + } finally { + unmap(buffer); + } + } + + private static ByteBuffer mmap(File file) throws IOException { + try (FileInputStream in = new FileInputStream(file)) { + FileChannel fileChannel = in.getChannel(); + return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); + } + } + + private static void unmap(ByteBuffer buffer) { + if (buffer instanceof DirectByteBuffer) { + NioUtils.freeDirectBuffer(buffer); + } + } +} diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java index e74dac5b1d75..981cc8387af9 100644 --- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java +++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java @@ -69,7 +69,7 @@ final class UpdatableFontDir { long getRevision(File file) throws IOException; - void tryToCreateTypeface(File file) throws IOException; + void tryToCreateTypeface(File file) throws Throwable; } /** Interface to mock fs-verity in tests. */ @@ -378,10 +378,10 @@ final class UpdatableFontDir { // Try to create Typeface and treat as failure something goes wrong. try { mParser.tryToCreateTypeface(fontFileInfo.getFile()); - } catch (IOException e) { + } catch (Throwable t) { throw new SystemFontException( FontManager.RESULT_ERROR_INVALID_FONT_FILE, - "Failed to create Typeface from file", e); + "Failed to create Typeface from file", t); } FontConfig fontConfig = getSystemFontConfig(); diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 1c27c6539b17..61107b2efc93 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -313,6 +313,7 @@ public class InputManagerService extends IInputManager.Stub private static native void nativeSetFocusedDisplay(long ptr, int displayId); private static native boolean nativeTransferTouchFocus(long ptr, IBinder fromChannelToken, IBinder toChannelToken, boolean isDragDrop); + private static native boolean nativeTransferTouch(long ptr, IBinder destChannelToken); private static native void nativeSetPointerSpeed(long ptr, int speed); private static native void nativeSetShowTouches(long ptr, boolean enabled); private static native void nativeSetInteractive(long ptr, boolean interactive); @@ -676,6 +677,19 @@ public class InputManagerService extends IInputManager.Stub } /** + * Transfer the current touch gesture to the provided window. + * + * @param destChannelToken The token of the window or input channel that should receive the + * gesture + * @return True if the transfer succeeded, false if there was no active touch gesture happening + */ + public boolean transferTouch(IBinder destChannelToken) { + // TODO(b/162194035): Replace this with a SPY window + Objects.requireNonNull(destChannelToken, "destChannelToken must not be null."); + return nativeTransferTouch(mPtr, destChannelToken); + } + + /** * Creates an input channel that will receive all input from the input dispatcher. * @param inputChannelName The input channel name. * @param displayId Target display id. diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index c55cf51ed15c..8829fa9c37e3 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -146,7 +146,8 @@ import java.util.concurrent.CopyOnWriteArrayList; * The service class that manages LocationProviders and issues location * updates and alerts. */ -public class LocationManagerService extends ILocationManager.Stub { +public class LocationManagerService extends ILocationManager.Stub implements + LocationProviderManager.StateChangedListener { /** * Controls lifecycle of LocationManagerService. @@ -228,7 +229,7 @@ public class LocationManagerService extends ILocationManager.Stub { private static final String ATTRIBUTION_TAG = "LocationService"; - private final Object mLock = new Object(); + final Object mLock = new Object(); private final Context mContext; private final Injector mInjector; @@ -257,7 +258,7 @@ public class LocationManagerService extends ILocationManager.Stub { new CopyOnWriteArrayList<>(); @GuardedBy("mLock") - private @Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener; + @Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener; LocationManagerService(Context context, Injector injector) { mContext = context.createAttributionContext(ATTRIBUTION_TAG); @@ -331,9 +332,8 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (mProviderManagers) { Preconditions.checkState(getLocationProviderManager(manager.getName()) == null); - manager.startManager(); - manager.setOnProviderLocationTagsChangeListener( - mOnProviderLocationTagsChangeListener); + manager.startManager(this); + if (realProvider != null) { // custom logic wrapping all non-passive providers if (manager != mPassiveManager) { @@ -1356,6 +1356,44 @@ public class LocationManagerService extends ILocationManager.Stub { ipw.decreaseIndent(); } + @Override + public void onStateChanged(String provider, AbstractLocationProvider.State oldState, + AbstractLocationProvider.State newState) { + if (!Objects.equals(oldState.identity, newState.identity)) { + refreshAppOpsRestrictions(UserHandle.USER_ALL); + } + + OnProviderLocationTagsChangeListener listener; + synchronized (mLock) { + listener = mOnProviderLocationTagsChangeListener; + } + + if (listener != null) { + if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags) + || !Objects.equals(oldState.identity, newState.identity)) { + if (oldState.identity != null) { + listener.onLocationTagsChanged( + new LocationManagerInternal.LocationTagInfo( + oldState.identity.getUid(), + oldState.identity.getPackageName(), + Collections.emptySet())); + } + if (newState.identity != null) { + ArraySet<String> attributionTags = new ArraySet<>( + newState.extraAttributionTags.size() + 1); + attributionTags.addAll(newState.extraAttributionTags); + attributionTags.add(newState.identity.getAttributionTag()); + + listener.onLocationTagsChanged( + new LocationManagerInternal.LocationTagInfo( + newState.identity.getUid(), + newState.identity.getPackageName(), + attributionTags)); + } + } + } + } + private void refreshAppOpsRestrictions(int userId) { if (userId == UserHandle.USER_ALL) { final int[] runningUserIds = mInjector.getUserInfoHelper().getRunningUserIds(); @@ -1367,27 +1405,34 @@ public class LocationManagerService extends ILocationManager.Stub { Preconditions.checkArgument(userId >= 0); - ArraySet<String> packages = new ArraySet<>(); - for (LocationProviderManager manager : mProviderManagers) { - packages.add(manager.getIdentity().getPackageName()); - } - packages.add(mContext.getPackageName()); - packages.addAll(mInjector.getSettingsHelper().getIgnoreSettingsPackageWhitelist()); - String[] allowedPackages = packages.toArray(new String[0]); boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId); + String[] allowedPackages = null; + if (!enabled) { + ArraySet<String> packages = new ArraySet<>(); + for (LocationProviderManager manager : mProviderManagers) { + CallerIdentity identity = manager.getIdentity(); + if (identity != null) { + packages.add(identity.getPackageName()); + } + } + packages.add(mContext.getPackageName()); + packages.addAll(mInjector.getSettingsHelper().getIgnoreSettingsPackageWhitelist()); + allowedPackages = packages.toArray(new String[0]); + } + AppOpsManager appOpsManager = Objects.requireNonNull( mContext.getSystemService(AppOpsManager.class)); appOpsManager.setUserRestrictionForUser( AppOpsManager.OP_COARSE_LOCATION, - enabled, + !enabled, LocationManagerService.this, allowedPackages, userId); appOpsManager.setUserRestrictionForUser( AppOpsManager.OP_FINE_LOCATION, - enabled, + !enabled, LocationManagerService.this, allowedPackages, userId); @@ -1467,11 +1512,6 @@ public class LocationManagerService extends ILocationManager.Stub { @Nullable OnProviderLocationTagsChangeListener listener) { synchronized (mLock) { mOnProviderLocationTagsChangeListener = listener; - final int providerCount = mProviderManagers.size(); - for (int i = 0; i < providerCount; i++) { - final LocationProviderManager manager = mProviderManagers.get(i); - manager.setOnProviderLocationTagsChangeListener(listener); - } } } } 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 ca5343174478..a4a59564d0d1 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -55,8 +55,6 @@ import android.location.LastLocationRequest; import android.location.Location; import android.location.LocationManager; import android.location.LocationManagerInternal; -import android.location.LocationManagerInternal.LocationTagInfo; -import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener; import android.location.LocationManagerInternal.ProviderEnabledListener; import android.location.LocationRequest; import android.location.LocationResult; @@ -90,7 +88,6 @@ import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; -import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.location.LocationPermissions; @@ -122,7 +119,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; @@ -171,6 +167,11 @@ public class LocationProviderManager extends private static final int STATE_STOPPING = 1; private static final int STATE_STOPPED = 2; + public interface StateChangedListener { + void onStateChanged(String provider, AbstractLocationProvider.State oldState, + AbstractLocationProvider.State newState); + } + protected interface LocationTransport { void deliverOnLocationChanged(LocationResult locationResult, @@ -1315,7 +1316,7 @@ public class LocationProviderManager extends private @Nullable OnAlarmListener mDelayedRegister; @GuardedBy("mLock") - private @Nullable OnProviderLocationTagsChangeListener mOnLocationTagsChangeListener; + private @Nullable StateChangedListener mStateChangedListener; public LocationProviderManager(Context context, Injector injector, String name, @Nullable PassiveLocationProviderManager passiveManager) { @@ -1354,10 +1355,11 @@ public class LocationProviderManager extends return TAG; } - public void startManager() { + public void startManager(@Nullable StateChangedListener listener) { synchronized (mLock) { Preconditions.checkState(mState == STATE_STOPPED); mState = STATE_STARTED; + mStateChangedListener = listener; mUserHelper.addListener(mUserChangedListener); mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener); @@ -1396,6 +1398,7 @@ public class LocationProviderManager extends mEnabled.clear(); mLastLocations.clear(); + mStateChangedListener = null; mState = STATE_STOPPED; } } @@ -1476,19 +1479,6 @@ public class LocationProviderManager extends } } - /** - * Registers a listener for the location tags of the provider. - * - * @param listener The listener - */ - public void setOnProviderLocationTagsChangeListener( - @Nullable OnProviderLocationTagsChangeListener listener) { - Preconditions.checkArgument(mOnLocationTagsChangeListener == null || listener == null); - synchronized (mLock) { - mOnLocationTagsChangeListener = listener; - } - } - public void setMockProvider(@Nullable MockLocationProvider provider) { synchronized (mLock) { Preconditions.checkState(mState != STATE_STOPPED); @@ -2292,31 +2282,10 @@ public class LocationProviderManager extends updateRegistrations(Registration::onProviderPropertiesChanged); } - if (mOnLocationTagsChangeListener != null) { - if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags) - || !Objects.equals(oldState.identity, newState.identity)) { - if (oldState.identity != null) { - FgThread.getHandler().sendMessage(PooledLambda.obtainMessage( - OnProviderLocationTagsChangeListener::onLocationTagsChanged, - mOnLocationTagsChangeListener, new LocationTagInfo( - oldState.identity.getUid(), oldState.identity.getPackageName(), - Collections.emptySet()) - )); - } - if (newState.identity != null) { - ArraySet<String> attributionTags = new ArraySet<>( - newState.extraAttributionTags.size() + 1); - attributionTags.addAll(newState.extraAttributionTags); - attributionTags.add(newState.identity.getAttributionTag()); - - FgThread.getHandler().sendMessage(PooledLambda.obtainMessage( - OnProviderLocationTagsChangeListener::onLocationTagsChanged, - mOnLocationTagsChangeListener, new LocationTagInfo( - newState.identity.getUid(), newState.identity.getPackageName(), - attributionTags) - )); - } - } + if (mStateChangedListener != null) { + StateChangedListener listener = mStateChangedListener; + FgThread.getExecutor().execute( + () -> listener.onStateChanged(mName, oldState, newState)); } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 19c717d62425..0700a9f51dc3 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -9984,6 +9984,7 @@ public class NotificationManagerService extends SystemService { static final String TAG_APPROVED = "allowed"; static final String TAG_DISALLOWED= "disallowed"; static final String XML_SEPARATOR = ","; + static final String FLAG_SEPARATOR = "\\|"; private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>(); ArrayMap<Pair<ComponentName, Integer>, NotificationListenerFilter> @@ -10255,7 +10256,7 @@ public class NotificationManagerService extends SystemService { private int getTypesFromStringList(String typeList) { int types = 0; if (typeList != null) { - String[] typeStrings = typeList.split(XML_SEPARATOR); + String[] typeStrings = typeList.split(FLAG_SEPARATOR); for (int i = 0; i < typeStrings.length; i++) { final String typeString = typeStrings[i]; if (TextUtils.isEmpty(typeString)) { diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java index ec79483e0f34..ed00609cd8ab 100644 --- a/services/core/java/com/android/server/pm/DumpState.java +++ b/services/core/java/com/android/server/pm/DumpState.java @@ -58,6 +58,7 @@ public final class DumpState { private boolean mTitlePrinted; private boolean mFullPreferred; private boolean mCheckIn; + private boolean mBrief; private String mTargetPackageName; @@ -128,4 +129,12 @@ public final class DumpState { public void setCheckIn(boolean checkIn) { mCheckIn = checkIn; } + + public boolean isBrief() { + return mBrief; + } + + public void setBrief(boolean brief) { + mBrief = brief; + } } diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java index 2015c78e0817..34caaf52b215 100644 --- a/services/core/java/com/android/server/pm/KeySetManagerService.java +++ b/services/core/java/com/android/server/pm/KeySetManagerService.java @@ -30,6 +30,7 @@ import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.utils.WatchedArrayMap; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -65,7 +66,7 @@ public class KeySetManagerService { protected final LongSparseArray<ArraySet<Long>> mKeySetMapping; - private final ArrayMap<String, PackageSetting> mPackages; + private final WatchedArrayMap<String, PackageSetting> mPackages; private long lastIssuedKeySetId = 0; @@ -114,7 +115,7 @@ public class KeySetManagerService { } } - public KeySetManagerService(ArrayMap<String, PackageSetting> packages) { + public KeySetManagerService(WatchedArrayMap<String, PackageSetting> packages) { mKeySets = new LongSparseArray<KeySetHandle>(); mPublicKeys = new LongSparseArray<PublicKeyHandle>(); mKeySetMapping = new LongSparseArray<ArraySet<Long>>(); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index a799ce20f5c7..6f4ec82e357a 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2678,12 +2678,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final String packageNameToLog = (params.installFlags & PackageManager.INSTALL_FROM_ADB) == 0 ? packageName : ""; final long currentTimestamp = System.currentTimeMillis(); + final int packageUid; + if (returnCode != INSTALL_SUCCEEDED) { + // Package didn't install; no valid uid + packageUid = Process.INVALID_UID; + } else { + packageUid = mPm.getPackageUid(packageName, 0, userId); + } FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLER_V2_REPORTED, isIncrementalInstallation(), packageNameToLog, currentTimestamp - createdMillis, returnCode, - getApksSize(packageName)); + getApksSize(packageName), + packageUid); } private long getApksSize(String packageName) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 679042ff40c9..20ca94965188 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -404,6 +404,7 @@ import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2; import com.android.server.rollback.RollbackManagerInternal; import com.android.server.storage.DeviceStorageMonitorInternal; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.utils.SnapshotCache; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.utils.Watchable; import com.android.server.utils.Watched; @@ -871,12 +872,17 @@ public class PackageManagerService extends IPackageManager.Stub @Watched @GuardedBy("mLock") final WatchedArrayMap<String, AndroidPackage> mPackages = new WatchedArrayMap<>(); + private final SnapshotCache<WatchedArrayMap<String, AndroidPackage>> mPackagesSnapshot = + new SnapshotCache.Auto(mPackages, mPackages, "PackageManagerService.mPackages"); // Keys are isolated uids and values are the uid of the application // that created the isolated process. @Watched @GuardedBy("mLock") final WatchedSparseIntArray mIsolatedOwners = new WatchedSparseIntArray(); + private final SnapshotCache<WatchedSparseIntArray> mIsolatedOwnersSnapshot = + new SnapshotCache.Auto(mIsolatedOwners, mIsolatedOwners, + "PackageManagerService.mIsolatedOwners"); /** * Tracks new system packages [received in an OTA] that we expect to @@ -1309,14 +1315,17 @@ public class PackageManagerService extends IPackageManager.Stub // Avoid invalidation-thrashing by preventing cache invalidations from causing property // writes if the cache isn't enabled yet. We re-enable writes later when we're // done initializing. - sSnapshotCorked = true; + sSnapshotCorked.incrementAndGet(); PackageManager.corkPackageInfoCache(); } @Override public void enablePackageCaches() { // Uncork cache invalidations and allow clients to cache package information. - sSnapshotCorked = false; + int corking = sSnapshotCorked.decrementAndGet(); + if (TRACE_SNAPSHOTS && corking == 0) { + Log.i(TAG, "snapshot: corking returns to 0"); + } PackageManager.uncorkPackageInfoCache(); } } @@ -1395,14 +1404,27 @@ public class PackageManagerService extends IPackageManager.Stub @Watched final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> mSharedLibraries = new WatchedArrayMap<>(); + private final SnapshotCache<WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>> + mSharedLibrariesSnapshot = + new SnapshotCache.Auto<>(mSharedLibraries, mSharedLibraries, + "PackageManagerService.mSharedLibraries"); @Watched final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> mStaticLibsByDeclaringPackage = new WatchedArrayMap<>(); + private final SnapshotCache<WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>> + mStaticLibsByDeclaringPackageSnapshot = + new SnapshotCache.Auto<>(mSharedLibraries, mSharedLibraries, + "PackageManagerService.mSharedLibraries"); // Mapping from instrumentation class names to info about them. @Watched final WatchedArrayMap<ComponentName, ParsedInstrumentation> mInstrumentation = new WatchedArrayMap<>(); + private final SnapshotCache<WatchedArrayMap<ComponentName, ParsedInstrumentation>> + mInstrumentationSnapshot = + new SnapshotCache.Auto<>(mInstrumentation, mInstrumentation, + "PackageManagerService.mInstrumentation"); + // Packages whose data we have transfered into another package, thus // should no longer exist. @@ -1588,6 +1610,7 @@ public class PackageManagerService extends IPackageManager.Stub static final int INTEGRITY_VERIFICATION_COMPLETE = 25; static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26; static final int DOMAIN_VERIFICATION = 27; + static final int SNAPSHOT_UNCORK = 28; static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000; static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500; @@ -1834,11 +1857,11 @@ public class PackageManagerService extends IPackageManager.Stub Snapshot(int type) { if (type == Snapshot.SNAPPED) { settings = mSettings.snapshot(); - isolatedOwners = mIsolatedOwners.snapshot(); - packages = mPackages.snapshot(); - sharedLibs = mSharedLibraries.snapshot(); - staticLibs = mStaticLibsByDeclaringPackage.snapshot(); - instrumentation = mInstrumentation.snapshot(); + isolatedOwners = mIsolatedOwnersSnapshot.snapshot(); + packages = mPackagesSnapshot.snapshot(); + sharedLibs = mSharedLibrariesSnapshot.snapshot(); + staticLibs = mStaticLibsByDeclaringPackageSnapshot.snapshot(); + instrumentation = mInstrumentationSnapshot.snapshot(); resolveComponentName = mResolveComponentName.clone(); resolveActivity = new ActivityInfo(mResolveActivity); instantAppInstallerActivity = @@ -4874,12 +4897,16 @@ public class PackageManagerService extends IPackageManager.Stub // A lock-free cache for frequently called functions. private volatile Computer mSnapshotComputer; // If true, the snapshot is invalid (stale). The attribute is static since it may be - // set from outside classes. - private static volatile boolean sSnapshotInvalid = true; + // set from outside classes. The attribute may be set to true anywhere, although it + // should only be set true while holding mLock. However, the attribute id guaranteed + // to be set false only while mLock and mSnapshotLock are both held. + private static AtomicBoolean sSnapshotInvalid = new AtomicBoolean(true); + // The package manager that is using snapshots. + private static PackageManagerService sSnapshotConsumer = null; // If true, the snapshot is corked. Do not create a new snapshot but use the live // computer. This throttles snapshot creation during periods of churn in Package // Manager. - private static volatile boolean sSnapshotCorked = false; + private static AtomicInteger sSnapshotCorked = new AtomicInteger(0); /** * This lock is used to make reads from {@link #sSnapshotInvalid} and @@ -4897,7 +4924,10 @@ public class PackageManagerService extends IPackageManager.Stub // The snapshot disable/enable switch. An image with the flag set true uses snapshots // and an image with the flag set false does not use snapshots. - private static final boolean SNAPSHOT_ENABLED = false; + private static final boolean SNAPSHOT_ENABLED = true; + + // The default auto-cork delay for snapshots. This is 1s. + private static final long SNAPSHOT_AUTOCORK_DELAY_MS = TimeUnit.SECONDS.toMillis(1); // The per-instance snapshot disable/enable flag. This is generally set to false in // test instances and set to SNAPSHOT_ENABLED in operational instances. @@ -4922,15 +4952,16 @@ public class PackageManagerService extends IPackageManager.Stub // If the current thread holds mLock then it may have modified state but not // yet invalidated the snapshot. Always give the thread the live computer. return mLiveComputer; + } else if (sSnapshotCorked.get() > 0) { + // Snapshots are corked, which means new ones should not be built right now. + mSnapshotStatistics.corked(); + return mLiveComputer; } synchronized (mSnapshotLock) { + // This synchronization block serializes access to the snapshot computer and + // to the code that samples mSnapshotInvalid. Computer c = mSnapshotComputer; - if (sSnapshotCorked && (c != null)) { - // Snapshots are corked, which means new ones should not be built right now. - c.use(); - return c; - } - if (sSnapshotInvalid || (c == null)) { + if (sSnapshotInvalid.getAndSet(false) || (c == null)) { // The snapshot is invalid if it is marked as invalid or if it is null. If it // is null, then it is currently being rebuilt by rebuildSnapshot(). synchronized (mLock) { @@ -4938,9 +4969,7 @@ public class PackageManagerService extends IPackageManager.Stub // invalidated as it is rebuilt. However, the snapshot is still // self-consistent (the lock is being held) and is current as of the time // this function is entered. - if (sSnapshotInvalid) { - rebuildSnapshot(); - } + rebuildSnapshot(); // Guaranteed to be non-null. mSnapshotComputer is only be set to null // temporarily in rebuildSnapshot(), which is guarded by mLock(). Since @@ -4958,12 +4987,11 @@ public class PackageManagerService extends IPackageManager.Stub * Rebuild the cached computer. mSnapshotComputer is temporarily set to null to block other * threads from using the invalid computer until it is rebuilt. */ - @GuardedBy("mLock") + @GuardedBy({ "mLock", "mSnapshotLock"}) private void rebuildSnapshot() { final long now = SystemClock.currentTimeMicro(); final int hits = mSnapshotComputer == null ? -1 : mSnapshotComputer.getUsed(); mSnapshotComputer = null; - sSnapshotInvalid = false; final Snapshot args = new Snapshot(Snapshot.SNAPPED); mSnapshotComputer = new ComputerEngine(args); final long done = SystemClock.currentTimeMicro(); @@ -4972,6 +5000,30 @@ public class PackageManagerService extends IPackageManager.Stub } /** + * Create a new snapshot. Used for testing only. This does collect statistics or + * update the snapshot used by other actors. It does not alter the invalidation + * flag. This method takes the mLock internally. + */ + private Computer createNewSnapshot() { + synchronized (mLock) { + final Snapshot args = new Snapshot(Snapshot.SNAPPED); + return new ComputerEngine(args); + } + } + + /** + * Cork snapshots. This times out after the programmed delay. + */ + private void corkSnapshots(int multiplier) { + int corking = sSnapshotCorked.getAndIncrement(); + if (TRACE_SNAPSHOTS && corking == 0) { + Log.i(TAG, "snapshot: corking goes positive"); + } + Message message = mHandler.obtainMessage(SNAPSHOT_UNCORK); + mHandler.sendMessageDelayed(message, SNAPSHOT_AUTOCORK_DELAY_MS * multiplier); + } + + /** * Create a live computer */ private ComputerLocked createLiveComputer() { @@ -4986,9 +5038,9 @@ public class PackageManagerService extends IPackageManager.Stub */ public static void onChange(@Nullable Watchable what) { if (TRACE_SNAPSHOTS) { - Log.e(TAG, "snapshot: onChange(" + what + ")"); + Log.i(TAG, "snapshot: onChange(" + what + ")"); } - sSnapshotInvalid = true; + sSnapshotInvalid.set(true); } /** @@ -5367,6 +5419,13 @@ public class PackageManagerService extends IPackageManager.Stub mDomainVerificationManager.runMessage(messageCode, object); break; } + case SNAPSHOT_UNCORK: { + int corking = sSnapshotCorked.decrementAndGet(); + if (TRACE_SNAPSHOTS && corking == 0) { + Log.e(TAG, "snapshot: corking goes to zero in message handler"); + } + break; + } } } } @@ -6383,12 +6442,13 @@ public class PackageManagerService extends IPackageManager.Stub // constructor, at which time the invalidation method updates it. The cache is // corked initially to ensure a cached computer is not built until the end of the // constructor. - mSnapshotEnabled = SNAPSHOT_ENABLED; - sSnapshotCorked = true; - sSnapshotInvalid = true; mSnapshotStatistics = new SnapshotStatistics(); + sSnapshotConsumer = this; + sSnapshotCorked.set(1); + sSnapshotInvalid.set(true); mLiveComputer = createLiveComputer(); mSnapshotComputer = null; + mSnapshotEnabled = SNAPSHOT_ENABLED; registerObserver(); } @@ -18521,7 +18581,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy({"mInstallLock", "mLock"}) + @GuardedBy("mInstallLock") private void installPackagesTracedLI(List<InstallRequest> requests) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages"); @@ -24018,6 +24078,15 @@ public class PackageManagerService extends IPackageManager.Stub dumpState.setDump(DumpState.DUMP_PER_UID_READ_TIMEOUTS); } else if ("snapshot".equals(cmd)) { dumpState.setDump(DumpState.DUMP_SNAPSHOT_STATISTICS); + if (opti < args.length) { + if ("--full".equals(args[opti])) { + dumpState.setBrief(false); + opti++; + } else if ("--brief".equals(args[opti])) { + dumpState.setBrief(true); + opti++; + } + } } else if ("write".equals(cmd)) { synchronized (mLock) { writeSettingsLPrTEMP(); @@ -24033,11 +24102,11 @@ public class PackageManagerService extends IPackageManager.Stub pw.println("vers,1"); } - // reader - if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) { - if (!checkin) { - dump(DumpState.DUMP_VERSION, fd, pw, dumpState); - } + if (!checkin + && dumpState.isDumping(DumpState.DUMP_VERSION) + && packageName == null) { + // dump version information for all volumes with installed packages + dump(DumpState.DUMP_VERSION, fd, pw, dumpState); } if (!checkin @@ -24068,7 +24137,8 @@ public class PackageManagerService extends IPackageManager.Stub ipw.decreaseIndent(); } - if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) { + if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) + && packageName == null) { final String requiredVerifierPackage = mRequiredVerifierPackage; if (!checkin) { if (dumpState.onTitlePrinted()) { @@ -24089,7 +24159,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && packageName == null) { + if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) + && packageName == null) { final DomainVerificationProxy proxy = mDomainVerificationManager.getProxy(); final ComponentName verifierComponent = proxy.getComponentName(); if (verifierComponent != null) { @@ -24116,11 +24187,13 @@ public class PackageManagerService extends IPackageManager.Stub } } - if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) { + if (dumpState.isDumping(DumpState.DUMP_LIBS) + && packageName == null) { dump(DumpState.DUMP_LIBS, fd, pw, dumpState); } - if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) { + if (dumpState.isDumping(DumpState.DUMP_FEATURES) + && packageName == null) { if (dumpState.onTitlePrinted()) { pw.println(); } @@ -24130,12 +24203,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mAvailableFeatures) { for (FeatureInfo feat : mAvailableFeatures.values()) { - if (checkin) { - pw.print("feat,"); - pw.print(feat.name); - pw.print(","); - pw.println(feat.version); - } else { + if (!checkin) { pw.print(" "); pw.print(feat.name); if (feat.version > 0) { @@ -24143,55 +24211,73 @@ public class PackageManagerService extends IPackageManager.Stub pw.print(feat.version); } pw.println(); + } else { + pw.print("feat,"); + pw.print(feat.name); + pw.print(","); + pw.println(feat.version); } } } } - if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) { synchronized (mLock) { mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName); } } - if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) { synchronized (mLock) { mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName); } } - if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) { synchronized (mLock) { mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName); } } - if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) { synchronized (mLock) { mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName); } } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_PREFERRED) + && packageName == null) { dump(DumpState.DUMP_PREFERRED, fd, pw, dumpState); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML) + && packageName == null) { dump(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED) + && packageName == null) { dump(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) { mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) { synchronized (mLock) { mComponentResolver.dumpContentProviders(pw, dumpState, packageName); } } - if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_KEYSETS)) { synchronized (mLock) { mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState); } @@ -24206,11 +24292,15 @@ public class PackageManagerService extends IPackageManager.Stub } } - if (dumpState.isDumping(DumpState.DUMP_QUERIES)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_QUERIES) + && packageName == null) { dump(DumpState.DUMP_QUERIES, fd, pw, dumpState); } - if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_SHARED_USERS) + && packageName == null) { // This cannot be moved to ComputerEngine since the set of packages in the // SharedUserSetting do not have a copy. synchronized (mLock) { @@ -24218,7 +24308,9 @@ public class PackageManagerService extends IPackageManager.Stub } } - if (dumpState.isDumping(DumpState.DUMP_CHANGES)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_CHANGES) + && packageName == null) { if (dumpState.onTitlePrinted()) pw.println(); pw.println("Package Changes:"); synchronized (mLock) { @@ -24245,7 +24337,9 @@ public class PackageManagerService extends IPackageManager.Stub } } - if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_FROZEN) + && packageName == null) { // XXX should handle packageName != null by dumping only install data that // the given package is involved with. if (dumpState.onTitlePrinted()) pw.println(); @@ -24266,7 +24360,9 @@ public class PackageManagerService extends IPackageManager.Stub ipw.decreaseIndent(); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_VOLUMES) + && packageName == null) { if (dumpState.onTitlePrinted()) pw.println(); final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); @@ -24285,50 +24381,61 @@ public class PackageManagerService extends IPackageManager.Stub ipw.decreaseIndent(); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS) + if (!checkin + && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS) && packageName == null) { synchronized (mLock) { mComponentResolver.dumpServicePermissions(pw, dumpState); } } - if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_DEXOPT) + && packageName == null) { if (dumpState.onTitlePrinted()) pw.println(); dump(DumpState.DUMP_DEXOPT, fd, pw, dumpState); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS) + && packageName == null) { if (dumpState.onTitlePrinted()) pw.println(); dump(DumpState.DUMP_COMPILER_STATS, fd, pw, dumpState); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) { - if (dumpState.onTitlePrinted()) pw.println(); - synchronized (mLock) { - mSettings.dumpReadMessagesLPr(pw, dumpState); + if (dumpState.isDumping(DumpState.DUMP_MESSAGES) + && packageName == null) { + if (!checkin) { + if (dumpState.onTitlePrinted()) pw.println(); + synchronized (mLock) { + mSettings.dumpReadMessagesLPr(pw, dumpState); + } + pw.println(); + pw.println("Package warning messages:"); + dumpCriticalInfo(pw, null); + } else { + dumpCriticalInfo(pw, "msg,"); } - pw.println(); - pw.println("Package warning messages:"); - dumpCriticalInfo(pw, null); - } - - if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) { - dumpCriticalInfo(pw, "msg,"); } // PackageInstaller should be called outside of mPackages lock - if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_INSTALLS) + && packageName == null) { // XXX should handle packageName != null by dumping only install data that // the given package is involved with. if (dumpState.onTitlePrinted()) pw.println(); mInstallerService.dump(new IndentingPrintWriter(pw, " ", 120)); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_APEX) + && packageName == null) { mApexManager.dump(pw, packageName); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS) + if (!checkin + && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS) && packageName == null) { pw.println(); pw.println("Per UID read timeouts:"); @@ -24347,19 +24454,22 @@ public class PackageManagerService extends IPackageManager.Stub } } - if (!checkin && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)) { + if (!checkin + && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS) + && packageName == null) { pw.println("Snapshot statistics"); if (!mSnapshotEnabled) { pw.println(" Snapshots disabled"); } else { int hits = 0; + int level = sSnapshotCorked.get(); synchronized (mSnapshotLock) { if (mSnapshotComputer != null) { hits = mSnapshotComputer.getUsed(); } } final long now = SystemClock.currentTimeMicro(); - mSnapshotStatistics.dump(pw, " ", now, hits, true); + mSnapshotStatistics.dump(pw, " ", now, hits, level, dumpState.isBrief()); } } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 1b8eee3925a5..f5a13d5781ad 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -351,6 +351,7 @@ public final class Settings implements Watchable, Snappable { private final PackageManagerTracedLock mLock; + @Watched(manual = true) private final RuntimePermissionPersistence mRuntimePermissionsPersistence; private final File mSettingsFilename; @@ -364,19 +365,21 @@ public final class Settings implements Watchable, Snappable { /** Map from package name to settings */ @Watched @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - final WatchedArrayMap<String, PackageSetting> mPackages = new WatchedArrayMap<>(); + final WatchedArrayMap<String, PackageSetting> mPackages; + private final SnapshotCache<WatchedArrayMap<String, PackageSetting>> mPackagesSnapshot; /** * List of packages that were involved in installing other packages, i.e. are listed * in at least one app's InstallSource. */ @Watched - private final WatchedArraySet<String> mInstallerPackages = new WatchedArraySet<>(); + private final WatchedArraySet<String> mInstallerPackages; + private final SnapshotCache<WatchedArraySet<String>> mInstallerPackagesSnapshot; /** Map from package name to appId and excluded userids */ @Watched - private final WatchedArrayMap<String, KernelPackageState> mKernelMapping = - new WatchedArrayMap<>(); + private final WatchedArrayMap<String, KernelPackageState> mKernelMapping; + private final SnapshotCache<WatchedArrayMap<String, KernelPackageState>> mKernelMappingSnapshot; // List of replaced system applications @Watched @@ -397,7 +400,7 @@ public final class Settings implements Watchable, Snappable { /** Map from volume UUID to {@link VersionInfo} */ @Watched - private WatchedArrayMap<String, VersionInfo> mVersion = new WatchedArrayMap<>(); + private final WatchedArrayMap<String, VersionInfo> mVersion = new WatchedArrayMap<>(); /** * Version details for a storage volume that may hold apps. @@ -435,6 +438,7 @@ public final class Settings implements Watchable, Snappable { } /** Device identity for the purpose of package verification. */ + @Watched(manual = true) private VerifierDeviceIdentity mVerifierDeviceIdentity; // The user's preferred activities associated with particular intent @@ -462,10 +466,12 @@ public final class Settings implements Watchable, Snappable { private final WatchedSparseArray<SettingBase> mOtherAppIds; // For reading/writing settings file. - private final ArrayList<Signature> mPastSignatures = - new ArrayList<Signature>(); - private final ArrayMap<Long, Integer> mKeySetRefs = - new ArrayMap<Long, Integer>(); + @Watched + private final WatchedArrayList<Signature> mPastSignatures = + new WatchedArrayList<Signature>(); + @Watched + private final WatchedArrayMap<Long, Integer> mKeySetRefs = + new WatchedArrayMap<Long, Integer>(); // Packages that have been renamed since they were first installed. // Keys are the new names of the packages, values are the original @@ -495,18 +501,21 @@ public final class Settings implements Watchable, Snappable { * TODO: make this just a local variable that is passed in during package * scanning to make it less confusing. */ - private final ArrayList<PackageSetting> mPendingPackages = new ArrayList<>(); + @Watched + private final WatchedArrayList<PackageSetting> mPendingPackages = new WatchedArrayList<>(); private final File mSystemDir; - public final KeySetManagerService mKeySetManagerService = - new KeySetManagerService(mPackages.untrackedStorage()); + private final KeySetManagerService mKeySetManagerService; /** Settings and other information about permissions */ + @Watched(manual = true) final LegacyPermissionSettings mPermissions; + @Watched(manual = true) private final LegacyPermissionDataProvider mPermissionDataProvider; + @Watched(manual = true) private final DomainVerificationManagerInternal mDomainVerificationManager; /** @@ -532,8 +541,42 @@ public final class Settings implements Watchable, Snappable { }}; } + private void registerObservers() { + mPackages.registerObserver(mObserver); + mInstallerPackages.registerObserver(mObserver); + mKernelMapping.registerObserver(mObserver); + mDisabledSysPackages.registerObserver(mObserver); + mBlockUninstallPackages.registerObserver(mObserver); + mVersion.registerObserver(mObserver); + mPreferredActivities.registerObserver(mObserver); + mPersistentPreferredActivities.registerObserver(mObserver); + mCrossProfileIntentResolvers.registerObserver(mObserver); + mSharedUsers.registerObserver(mObserver); + mAppIds.registerObserver(mObserver); + mOtherAppIds.registerObserver(mObserver); + mRenamedPackages.registerObserver(mObserver); + mNextAppLinkGeneration.registerObserver(mObserver); + mDefaultBrowserApp.registerObserver(mObserver); + mPendingPackages.registerObserver(mObserver); + mPastSignatures.registerObserver(mObserver); + mKeySetRefs.registerObserver(mObserver); + } + + // CONSTRUCTOR @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public Settings(Map<String, PackageSetting> pkgSettings) { + mPackages = new WatchedArrayMap<>(); + mPackagesSnapshot = + new SnapshotCache.Auto<>(mPackages, mPackages, "Settings.mPackages"); + mKernelMapping = new WatchedArrayMap<>(); + mKernelMappingSnapshot = + new SnapshotCache.Auto<>(mKernelMapping, mKernelMapping, "Settings.mKernelMapping"); + mInstallerPackages = new WatchedArraySet<>(); + mInstallerPackagesSnapshot = + new SnapshotCache.Auto<>(mInstallerPackages, mInstallerPackages, + "Settings.mInstallerPackages"); + mKeySetManagerService = new KeySetManagerService(mPackages); + mLock = new PackageManagerTracedLock(); mPackages.putAll(pkgSettings); mAppIds = new WatchedArrayList<>(); @@ -549,22 +592,8 @@ public final class Settings implements Watchable, Snappable { mBackupStoppedPackagesFilename = null; mKernelMappingFilename = null; mDomainVerificationManager = null; - mPackages.registerObserver(mObserver); - mInstallerPackages.registerObserver(mObserver); - mKernelMapping.registerObserver(mObserver); - mDisabledSysPackages.registerObserver(mObserver); - mBlockUninstallPackages.registerObserver(mObserver); - mVersion.registerObserver(mObserver); - mPreferredActivities.registerObserver(mObserver); - mPersistentPreferredActivities.registerObserver(mObserver); - mCrossProfileIntentResolvers.registerObserver(mObserver); - mSharedUsers.registerObserver(mObserver); - mAppIds.registerObserver(mObserver); - mOtherAppIds.registerObserver(mObserver); - mRenamedPackages.registerObserver(mObserver); - mNextAppLinkGeneration.registerObserver(mObserver); - mDefaultBrowserApp.registerObserver(mObserver); + registerObservers(); Watchable.verifyWatchedAttributes(this, mObserver); mSnapshot = makeCache(); @@ -574,6 +603,18 @@ public final class Settings implements Watchable, Snappable { LegacyPermissionDataProvider permissionDataProvider, @NonNull DomainVerificationManagerInternal domainVerificationManager, @NonNull PackageManagerTracedLock lock) { + mPackages = new WatchedArrayMap<>(); + mPackagesSnapshot = + new SnapshotCache.Auto<>(mPackages, mPackages, "Settings.mPackages"); + mKernelMapping = new WatchedArrayMap<>(); + mKernelMappingSnapshot = + new SnapshotCache.Auto<>(mKernelMapping, mKernelMapping, "Settings.mKernelMapping"); + mInstallerPackages = new WatchedArraySet<>(); + mInstallerPackagesSnapshot = + new SnapshotCache.Auto<>(mInstallerPackages, mInstallerPackages, + "Settings.mInstallerPackages"); + mKeySetManagerService = new KeySetManagerService(mPackages); + mLock = lock; mAppIds = new WatchedArrayList<>(); mOtherAppIds = new WatchedSparseArray<>(); @@ -602,22 +643,7 @@ public final class Settings implements Watchable, Snappable { mDomainVerificationManager = domainVerificationManager; - mPackages.registerObserver(mObserver); - mInstallerPackages.registerObserver(mObserver); - mKernelMapping.registerObserver(mObserver); - mDisabledSysPackages.registerObserver(mObserver); - mBlockUninstallPackages.registerObserver(mObserver); - mVersion.registerObserver(mObserver); - mPreferredActivities.registerObserver(mObserver); - mPersistentPreferredActivities.registerObserver(mObserver); - mCrossProfileIntentResolvers.registerObserver(mObserver); - mSharedUsers.registerObserver(mObserver); - mAppIds.registerObserver(mObserver); - mOtherAppIds.registerObserver(mObserver); - mRenamedPackages.registerObserver(mObserver); - mNextAppLinkGeneration.registerObserver(mObserver); - mDefaultBrowserApp.registerObserver(mObserver); - + registerObservers(); Watchable.verifyWatchedAttributes(this, mObserver); mSnapshot = makeCache(); @@ -629,8 +655,13 @@ public final class Settings implements Watchable, Snappable { * are changed by PackageManagerService APIs are deep-copied */ private Settings(Settings r) { - final int mPackagesSize = r.mPackages.size(); - mPackages.putAll(r.mPackages); + mPackages = r.mPackagesSnapshot.snapshot(); + mPackagesSnapshot = new SnapshotCache.Sealed<>(); + mKernelMapping = r.mKernelMappingSnapshot.snapshot(); + mKernelMappingSnapshot = new SnapshotCache.Sealed<>(); + mInstallerPackages = r.mInstallerPackagesSnapshot.snapshot(); + mInstallerPackagesSnapshot = new SnapshotCache.Sealed<>(); + mKeySetManagerService = new KeySetManagerService(mPackages); // The following assignments satisfy Java requirements but are not // needed by the read-only methods. Note especially that the lock @@ -647,9 +678,7 @@ public final class Settings implements Watchable, Snappable { mDomainVerificationManager = r.mDomainVerificationManager; - mInstallerPackages.addAll(r.mInstallerPackages); - mKernelMapping.putAll(r.mKernelMapping); - mDisabledSysPackages.putAll(r.mDisabledSysPackages); + mDisabledSysPackages.snapshot(r.mDisabledSysPackages); mBlockUninstallPackages.snapshot(r.mBlockUninstallPackages); mVersion.putAll(r.mVersion); mVerifierDeviceIdentity = r.mVerifierDeviceIdentity; @@ -659,23 +688,26 @@ public final class Settings implements Watchable, Snappable { mPersistentPreferredActivities, r.mPersistentPreferredActivities); WatchedSparseArray.snapshot( mCrossProfileIntentResolvers, r.mCrossProfileIntentResolvers); - mSharedUsers.putAll(r.mSharedUsers); + mSharedUsers.snapshot(r.mSharedUsers); mAppIds = r.mAppIds.snapshot(); mOtherAppIds = r.mOtherAppIds.snapshot(); - mPastSignatures.addAll(r.mPastSignatures); - mKeySetRefs.putAll(r.mKeySetRefs); + WatchedArrayList.snapshot( + mPastSignatures, r.mPastSignatures); + WatchedArrayMap.snapshot( + mKeySetRefs, r.mKeySetRefs); mRenamedPackages.snapshot(r.mRenamedPackages); mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration); mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp); // mReadMessages - mPendingPackages.addAll(r.mPendingPackages); + WatchedArrayList.snapshot( + mPendingPackages, r.mPendingPackages); mSystemDir = null; // mKeySetManagerService; mPermissions = r.mPermissions; mPermissionDataProvider = r.mPermissionDataProvider; // Do not register any Watchables and do not create a snapshot cache. - mSnapshot = null; + mSnapshot = new SnapshotCache.Sealed(); } /** @@ -2326,7 +2358,7 @@ public final class Settings implements Watchable, Snappable { serializer.startTag(null, "shared-user"); serializer.attribute(null, ATTR_NAME, usr.name); serializer.attributeInt(null, "userId", usr.userId); - usr.signatures.writeXml(serializer, "sigs", mPastSignatures); + usr.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage()); serializer.endTag(null, "shared-user"); } @@ -2736,11 +2768,11 @@ public final class Settings implements Watchable, Snappable { writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions); - pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); + pkg.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage()); if (installSource.initiatingPackageSignatures != null) { installSource.initiatingPackageSignatures.writeXml( - serializer, "install-initiator-sigs", mPastSignatures); + serializer, "install-initiator-sigs", mPastSignatures.untrackedStorage()); } writeSigningKeySetLPr(serializer, pkg.keySetData); @@ -2909,7 +2941,7 @@ public final class Settings implements Watchable, Snappable { } else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) { // No longer used. } else if (tagName.equals("keyset-settings")) { - mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs); + mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs.untrackedStorage()); } else if (TAG_VERSION.equals(tagName)) { final String volumeUuid = XmlUtils.readStringAttribute(parser, ATTR_VOLUME_UUID); @@ -3697,7 +3729,7 @@ public final class Settings implements Watchable, Snappable { } else if (tagName.equals(TAG_ENABLED_COMPONENTS)) { readEnabledComponentsLPw(packageSetting, parser, 0); } else if (tagName.equals("sigs")) { - packageSetting.signatures.readXml(parser, mPastSignatures); + packageSetting.signatures.readXml(parser, mPastSignatures.untrackedStorage()); } else if (tagName.equals(TAG_PERMISSIONS)) { readInstallPermissionsLPr(parser, packageSetting.getLegacyPermissionState(), users); @@ -3728,7 +3760,7 @@ public final class Settings implements Watchable, Snappable { packageSetting.keySetData.addDefinedKeySet(id, alias); } else if (tagName.equals("install-initiator-sigs")) { final PackageSignatures signatures = new PackageSignatures(); - signatures.readXml(parser, mPastSignatures); + signatures.readXml(parser, mPastSignatures.untrackedStorage()); packageSetting.installSource = packageSetting.installSource.setInitiatingPackageSignatures(signatures); } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) { @@ -3923,7 +3955,7 @@ public final class Settings implements Watchable, Snappable { String tagName = parser.getName(); if (tagName.equals("sigs")) { - su.signatures.readXml(parser, mPastSignatures); + su.signatures.readXml(parser, mPastSignatures.untrackedStorage()); } else if (tagName.equals("perms")) { readInstallPermissionsLPr(parser, su.getLegacyPermissionState(), users); } else { diff --git a/services/core/java/com/android/server/pm/SnapshotStatistics.java b/services/core/java/com/android/server/pm/SnapshotStatistics.java index c425bad50ae8..7bf00603f132 100644 --- a/services/core/java/com/android/server/pm/SnapshotStatistics.java +++ b/services/core/java/com/android/server/pm/SnapshotStatistics.java @@ -23,6 +23,7 @@ import android.os.Message; import android.os.SystemClock; import android.text.TextUtils; +import com.android.internal.annotations.GuardedBy; import com.android.server.EventLogTags; import java.io.PrintWriter; @@ -239,6 +240,11 @@ public class SnapshotStatistics { public int mTotalUsed = 0; /** + * The total number of times a snapshot was bypassed because corking was in effect. + */ + public int mTotalCorked = 0; + + /** * The total number of builds that count as big, which means they took longer than * SNAPSHOT_BIG_BUILD_TIME_NS. */ @@ -291,6 +297,13 @@ public class SnapshotStatistics { } } + /** + * Record a cork. + */ + private void corked() { + mTotalCorked++; + } + private Stats(long now) { mStartTimeUs = now; mTimes = new int[mTimeBins.count()]; @@ -308,6 +321,7 @@ public class SnapshotStatistics { mUsed = Arrays.copyOf(orig.mUsed, orig.mUsed.length); mTotalBuilds = orig.mTotalBuilds; mTotalUsed = orig.mTotalUsed; + mTotalCorked = orig.mTotalCorked; mBigBuilds = orig.mBigBuilds; mShortLived = orig.mShortLived; mTotalTimeUs = orig.mTotalTimeUs; @@ -365,6 +379,7 @@ public class SnapshotStatistics { * Dump the summary statistics record. Choose the header or the data. * number of builds * number of uses + * number of corks * number of big builds * number of short lifetimes * cumulative build time, in seconds @@ -373,13 +388,13 @@ public class SnapshotStatistics { private void dumpStats(PrintWriter pw, String indent, long now, boolean header) { dumpPrefix(pw, indent, now, header, "Summary stats"); if (header) { - pw.format(Locale.US, " %10s %10s %10s %10s %10s %10s", - "TotBlds", "TotUsed", "BigBlds", "ShortLvd", + pw.format(Locale.US, " %10s %10s %10s %10s %10s %10s %10s", + "TotBlds", "TotUsed", "TotCork", "BigBlds", "ShortLvd", "TotTime", "MaxTime"); } else { pw.format(Locale.US, - " %10d %10d %10d %10d %10d %10d", - mTotalBuilds, mTotalUsed, mBigBuilds, mShortLived, + " %10d %10d %10d %10d %10d %10d %10d", + mTotalBuilds, mTotalUsed, mTotalCorked, mBigBuilds, mShortLived, mTotalTimeUs / 1000, mMaxBuildTimeUs / 1000); } pw.println(); @@ -516,7 +531,7 @@ public class SnapshotStatistics { * @param done The time at which the snapshot rebuild completed, in ns. * @param hits The number of times the previous snapshot was used. */ - public void rebuild(long now, long done, int hits) { + public final void rebuild(long now, long done, int hits) { // The duration has a span of about 2000s final int duration = (int) (done - now); boolean reportEvent = false; @@ -544,9 +559,20 @@ public class SnapshotStatistics { } /** + * Record a corked snapshot request. + */ + public final void corked() { + synchronized (mLock) { + mShort[0].corked(); + mLong[0].corked(); + } + } + + /** * Roll a stats array. Shift the elements up an index and create a new element at * index zero. The old element zero is completed with the specified time. */ + @GuardedBy("mLock") private void shift(Stats[] s, long now) { s[0].complete(now); for (int i = s.length - 1; i > 0; i--) { @@ -598,7 +624,8 @@ public class SnapshotStatistics { * Dump the statistics. The format is compatible with the PackageManager dumpsys * output. */ - public void dump(PrintWriter pw, String indent, long now, int unrecorded, boolean full) { + public void dump(PrintWriter pw, String indent, long now, int unrecorded, + int corkLevel, boolean full) { // Grab the raw statistics under lock, but print them outside of the lock. Stats[] l; Stats[] s; @@ -608,7 +635,8 @@ public class SnapshotStatistics { s = Arrays.copyOf(mShort, mShort.length); s[0] = new Stats(s[0]); } - pw.format(Locale.US, "%s Unrecorded hits %d", indent, unrecorded); + pw.format(Locale.US, "%s Unrecorded-hits: %d Cork-level: %d", indent, + unrecorded, corkLevel); pw.println(); dump(pw, indent, now, l, s, "stats"); if (!full) { diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index f86eb601e0ae..b7a069e10b67 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -94,9 +94,6 @@ "include-filter": "com.android.server.pm.test.OverlayActorVisibilityTest" } ] - }, - { - "name": "CtsPackageManagerBootTestCases" } ], "postsubmit": [ @@ -154,6 +151,9 @@ "include-filter": "com.android.server.pm.parsing.SystemPartitionParseTest" } ] + }, + { + "name": "CtsPackageManagerBootTestCases" } ], "imports": [ 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 50f958f558f7..a7bac20976cd 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -37,6 +37,7 @@ import android.content.pm.PermissionInfo; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.media.RingtoneManager; +import android.media.midi.MidiManager; import android.net.Uri; import android.os.Build; import android.os.Environment; @@ -866,6 +867,11 @@ final class DefaultPermissionGrantPolicy { grantPermissionsToSystemPackage(pm, systemCaptionsServicePackageName, userId, MICROPHONE_PERMISSIONS); } + + // Bluetooth MIDI Service + grantSystemFixedPermissionsToSystemPackage(pm, + MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, userId, + NEARBY_DEVICES_PERMISSIONS); } private String getDefaultSystemHandlerActivityPackageForCategory(PackageManagerWrapper pm, diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index d73203df81c4..c897c3ee4f42 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -5130,6 +5130,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { long[] pattern; switch (effectId) { case HapticFeedbackConstants.CONTEXT_CLICK: + case HapticFeedbackConstants.GESTURE_END: return VibrationEffect.get(VibrationEffect.EFFECT_TICK); case HapticFeedbackConstants.TEXT_HANDLE_MOVE: if (!mHapticTextHandleEnabled) { @@ -5142,7 +5143,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE: case HapticFeedbackConstants.ENTRY_BUMP: case HapticFeedbackConstants.DRAG_CROSSING: - case HapticFeedbackConstants.GESTURE_END: return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false); case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS case HapticFeedbackConstants.VIRTUAL_KEY: diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 8991981e6e6d..3018e5f8fa33 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -2957,8 +2957,7 @@ public final class PowerManagerService extends SystemService // Group has been removed. return; } - // TODO (b/175764708): Support per-display doze. - wakefulness = getWakefulnessLocked(); + wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); if ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) && mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId) && mDisplayGroupPowerStateMapper.isReady(groupId)) { @@ -3100,6 +3099,7 @@ public final class PowerManagerService extends SystemService * Returns true if the device is allowed to doze in its current state. */ private boolean canDozeLocked() { + // TODO (b/175764708): Support per-display doze. return getWakefulnessLocked() == WAKEFULNESS_DOZING; } diff --git a/services/core/java/com/android/server/utils/SnapshotCache.java b/services/core/java/com/android/server/utils/SnapshotCache.java index b4b8835ac026..42b9b23c1ab2 100644 --- a/services/core/java/com/android/server/utils/SnapshotCache.java +++ b/services/core/java/com/android/server/utils/SnapshotCache.java @@ -19,6 +19,9 @@ package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; +import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicInteger; + /** * A class that caches snapshots. Instances are instantiated on a {@link Watchable}; when the * {@link Watchable} reports a change, the cache is cleared. The snapshot() method fetches the @@ -35,25 +38,65 @@ public abstract class SnapshotCache<T> extends Watcher{ */ private static final boolean ENABLED = true; + /** + * The statistics for a single cache. The object records the number of times a + * snapshot was reused and the number of times a snapshot was rebuilt. + */ + private static class Statistics { + final String mName; + private final AtomicInteger mReused = new AtomicInteger(0); + private final AtomicInteger mRebuilt = new AtomicInteger(0); + Statistics(@NonNull String n) { + mName = n; + } + } + // The source object from which snapshots are created. This may be null if createSnapshot() // does not require it. protected final T mSource; // The cached snapshot - private T mSnapshot = null; + private volatile T mSnapshot = null; // True if the snapshot is sealed and may not be modified. - private boolean mSealed = false; + private volatile boolean mSealed = false; + + // The statistics for this cache. This may be null. + private final Statistics mStatistics; + + /** + * The global list of caches. + */ + private static final WeakHashMap<SnapshotCache, Void> sCaches = new WeakHashMap<>(); /** * Create a cache with a source object for rebuilding snapshots and a - * {@link Watchable} that notifies when the cache is invalid. + * {@link Watchable} that notifies when the cache is invalid. If the name is null + * then statistics are not collected for this cache. * @param source Source data for rebuilding snapshots. * @param watchable The object that notifies when the cache is invalid. + * @param name The name of the cache, for statistics reporting. */ - public SnapshotCache(@Nullable T source, @NonNull Watchable watchable) { + public SnapshotCache(@Nullable T source, @NonNull Watchable watchable, @Nullable String name) { mSource = source; watchable.registerObserver(this); + if (name != null) { + mStatistics = new Statistics(name); + sCaches.put(this, null); + } else { + mStatistics = null; + } + } + + /** + * Create a cache with a source object for rebuilding snapshots and a + * {@link Watchable} that notifies when the cache is invalid. The name is null in + * this API. + * @param source Source data for rebuilding snapshots. + * @param watchable The object that notifies when the cache is invalid. + */ + public SnapshotCache(@Nullable T source, @NonNull Watchable watchable) { + this(source, watchable, null); } /** @@ -63,13 +106,14 @@ public abstract class SnapshotCache<T> extends Watcher{ public SnapshotCache() { mSource = null; mSealed = true; + mStatistics = null; } /** * Notify the object that the source object has changed. If the local object is sealed then * IllegalStateException is thrown. Otherwise, the cache is cleared. */ - public void onChange(@Nullable Watchable what) { + public final void onChange(@Nullable Watchable what) { if (mSealed) { throw new IllegalStateException("attempt to change a sealed object"); } @@ -79,7 +123,7 @@ public abstract class SnapshotCache<T> extends Watcher{ /** * Seal the cache. Attempts to modify the cache will generate an exception. */ - public void seal() { + public final void seal() { mSealed = true; } @@ -88,11 +132,14 @@ public abstract class SnapshotCache<T> extends Watcher{ * new snapshot and saves it in the cache. * @return A snapshot as returned by createSnapshot() and possibly cached. */ - public T snapshot() { + public final T snapshot() { T s = mSnapshot; if (s == null || !ENABLED) { s = createSnapshot(); mSnapshot = s; + if (mStatistics != null) mStatistics.mRebuilt.incrementAndGet(); + } else { + if (mStatistics != null) mStatistics.mReused.incrementAndGet(); } return s; } @@ -123,4 +170,25 @@ public abstract class SnapshotCache<T> extends Watcher{ throw new UnsupportedOperationException("cannot snapshot a sealed snaphot"); } } + + /** + * A snapshot cache suitable for Snappable types. The key is that Snappable types + * have a known implementation of createSnapshot() so that this class is concrete. + * @param <T> The class whose snapshot is being cached. + */ + public static class Auto<T extends Snappable<T>> extends SnapshotCache<T> { + public Auto(@NonNull T source, @NonNull Watchable watchable, @Nullable String name) { + super(source, watchable, name); + } + public Auto(@NonNull T source, @NonNull Watchable watchable) { + this(source, watchable, null); + } + /** + * Concrete createSnapshot() using the snapshot() method of <T>. + */ + public T createSnapshot() { + return mSource.snapshot(); + } + } + } diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java index 88180239da67..3bdeec0c1d8e 100644 --- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java +++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java @@ -158,8 +158,15 @@ public class UnderlyingNetworkTracker { * carrier owned networks may be selected, as the request specifies only subIds in the VCN's * subscription group, while the VCN networks are excluded by virtue of not having subIds set on * the VCN-exposed networks. + * + * <p>If the VCN that this UnderlyingNetworkTracker belongs to is in test-mode, this will return + * a NetworkRequest that only matches Test Networks. */ private NetworkRequest getRouteSelectionRequest() { + if (mVcnContext.isInTestMode()) { + return getTestNetworkRequest(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)); + } + return getBaseNetworkRequestBuilder() .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) .build(); @@ -210,6 +217,15 @@ public class UnderlyingNetworkTracker { .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); } + /** Builds and returns a NetworkRequest for the given subIds to match Test Networks. */ + private NetworkRequest getTestNetworkRequest(@NonNull Set<Integer> subIds) { + return new NetworkRequest.Builder() + .clearCapabilities() + .addTransportType(NetworkCapabilities.TRANSPORT_TEST) + .setSubscriptionIds(subIds) + .build(); + } + /** * Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot. * diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java index 7399e56b3a95..d958222ea407 100644 --- a/services/core/java/com/android/server/vcn/VcnContext.java +++ b/services/core/java/com/android/server/vcn/VcnContext.java @@ -31,14 +31,17 @@ public class VcnContext { @NonNull private final Context mContext; @NonNull private final Looper mLooper; @NonNull private final VcnNetworkProvider mVcnNetworkProvider; + private final boolean mIsInTestMode; public VcnContext( @NonNull Context context, @NonNull Looper looper, - @NonNull VcnNetworkProvider vcnNetworkProvider) { + @NonNull VcnNetworkProvider vcnNetworkProvider, + boolean isInTestMode) { mContext = Objects.requireNonNull(context, "Missing context"); mLooper = Objects.requireNonNull(looper, "Missing looper"); mVcnNetworkProvider = Objects.requireNonNull(vcnNetworkProvider, "Missing networkProvider"); + mIsInTestMode = isInTestMode; } @NonNull @@ -56,6 +59,10 @@ public class VcnContext { return mVcnNetworkProvider; } + public boolean isInTestMode() { + return mIsInTestMode; + } + /** * Verifies that the caller is running on the VcnContext Thread. * diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 9f51d97d3707..667a4fa0ae26 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2483,39 +2483,52 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** - * @return whether this activity supports split-screen multi-window and can be put in the docked - * root task. + * @return whether this activity supports split-screen multi-window and can be put in + * split-screen. */ @Override public boolean supportsSplitScreenWindowingMode() { - // An activity can not be docked even if it is considered resizeable because it only - // supports picture-in-picture mode but has a non-resizeable resizeMode - return super.supportsSplitScreenWindowingMode() - && mAtmService.mSupportsSplitScreenMultiWindow && supportsMultiWindow(); + return supportsSplitScreenWindowingModeInDisplayArea(getDisplayArea()); } /** - * @return whether this activity supports freeform multi-window and can be put in the freeform - * root task. + * @return whether this activity supports split-screen multi-window and can be put in + * split-screen if it is in the given {@link TaskDisplayArea}. */ + boolean supportsSplitScreenWindowingModeInDisplayArea(@Nullable TaskDisplayArea tda) { + return super.supportsSplitScreenWindowingMode() + && mAtmService.mSupportsSplitScreenMultiWindow + && supportsMultiWindowInDisplayArea(tda); + } + boolean supportsFreeform() { - return mAtmService.mSupportsFreeformWindowManagement && supportsMultiWindow(); + return supportsFreeformInDisplayArea(getDisplayArea()); } /** - * @return whether this activity supports multi-window. + * @return whether this activity supports freeform multi-window and can be put in the freeform + * windowing mode if it is in the given {@link TaskDisplayArea}. */ + boolean supportsFreeformInDisplayArea(@Nullable TaskDisplayArea tda) { + return mAtmService.mSupportsFreeformWindowManagement + && supportsMultiWindowInDisplayArea(tda); + } + boolean supportsMultiWindow() { - return mAtmService.mSupportsMultiWindow && !isActivityTypeHome() - && (isResizeable() || mAtmService.mDevEnableNonResizableMultiWindow); + return supportsMultiWindowInDisplayArea(getDisplayArea()); } - // TODO(b/176061101) replace supportsMultiWindow() after fixing tests. - boolean supportsMultiWindow2() { + /** + * @return whether this activity supports multi-window if it is in the given + * {@link TaskDisplayArea}. + */ + boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) { + if (isActivityTypeHome()) { + return false; + } if (!mAtmService.mSupportsMultiWindow) { return false; } - final TaskDisplayArea tda = getDisplayArea(); if (tda == null) { return false; } @@ -2811,6 +2824,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Transition newTransition = (!mAtmService.getTransitionController().isCollecting() && mAtmService.getTransitionController().getTransitionPlayer() != null) ? mAtmService.getTransitionController().createTransition(TRANSIT_CLOSE) : null; + mTaskSupervisor.mNoHistoryActivities.remove(this); makeFinishingLocked(); // Make a local reference to its task since this.task could be set to null once this // activity is destroyed and detached from task. diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 8583061a3986..7fe0f5be287c 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -277,6 +277,13 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { * settle down before doing so. It contains ActivityRecord objects. */ final ArrayList<ActivityRecord> mFinishingActivities = new ArrayList<>(); + /** + * Activities that specify No History must be removed once the user navigates away from them. + * If the device goes to sleep with such an activity in the paused state then we save it + * here and finish it later if another activity replaces it on wakeup. + */ + final ArrayList<ActivityRecord> mNoHistoryActivities = new ArrayList<>(); + /** List of activities whose multi-window mode changed that we need to report to the * application */ private final ArrayList<ActivityRecord> mMultiWindowModeChangedActivities = new ArrayList<>(); @@ -479,6 +486,20 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } } + void finishNoHistoryActivitiesIfNeeded(ActivityRecord next) { + for (int i = mNoHistoryActivities.size() - 1; i >= 0; --i) { + final ActivityRecord noHistoryActivity = mNoHistoryActivities.get(i); + if (!noHistoryActivity.finishing && noHistoryActivity != next + && next.occludesParent() + && noHistoryActivity.getDisplayId() == next.getDisplayId()) { + ProtoLog.d(WM_DEBUG_STATES, "no-history finish of %s on new resume", + noHistoryActivity); + noHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */); + mNoHistoryActivities.remove(noHistoryActivity); + } + } + } + private static int nextTaskIdForUser(int taskId, int userId) { int nextTaskId = taskId + 1; if (nextTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) { @@ -1684,7 +1705,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // Leave the task in its current root task or a fullscreen root task if it isn't // resizeable and the preferred root task is in multi-window mode. - if (inMultiWindowMode && !task.supportsMultiWindow()) { + if (inMultiWindowMode + && !task.supportsMultiWindowInDisplayArea(rootTask.getDisplayArea())) { Slog.w(TAG, "Can not move unresizeable task=" + task + " to multi-window root task=" + rootTask + " Moving to a fullscreen root task instead."); if (prevRootTask != null) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 8800a9e2d479..91f75e57c092 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -901,6 +901,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp && preferredModeId != 0) { mTmpApplySurfaceChangesTransactionState.preferredModeId = preferredModeId; } + + final float preferredMaxRefreshRate = getDisplayPolicy().getRefreshRatePolicy() + .getPreferredMaxRefreshRate(w); + if (mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate == 0 + && preferredMaxRefreshRate != 0) { + mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate = + preferredMaxRefreshRate; + } } } @@ -4230,6 +4238,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mLastHasContent, mTmpApplySurfaceChangesTransactionState.preferredRefreshRate, mTmpApplySurfaceChangesTransactionState.preferredModeId, + mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate, mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing, true /* inTraversal, must call performTraversalInTrans... below */); } @@ -4513,12 +4522,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } private static final class ApplySurfaceChangesTransactionState { - boolean displayHasContent; - boolean obscured; - boolean syswin; - boolean preferMinimalPostProcessing; - float preferredRefreshRate; - int preferredModeId; + public boolean displayHasContent; + public boolean obscured; + public boolean syswin; + public boolean preferMinimalPostProcessing; + public float preferredRefreshRate; + public int preferredModeId; + public float preferredMaxRefreshRate; void reset() { displayHasContent = false; @@ -4527,6 +4537,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp preferMinimalPostProcessing = false; preferredRefreshRate = 0; preferredModeId = 0; + preferredMaxRefreshRate = 0; } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 30f69dd79f5e..37e15c71c924 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -92,6 +92,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; +import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON; import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT; @@ -2667,7 +2668,8 @@ public class DisplayPolicy { if (oldImmersiveMode != newImmersiveMode) { mLastImmersiveMode = newImmersiveMode; // The immersive confirmation window should be attached to the immersive window root. - final int rootDisplayAreaId = win.getRootDisplayArea().mFeatureId; + final RootDisplayArea root = win.getRootDisplayArea(); + final int rootDisplayAreaId = root == null ? FEATURE_UNDEFINED : root.mFeatureId; mImmersiveModeConfirmation.immersiveModeChangedLw(rootDisplayAreaId, newImmersiveMode, mService.mPolicy.isUserSetupComplete(), isNavBarEmpty(disableFlags)); diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java index 26871d130fbf..deaf611446bc 100644 --- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java +++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java @@ -20,6 +20,7 @@ import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import android.util.ArraySet; +import android.view.Display; import android.view.Display.Mode; import android.view.DisplayInfo; @@ -95,18 +96,10 @@ class RefreshRatePolicy { return 0; } - // If app requests a certain refresh rate or mode, don't override it. if (w.mAttrs.preferredRefreshRate != 0 || w.mAttrs.preferredDisplayModeId != 0) { return w.mAttrs.preferredDisplayModeId; } - final String packageName = w.getOwningPackage(); - - // If app is using Camera, force it to default (lower) refresh rate. - if (mNonHighRefreshRatePackages.contains(packageName)) { - return mLowRefreshRateMode.getModeId(); - } - return 0; } @@ -145,6 +138,44 @@ class RefreshRatePolicy { if (mHighRefreshRateDenylist.isDenylisted(packageName)) { return mLowRefreshRateMode.getRefreshRate(); } + + final int preferredModeId = getPreferredModeId(w); + if (preferredModeId > 0) { + DisplayInfo info = w.getDisplayInfo(); + if (info != null) { + for (Display.Mode mode : info.supportedModes) { + if (preferredModeId == mode.getModeId()) { + return mode.getRefreshRate(); + } + } + } + } + + return 0; + } + + float getPreferredMaxRefreshRate(WindowState w) { + // If app is animating, it's not able to control refresh rate because we want the animation + // to run in default refresh rate. + if (w.isAnimating(TRANSITION | PARENTS)) { + return 0; + } + + // If app requests a certain refresh rate or mode, don't override it. + if (w.mAttrs.preferredDisplayModeId != 0) { + return 0; + } + + if (w.mAttrs.preferredMaxDisplayRefreshRate > 0) { + return w.mAttrs.preferredMaxDisplayRefreshRate; + } + + final String packageName = w.getOwningPackage(); + // If app is using Camera, force it to default (lower) refresh rate. + if (mNonHighRefreshRatePackages.contains(packageName)) { + return mLowRefreshRateMode.getRefreshRate(); + } + return 0; } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index ab7e65cc64e8..0879ddd53a39 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -3046,7 +3046,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // There is a 1-to-1 relationship between root task and task when not in // primary split-windowing mode. if (task.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - && r.supportsSplitScreenWindowingMode() + && r.supportsSplitScreenWindowingModeInDisplayArea(task.getDisplayArea()) && (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || windowingMode == WINDOWING_MODE_UNDEFINED)) { return true; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index be01173415a5..80a68f514faf 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -61,6 +61,7 @@ import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.SurfaceControl.METADATA_TASK_ID; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; @@ -585,13 +586,6 @@ class Task extends WindowContainer<WindowContainer> { ActivityRecord mLastPausedActivity = null; /** - * Activities that specify No History must be removed once the user navigates away from them. - * If the device goes to sleep with such an activity in the paused state then we save it here - * and finish it later if another activity replaces it on wakeup. - */ - ActivityRecord mLastNoHistoryActivity = null; - - /** * Current activity that is resumed, or null if there is none. * Only set at leaf tasks. */ @@ -1670,6 +1664,14 @@ class Task extends WindowContainer<WindowContainer> { return isUidPresent; } + ActivityRecord topActivityContainsStartingWindow() { + if (getParent() == null) { + return null; + } + return getActivity((r) -> r.getWindow(window -> + window.getBaseType() == TYPE_APPLICATION_STARTING) != null); + } + ActivityRecord topActivityWithStartingWindow() { if (getParent() == null) { return null; @@ -1977,32 +1979,46 @@ class Task extends WindowContainer<WindowContainer> { @Override public boolean supportsSplitScreenWindowingMode() { + return supportsSplitScreenWindowingModeInDisplayArea(getDisplayArea()); + } + + boolean supportsSplitScreenWindowingModeInDisplayArea(@Nullable TaskDisplayArea tda) { final Task topTask = getTopMostTask(); return super.supportsSplitScreenWindowingMode() - && (topTask == null || topTask.supportsSplitScreenWindowingModeInner()); + && (topTask == null || topTask.supportsSplitScreenWindowingModeInner(tda)); } - private boolean supportsSplitScreenWindowingModeInner() { + private boolean supportsSplitScreenWindowingModeInner(@Nullable TaskDisplayArea tda) { return super.supportsSplitScreenWindowingMode() && mAtmService.mSupportsSplitScreenMultiWindow - && supportsMultiWindow(); + && supportsMultiWindowInDisplayArea(tda); } boolean supportsFreeform() { - return mAtmService.mSupportsFreeformWindowManagement && supportsMultiWindow(); + return supportsFreeformInDisplayArea(getDisplayArea()); + } + + /** + * @return whether this task supports freeform multi-window if it is in the given + * {@link TaskDisplayArea}. + */ + boolean supportsFreeformInDisplayArea(@Nullable TaskDisplayArea tda) { + return mAtmService.mSupportsFreeformWindowManagement + && supportsMultiWindowInDisplayArea(tda); } boolean supportsMultiWindow() { - return mAtmService.mSupportsMultiWindow - && (isResizeable() || mAtmService.mDevEnableNonResizableMultiWindow); + return supportsMultiWindowInDisplayArea(getDisplayArea()); } - // TODO(b/176061101) replace supportsMultiWindow() after fixing tests. - boolean supportsMultiWindow2() { + /** + * @return whether this task supports multi-window if it is in the given + * {@link TaskDisplayArea}. + */ + boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) { if (!mAtmService.mSupportsMultiWindow) { return false; } - final TaskDisplayArea tda = getDisplayArea(); if (tda == null) { return false; } @@ -4340,7 +4356,12 @@ class Task extends WindowContainer<WindowContainer> { case WINDOWING_MODE_FULLSCREEN: if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) { // At least one of the split-screen stacks that covers this one is translucent. - return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; + // When in split mode, home task will be reparented to the secondary split while + // leaving tasks not supporting split below. Due to + // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to + // the bottom, this makes sure tasks not in split roots won't occlude home task + // unexpectedly. + return TASK_VISIBILITY_INVISIBLE; } break; case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: @@ -5733,7 +5754,9 @@ class Task extends WindowContainer<WindowContainer> { ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev); mPausingActivity = prev; mLastPausedActivity = prev; - mLastNoHistoryActivity = prev.isNoHistory() ? prev : null; + if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) { + mTaskSupervisor.mNoHistoryActivities.add(prev); + } prev.setState(PAUSING, "startPausingLocked"); prev.getTask().touchActiveTime(); @@ -5779,13 +5802,13 @@ class Task extends WindowContainer<WindowContainer> { Slog.w(TAG, "Exception thrown during pause", e); mPausingActivity = null; mLastPausedActivity = null; - mLastNoHistoryActivity = null; + mTaskSupervisor.mNoHistoryActivities.remove(prev); } } } else { mPausingActivity = null; mLastPausedActivity = null; - mLastNoHistoryActivity = null; + mTaskSupervisor.mNoHistoryActivities.remove(prev); } // If we are not going to sleep, we want to ensure the device is @@ -6296,13 +6319,8 @@ class Task extends WindowContainer<WindowContainer> { // If the most recent activity was noHistory but was only stopped rather // than stopped+finished because the device went to sleep, we need to make // sure to finish it as we're making a new activity topmost. - if (shouldSleepActivities() && mLastNoHistoryActivity != null - && !mLastNoHistoryActivity.finishing - && mLastNoHistoryActivity != next) { - ProtoLog.d(WM_DEBUG_STATES, "no-history finish of %s on new resume", - mLastNoHistoryActivity); - mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */); - mLastNoHistoryActivity = null; + if (shouldSleepActivities()) { + mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next); } if (prev != null && prev != next && next.nowVisible) { @@ -7311,8 +7329,10 @@ class Task extends WindowContainer<WindowContainer> { isPausingDied = true; } if (mLastPausedActivity != null && mLastPausedActivity.app == app) { + if (mLastPausedActivity.isNoHistory()) { + mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity); + } mLastPausedActivity = null; - mLastNoHistoryActivity = null; } return isPausingDied; } @@ -7348,8 +7368,6 @@ class Task extends WindowContainer<WindowContainer> { if (dumpAll) { printed |= printThisActivity(pw, mLastPausedActivity, dumpPackage, false, " mLastPausedActivity: ", null); - printed |= printThisActivity(pw, mLastNoHistoryActivity, dumpPackage, - false, " mLastNoHistoryActivity: ", null); } printed |= dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, false, headerPrinter); diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 368e6dd610e8..ccfdb8c23679 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1614,18 +1614,18 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { boolean supportsPip = mAtmService.mSupportsPictureInPicture; if (supportsMultiWindow) { if (task != null) { - supportsSplitScreen = task.supportsSplitScreenWindowingMode(); - supportsFreeform = task.supportsFreeform(); - supportsMultiWindow = task.supportsMultiWindow() + supportsSplitScreen = task.supportsSplitScreenWindowingModeInDisplayArea(this); + supportsFreeform = task.supportsFreeformInDisplayArea(this); + supportsMultiWindow = task.supportsMultiWindowInDisplayArea(this) // When the activity needs to be moved to PIP while the Task is not in PIP, // it can be moved to a new created PIP Task, so WINDOWING_MODE_PINNED is // always valid for Task as long as the device supports it. || (windowingMode == WINDOWING_MODE_PINNED && supportsPip); } else if (r != null) { - supportsSplitScreen = r.supportsSplitScreenWindowingMode(); - supportsFreeform = r.supportsFreeform(); + supportsSplitScreen = r.supportsSplitScreenWindowingModeInDisplayArea(this); + supportsFreeform = r.supportsFreeformInDisplayArea(this); supportsPip = r.supportsPictureInPicture(); - supportsMultiWindow = r.supportsMultiWindow(); + supportsMultiWindow = r.supportsMultiWindowInDisplayArea(this); } } @@ -2078,14 +2078,15 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { task.finishAllActivitiesImmediately(); } else { // Reparent task to corresponding launch root or display area. - final WindowContainer launchRoot = task.supportsSplitScreenWindowingMode() - ? toDisplayArea.getLaunchRootTask( - task.getWindowingMode(), - task.getActivityType(), - null /* options */, - null /* sourceTask */, - 0 /* launchFlags */) - : null; + final WindowContainer launchRoot = + task.supportsSplitScreenWindowingModeInDisplayArea(toDisplayArea) + ? toDisplayArea.getLaunchRootTask( + task.getWindowingMode(), + task.getActivityType(), + null /* options */, + null /* sourceTask */, + 0 /* launchFlags */) + : null; task.reparent(launchRoot == null ? toDisplayArea : launchRoot, POSITION_TOP); // Set the windowing mode to undefined by default to let the root task inherited the @@ -2101,7 +2102,8 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { if (lastReparentedRootTask != null) { if (toDisplayArea.isSplitScreenModeActivated() - && !lastReparentedRootTask.supportsSplitScreenWindowingMode()) { + && !lastReparentedRootTask.supportsSplitScreenWindowingModeInDisplayArea( + toDisplayArea)) { // Dismiss split screen if the last reparented root task doesn't support split mode. mAtmService.getTaskChangeNotificationController() .notifyActivityDismissingDockedRootTask(); diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 0bc799996abb..cc0471c30f5f 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -612,7 +612,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity, TaskDisplayArea displayArea) { - if (!activity.supportsFreeform() || activity.isResizeable()) { + if (!activity.supportsFreeformInDisplayArea(displayArea) || activity.isResizeable()) { return false; } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index a4c8db6f208f..cc8ee60ec8ba 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -38,7 +38,6 @@ import android.os.Binder; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; -import android.os.SystemProperties; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; @@ -76,9 +75,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private static final int REPORT_CONFIGS = CONTROLLABLE_CONFIGS; private static final int REPORT_WINDOW_CONFIGS = CONTROLLABLE_WINDOW_CONFIGS; - private static final boolean DEBUG_ENABLE_REVEAL_ANIMATION = - SystemProperties.getBoolean("persist.debug.enable_reveal_animation", false); - // The set of modes that are currently supports // TODO: Remove once the task organizer can support all modes @VisibleForTesting @@ -187,8 +183,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { SurfaceControl windowAnimationLeash = null; Rect mainFrame = null; final boolean playShiftUpAnimation = !task.inMultiWindowMode(); - if (prepareAnimation && playShiftUpAnimation && DEBUG_ENABLE_REVEAL_ANIMATION) { - final ActivityRecord topActivity = task.topActivityWithStartingWindow(); + if (prepareAnimation && playShiftUpAnimation) { + final ActivityRecord topActivity = task.topActivityContainsStartingWindow(); if (topActivity != null) { final WindowState mainWindow = topActivity.findMainWindow(false/* includeStartingApp */); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index c29211f3bb65..b7ad820bbdc4 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -302,92 +302,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } // Hierarchy changes final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); - if (!hops.isEmpty() && mService.isInLockTaskMode()) { - Slog.w(TAG, "Attempt to perform hierarchy operations while in lock task mode..."); - } else { - for (int i = 0, n = hops.size(); i < n; ++i) { - final WindowContainerTransaction.HierarchyOp hop = hops.get(i); - switch (hop.getType()) { - case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: { - final WindowContainer wc = WindowContainer.fromBinder( - hop.getContainer()); - final Task task = wc != null ? wc.asTask() : null; - if (task != null) { - task.getDisplayArea().setLaunchRootTask(task, - hop.getWindowingModes(), hop.getActivityTypes()); - } else { - throw new IllegalArgumentException( - "Cannot set non-task as launch root: " + wc); - } - break; - } - case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: { - final WindowContainer wc = WindowContainer.fromBinder( - hop.getContainer()); - final Task task = wc != null ? wc.asTask() : null; - if (task == null) { - throw new IllegalArgumentException("Cannot set " - + "non-task as launch root: " + wc); - } else if (!task.mCreatedByOrganizer) { - throw new UnsupportedOperationException("Cannot set " - + "non-organized task as adjacent flag root: " + wc); - } else if (task.mAdjacentTask == null) { - throw new UnsupportedOperationException("Cannot set " - + "non-adjacent task as adjacent flag root: " + wc); - } - - final boolean clearRoot = hop.getToTop(); - task.getDisplayArea() - .setLaunchAdjacentFlagRootTask(clearRoot ? null : task); - break; - } - case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: - effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); - break; - case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: - effects |= setAdjacentRootsHierarchyOp(hop); - break; - case HIERARCHY_OP_TYPE_REORDER: - case HIERARCHY_OP_TYPE_REPARENT: - final WindowContainer wc = WindowContainer.fromBinder( - hop.getContainer()); - if (wc == null || !wc.isAttached()) { - Slog.e(TAG, "Attempt to operate on detached container: " + wc); - continue; - } - if (syncId >= 0) { - addToSyncSet(syncId, wc); - } - if (transition != null) { - transition.collect(wc); - if (hop.isReparent()) { - if (wc.getParent() != null) { - // Collect the current parent. It's visibility may change as - // a result of this reparenting. - transition.collect(wc.getParent()); - } - if (hop.getNewParent() != null) { - final WindowContainer parentWc = - WindowContainer.fromBinder(hop.getNewParent()); - if (parentWc == null) { - Slog.e(TAG, "Can't resolve parent window from token"); - continue; - } - transition.collect(parentWc); - } - } - } - effects |= sanitizeAndApplyHierarchyOp(wc, hop); - break; - case HIERARCHY_OP_TYPE_LAUNCH_TASK: - Bundle launchOpts = hop.getLaunchOptions(); - int taskId = launchOpts.getInt( - WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); - launchOpts.remove( - WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); - mService.startActivityFromRecents(taskId, launchOpts); - break; - } + final int hopSize = hops.size(); + if (hopSize > 0) { + final boolean isInLockTaskMode = mService.isInLockTaskMode(); + for (int i = 0; i < hopSize; ++i) { + effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition, + isInLockTaskMode); } } // Queue-up bounds-change transactions for tasks which are now organized. Do @@ -541,6 +461,96 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return effects[0]; } + private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects, + int syncId, @Nullable Transition transition, boolean isInLockTaskMode) { + final int type = hop.getType(); + switch (type) { + case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: { + final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); + final Task task = wc != null ? wc.asTask() : null; + if (task != null) { + task.getDisplayArea().setLaunchRootTask(task, + hop.getWindowingModes(), hop.getActivityTypes()); + } else { + throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc); + } + break; + } + case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: { + final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); + final Task task = wc != null ? wc.asTask() : null; + if (task == null) { + throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc); + } else if (!task.mCreatedByOrganizer) { + throw new UnsupportedOperationException( + "Cannot set non-organized task as adjacent flag root: " + wc); + } else if (task.mAdjacentTask == null) { + throw new UnsupportedOperationException( + "Cannot set non-adjacent task as adjacent flag root: " + wc); + } + + final boolean clearRoot = hop.getToTop(); + task.getDisplayArea().setLaunchAdjacentFlagRootTask(clearRoot ? null : task); + break; + } + case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: + effects |= setAdjacentRootsHierarchyOp(hop); + break; + } + // The following operations may change task order so they are skipped while in lock task + // mode. The above operations are still allowed because they don't move tasks. And it may + // be necessary such as clearing launch root after entering lock task mode. + if (isInLockTaskMode) { + Slog.w(TAG, "Skip applying hierarchy operation " + hop + " while in lock task mode"); + return effects; + } + + switch (type) { + case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: + effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); + break; + case HIERARCHY_OP_TYPE_REORDER: + case HIERARCHY_OP_TYPE_REPARENT: + final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); + if (wc == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to operate on detached container: " + wc); + break; + } + if (syncId >= 0) { + addToSyncSet(syncId, wc); + } + if (transition != null) { + transition.collect(wc); + if (hop.isReparent()) { + if (wc.getParent() != null) { + // Collect the current parent. It's visibility may change as + // a result of this reparenting. + transition.collect(wc.getParent()); + } + if (hop.getNewParent() != null) { + final WindowContainer parentWc = + WindowContainer.fromBinder(hop.getNewParent()); + if (parentWc == null) { + Slog.e(TAG, "Can't resolve parent window from token"); + break; + } + transition.collect(parentWc); + } + } + } + effects |= sanitizeAndApplyHierarchyOp(wc, hop); + break; + case HIERARCHY_OP_TYPE_LAUNCH_TASK: + final Bundle launchOpts = hop.getLaunchOptions(); + final int taskId = launchOpts.getInt( + WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); + launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); + mService.startActivityFromRecents(taskId, launchOpts); + break; + } + return effects; + } + private int sanitizeAndApplyHierarchyOp(WindowContainer container, WindowContainerTransaction.HierarchyOp hop) { final Task task = container.asTask(); @@ -569,14 +579,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (newParent.asTaskDisplayArea() != null) { // For now, reparenting to displayarea is different from other reparents... as.reparent(newParent.asTaskDisplayArea(), hop.getToTop()); - } else { + } else if (newParent.asTask() != null) { if (newParent.inMultiWindowMode() && task.isLeafTask()) { if (newParent.inPinnedWindowingMode()) { Slog.w(TAG, "Can't support moving a task to another PIP window..." + " newParent=" + newParent + " task=" + task); return 0; } - if (!task.supportsMultiWindow()) { + if (!task.supportsMultiWindowInDisplayArea( + newParent.asTask().getDisplayArea())) { Slog.w(TAG, "Can't support task that doesn't support multi-window" + " mode in multi-window mode... newParent=" + newParent + " task=" + task); @@ -586,6 +597,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub task.reparent((Task) newParent, hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, false /*moveParents*/, "sanitizeAndApplyHierarchyOp"); + } else { + throw new RuntimeException("Can only reparent task to another task or" + + " taskDisplayArea, but not " + newParent); } } else { final Task rootTask = (Task) ( @@ -641,6 +655,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } final boolean newParentInMultiWindow = newParent.inMultiWindowMode(); + final TaskDisplayArea newParentTda = newParent.asTask() != null + ? newParent.asTask().getDisplayArea() + : newParent.asTaskDisplayArea(); final WindowContainer finalCurrentParent = currentParent; Slog.i(TAG, "reparentChildrenTasksHierarchyOp" + " currentParent=" + currentParent + " newParent=" + newParent + " hop=" + hop); @@ -656,7 +673,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // are reparenting from. return; } - if (newParentInMultiWindow && !task.supportsMultiWindow()) { + if (newParentInMultiWindow && !task.supportsMultiWindowInDisplayArea(newParentTda)) { Slog.e(TAG, "reparentChildrenTasksHierarchyOp non-resizeable task to multi window," + " task=" + task); return; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 540035fb6c97..4eff18cd5622 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -751,11 +751,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET; /** - * This is the frame rate which is passed to SurfaceFlinger if the window is part of the - * high refresh rate deny list. The variable is cached, so we do not send too many updates to - * SF. + * This is the frame rate which is passed to SurfaceFlinger if the window set a + * preferredDisplayModeId or is part of the high refresh rate deny list. + * The variable is cached, so we do not send too many updates to SF. */ - float mDenyListFrameRate = 0f; + float mAppPreferredFrameRate = 0f; static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */ @@ -2412,14 +2412,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } if (startingWindow && StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) { - // cancel the remove starting window animation on shell + // Cancel the remove starting window animation on shell. The main window might changed + // during animating, checking for all windows would be safer. if (mActivityRecord != null) { - final WindowState mainWindow = - mActivityRecord.findMainWindow(false/* includeStartingApp */); - if (mainWindow != null && mainWindow.isSelfAnimating(0 /* flags */, - ANIMATION_TYPE_STARTING_REVEAL)) { - mainWindow.cancelAnimation(); - } + mActivityRecord.forAllWindows(w -> { + if (w.isSelfAnimating(0, ANIMATION_TYPE_STARTING_REVEAL)) { + w.cancelAnimation(); + } + }, true); } } @@ -2682,6 +2682,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } + /** + * Move the touch gesture from the currently touched window on this display to this window. + */ + public boolean transferTouch() { + return mWmService.mInputManager.transferTouch(mInputChannelToken); + } + void disposeInputChannel() { if (mDeadWindowEventReceiver != null) { mDeadWindowEventReceiver.dispose(); @@ -5409,10 +5416,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } final float refreshRate = refreshRatePolicy.getPreferredRefreshRate(this); - if (mDenyListFrameRate != refreshRate) { - mDenyListFrameRate = refreshRate; + if (mAppPreferredFrameRate != refreshRate) { + mAppPreferredFrameRate = refreshRate; getPendingTransaction().setFrameRate( - mSurfaceControl, mDenyListFrameRate, Surface.FRAME_RATE_COMPATIBILITY_EXACT); + mSurfaceControl, mAppPreferredFrameRate, + Surface.FRAME_RATE_COMPATIBILITY_EXACT, Surface.CHANGE_FRAME_RATE_ALWAYS); } } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index cca62b9a7274..eb9c8dbc7e6b 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -1834,8 +1834,19 @@ static jboolean nativeTransferTouchFocus(JNIEnv* env, jclass /* clazz */, jlong } } -static void nativeSetPointerSpeed(JNIEnv* /* env */, - jclass /* clazz */, jlong ptr, jint speed) { +static jboolean nativeTransferTouch(JNIEnv* env, jclass /* clazz */, jlong ptr, + jobject destChannelTokenObj) { + sp<IBinder> destChannelToken = ibinderForJavaObject(env, destChannelTokenObj); + + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + if (im->getInputManager()->getDispatcher()->transferTouch(destChannelToken)) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } +} + +static void nativeSetPointerSpeed(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint speed) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); im->setPointerSpeed(speed); @@ -2308,6 +2319,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"nativeSetSystemUiLightsOut", "(JZ)V", (void*)nativeSetSystemUiLightsOut}, {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;Z)Z", (void*)nativeTransferTouchFocus}, + {"nativeTransferTouch", "(JLandroid/os/IBinder;)Z", (void*)nativeTransferTouch}, {"nativeSetPointerSpeed", "(JI)V", (void*)nativeSetPointerSpeed}, {"nativeSetShowTouches", "(JZ)V", (void*)nativeSetShowTouches}, {"nativeSetInteractive", "(JZ)V", (void*)nativeSetInteractive}, 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 92e4ec9d2e8b..00246dd21ed9 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 @@ -61,8 +61,6 @@ import android.location.ILocationListener; import android.location.LastLocationRequest; import android.location.Location; import android.location.LocationManagerInternal; -import android.location.LocationManagerInternal.LocationTagInfo; -import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener; import android.location.LocationManagerInternal.ProviderEnabledListener; import android.location.LocationRequest; import android.location.LocationResult; @@ -134,6 +132,8 @@ public class LocationProviderManagerTest { private Random mRandom; @Mock + private LocationProviderManager.StateChangedListener mStateChangedListener; + @Mock private LocationManagerInternal mInternal; @Mock private Context mContext; @@ -167,14 +167,14 @@ public class LocationProviderManagerTest { mInjector.getUserInfoHelper().startUser(OTHER_USER); mPassive = new PassiveLocationProviderManager(mContext, mInjector); - mPassive.startManager(); + mPassive.startManager(null); mPassive.setRealProvider(new PassiveLocationProvider(mContext)); mProvider = new TestProvider(PROPERTIES, PROVIDER_IDENTITY); mProvider.setProviderAllowed(true); mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive); - mManager.startManager(); + mManager.startManager(mStateChangedListener); mManager.setRealProvider(mProvider); } @@ -219,18 +219,18 @@ public class LocationProviderManagerTest { } @Test - public void testAttributionTags() { - OnProviderLocationTagsChangeListener listener = mock( - OnProviderLocationTagsChangeListener.class); - mManager.setOnProviderLocationTagsChangeListener(listener); - + public void testStateChangedListener() { mProvider.setExtraAttributionTags(Collections.singleton("extra")); - ArgumentCaptor<LocationTagInfo> captor = ArgumentCaptor.forClass(LocationTagInfo.class); - verify(listener, times(2)).onLocationTagsChanged(captor.capture()); + ArgumentCaptor<AbstractLocationProvider.State> captorOld = ArgumentCaptor.forClass( + AbstractLocationProvider.State.class); + ArgumentCaptor<AbstractLocationProvider.State> captorNew = ArgumentCaptor.forClass( + AbstractLocationProvider.State.class); + verify(mStateChangedListener, timeout(TIMEOUT_MS).times(2)).onStateChanged(eq(NAME), + captorOld.capture(), captorNew.capture()); - assertThat(captor.getAllValues().get(0).getTags()).isEmpty(); - assertThat(captor.getAllValues().get(1).getTags()).containsExactly("extra", "attribution"); + assertThat(captorOld.getAllValues().get(1).extraAttributionTags).isEmpty(); + assertThat(captorNew.getAllValues().get(1).extraAttributionTags).containsExactly("extra"); } @Test 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 b36720303a55..0dcc069e20ff 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 @@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.testng.Assert.expectThrows; +import android.app.appsearch.AppSearchResult; import android.app.appsearch.AppSearchSchema; import android.app.appsearch.GenericDocument; import android.app.appsearch.SearchResult; @@ -39,16 +40,19 @@ import android.util.ArraySet; import androidx.test.core.app.ApplicationProvider; import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter; +import com.android.server.appsearch.external.localstorage.stats.InitializeStats; import com.android.server.appsearch.external.localstorage.util.PrefixUtil; import com.android.server.appsearch.proto.DocumentProto; import com.android.server.appsearch.proto.GetOptimizeInfoResultProto; import com.android.server.appsearch.proto.PersistType; import com.android.server.appsearch.proto.PropertyConfigProto; import com.android.server.appsearch.proto.PropertyProto; +import com.android.server.appsearch.proto.PutResultProto; import com.android.server.appsearch.proto.SchemaProto; import com.android.server.appsearch.proto.SchemaTypeConfigProto; import com.android.server.appsearch.proto.SearchResultProto; import com.android.server.appsearch.proto.SearchSpecProto; +import com.android.server.appsearch.proto.StatusProto; import com.android.server.appsearch.proto.StringIndexingConfig; import com.android.server.appsearch.proto.TermMatchType; @@ -86,8 +90,6 @@ public class AppSearchImplTest { /*logger=*/ null); } - // TODO(b/175430168) add test to verify reset is working properly. - /** * Ensure that we can rewrite an incoming schema type by adding the database as a prefix. While * also keeping any other existing schema types that may already be part of Icing's persisted @@ -486,6 +488,142 @@ public class AppSearchImplTest { } @Test + public void testReset() throws Exception { + // Setup the index + Context context = ApplicationProvider.getApplicationContext(); + File appsearchDir = mTemporaryFolder.newFolder(); + AppSearchImpl appSearchImpl = + AppSearchImpl.create( + appsearchDir, + context, + VisibilityStore.NO_OP_USER_ID, + /*globalQuerierPackage=*/ "", + /*logger=*/ null); + + // Insert schema + List<AppSearchSchema> schemas = + ImmutableList.of( + new AppSearchSchema.Builder("Type1").build(), + new AppSearchSchema.Builder("Type2").build()); + appSearchImpl.setSchema( + context.getPackageName(), + "database1", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*forceOverride=*/ false, + /*version=*/ 0); + + // Insert a valid doc + GenericDocument validDoc = + new GenericDocument.Builder<>("namespace1", "id1", "Type1").build(); + appSearchImpl.putDocument( + context.getPackageName(), "database1", validDoc, /*logger=*/ null); + + // Query it via global query. We use the same code again later so this is to make sure we + // have our global query configured right. + SearchResultPage results = + appSearchImpl.globalQuery( + /*queryExpression=*/ "", + new SearchSpec.Builder().addFilterSchemas("Type1").build(), + context.getPackageName(), + VisibilityStore.NO_OP_USER_ID, + /*logger=*/ null); + assertThat(results.getResults()).hasSize(1); + assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc); + + // Create a doc with a malformed namespace + DocumentProto invalidDoc = + DocumentProto.newBuilder() + .setNamespace("invalidNamespace") + .setUri("id2") + .setSchema(context.getPackageName() + "$database1/Type1") + .build(); + AppSearchException e = + expectThrows( + AppSearchException.class, + () -> PrefixUtil.getPrefix(invalidDoc.getNamespace())); + assertThat(e) + .hasMessageThat() + .isEqualTo( + "The prefixed value \"invalidNamespace\" doesn't contain a valid database" + + " name"); + + // Insert the invalid doc with an invalid namespace right into icing + PutResultProto putResultProto = appSearchImpl.mIcingSearchEngineLocked.put(invalidDoc); + assertThat(putResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.OK); + + // Create a logger for capturing initialization to make sure we are logging the recovery + // process correctly. + AppSearchLoggerTest.TestLogger testLogger = new AppSearchLoggerTest.TestLogger(); + + // Initialize AppSearchImpl. This should cause a reset. + appSearchImpl.close(); + appSearchImpl = + AppSearchImpl.create( + appsearchDir, + context, + VisibilityStore.NO_OP_USER_ID, + /*globalQuerierPackage=*/ context.getPackageName(), + testLogger); + + // Check recovery state + InitializeStats initStats = testLogger.mInitializeStats; + assertThat(initStats).isNotNull(); + assertThat(initStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR); + assertThat(initStats.hasDeSync()).isFalse(); + assertThat(initStats.getDocumentStoreRecoveryCause()) + .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE); + // TODO(b/187879464): There should not be a recovery here, but icing lib reports one if the + // doc had no tokens. Once the mentioned bug is fixed, uncomment this. + // assertThat(initStats.getIndexRestorationCause()) + // .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE); + assertThat(initStats.getSchemaStoreRecoveryCause()) + .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE); + assertThat(initStats.getDocumentStoreDataStatus()) + .isEqualTo(InitializeStats.DOCUMENT_STORE_DATA_STATUS_NO_DATA_LOSS); + assertThat(initStats.hasReset()).isTrue(); + assertThat(initStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_OK); + + // Make sure all our data is gone + assertThat(appSearchImpl.getSchema(context.getPackageName(), "database1").getSchemas()) + .isEmpty(); + results = + appSearchImpl.globalQuery( + /*queryExpression=*/ "", + new SearchSpec.Builder().addFilterSchemas("Type1").build(), + context.getPackageName(), + VisibilityStore.NO_OP_USER_ID, + /*logger=*/ null); + assertThat(results.getResults()).isEmpty(); + + // Make sure the index can now be used successfully + appSearchImpl.setSchema( + context.getPackageName(), + "database1", + Collections.singletonList(new AppSearchSchema.Builder("Type1").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*forceOverride=*/ false, + /*version=*/ 0); + + // Insert a valid doc + appSearchImpl.putDocument( + context.getPackageName(), "database1", validDoc, /*logger=*/ null); + + // Query it via global query. + results = + appSearchImpl.globalQuery( + /*queryExpression=*/ "", + new SearchSpec.Builder().addFilterSchemas("Type1").build(), + context.getPackageName(), + VisibilityStore.NO_OP_USER_ID, + /*logger=*/ null); + assertThat(results.getResults()).hasSize(1); + assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc); + } + + @Test public void testRewriteSearchSpec_oneInstance() throws Exception { SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery(""); 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 2aad275ba62e..0e5c77c5182c 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 @@ -68,7 +68,7 @@ public class AppSearchLoggerTest { } // Test only not thread safe. - public class TestLogger implements AppSearchLogger { + public static class TestLogger implements AppSearchLogger { @Nullable CallStats mCallStats; @Nullable PutDocumentStats mPutDocumentStats; @Nullable InitializeStats mInitializeStats; 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 2d039810446a..edb683bf92f0 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 @@ -168,7 +168,9 @@ public class AppSearchStatsTest { .setSchemaStoreRecoveryLatencyMillis(nativeSchemaStoreRecoveryLatencyMillis) .setDocumentStoreDataStatus(nativeDocumentStoreDataStatus) .setDocumentCount(nativeNumDocuments) - .setSchemaTypeCount(nativeNumSchemaTypes); + .setSchemaTypeCount(nativeNumSchemaTypes) + .setHasReset(true) + .setResetStatusCode(AppSearchResult.RESULT_INVALID_SCHEMA); final InitializeStats iStats = iStatsBuilder.build(); assertThat(iStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE); @@ -192,6 +194,8 @@ public class AppSearchStatsTest { assertThat(iStats.getDocumentStoreDataStatus()).isEqualTo(nativeDocumentStoreDataStatus); assertThat(iStats.getDocumentCount()).isEqualTo(nativeNumDocuments); assertThat(iStats.getSchemaTypeCount()).isEqualTo(nativeNumSchemaTypes); + assertThat(iStats.hasReset()).isTrue(); + assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_INVALID_SCHEMA); } @Test diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java new file mode 100644 index 000000000000..b8fbe34a7dcb --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.face.aidl; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.hardware.biometrics.IBiometricService; +import android.hardware.biometrics.face.ISession; +import android.os.Handler; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import com.android.server.biometrics.sensors.LockoutCache; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.UserAwareBiometricScheduler; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; + +@Presubmit +@SmallTest +public class SensorTest { + + private static final String TAG = "SensorTest"; + private static final int USER_ID = 2; + private static final int SENSOR_ID = 4; + private static final byte[] HAT = new byte[69]; + + @Mock + private Context mContext; + @Mock + private IBiometricService mBiometricService; + @Mock + private ISession mSession; + @Mock + private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback; + @Mock + private Sensor.HalSessionCallback.Callback mHalSessionCallback; + @Mock + private LockoutResetDispatcher mLockoutResetDispatcher; + + private final TestLooper mLooper = new TestLooper(); + private final LockoutCache mLockoutCache = new LockoutCache(); + + private UserAwareBiometricScheduler mScheduler; + private Sensor.HalSessionCallback mHalCallback; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService); + + mScheduler = new UserAwareBiometricScheduler(TAG, + null /* gestureAvailabilityDispatcher */, + () -> USER_ID, + mUserSwitchCallback); + mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()), + TAG, mScheduler, SENSOR_ID, + USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback); + } + + @Test + public void halSessionCallback_respondsToResetLockout() throws Exception { + doAnswer((Answer<Void>) invocationOnMock -> { + mHalCallback.onLockoutCleared(); + return null; + }).when(mSession).resetLockout(any()); + mLockoutCache.setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_TIMED); + + mScheduler.scheduleClientMonitor(new FaceResetLockoutClient(mContext, + () -> mSession, USER_ID, TAG, SENSOR_ID, HAT, mLockoutCache, + mLockoutResetDispatcher)); + mLooper.dispatchAll(); + + verifyNotLocked(); + } + + @Test + public void halSessionCallback_respondsToUnprovokedResetLockout() { + mLockoutCache.setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_TIMED); + + mHalCallback.onLockoutCleared(); + mLooper.dispatchAll(); + + verifyNotLocked(); + } + + private void verifyNotLocked() { + assertEquals(LockoutTracker.LOCKOUT_NONE, mLockoutCache.getLockoutModeForUser(USER_ID)); + verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java new file mode 100644 index 000000000000..5dfc24889815 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint.aidl; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.hardware.biometrics.IBiometricService; +import android.hardware.biometrics.fingerprint.ISession; +import android.os.Handler; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import com.android.server.biometrics.sensors.LockoutCache; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.UserAwareBiometricScheduler; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; + +@Presubmit +@SmallTest +public class SensorTest { + + private static final String TAG = "SensorTest"; + private static final int USER_ID = 2; + private static final int SENSOR_ID = 4; + private static final byte[] HAT = new byte[69]; + + @Mock + private Context mContext; + @Mock + private IBiometricService mBiometricService; + @Mock + private ISession mSession; + @Mock + private UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback; + @Mock + private Sensor.HalSessionCallback.Callback mHalSessionCallback; + @Mock + private LockoutResetDispatcher mLockoutResetDispatcher; + + private final TestLooper mLooper = new TestLooper(); + private final LockoutCache mLockoutCache = new LockoutCache(); + + private UserAwareBiometricScheduler mScheduler; + private Sensor.HalSessionCallback mHalCallback; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService); + + mScheduler = new UserAwareBiometricScheduler(TAG, + null /* gestureAvailabilityDispatcher */, + () -> USER_ID, + mUserSwitchCallback); + mHalCallback = new Sensor.HalSessionCallback(mContext, new Handler(mLooper.getLooper()), + TAG, mScheduler, SENSOR_ID, + USER_ID, mLockoutCache, mLockoutResetDispatcher, mHalSessionCallback); + } + + @Test + public void halSessionCallback_respondsToResetLockout() throws Exception { + doAnswer((Answer<Void>) invocationOnMock -> { + mHalCallback.onLockoutCleared(); + return null; + }).when(mSession).resetLockout(any()); + mLockoutCache.setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_TIMED); + + mScheduler.scheduleClientMonitor(new FingerprintResetLockoutClient(mContext, + () -> mSession, USER_ID, TAG, SENSOR_ID, HAT, mLockoutCache, + mLockoutResetDispatcher)); + mLooper.dispatchAll(); + + verifyNotLocked(); + } + + @Test + public void halSessionCallback_respondsToUnprovokedResetLockout() { + mLockoutCache.setLockoutModeForUser(USER_ID, LockoutTracker.LOCKOUT_TIMED); + + mHalCallback.onLockoutCleared(); + mLooper.dispatchAll(); + + verifyNotLocked(); + } + + private void verifyNotLocked() { + assertEquals(LockoutTracker.LOCKOUT_NONE, mLockoutCache.getLockoutModeForUser(USER_ID)); + verify(mLockoutResetDispatcher).notifyLockoutResetCallbacks(eq(SENSOR_ID)); + } +} 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 cda659f05adf..fb2db2259c7e 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -23,10 +23,11 @@ import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REF import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE; -import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICKER; +import static com.android.server.display.DisplayModeDirector.Vote.INVALID_SIZE; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; @@ -137,7 +138,14 @@ public class DisplayModeDirectorTest { } } assertThat(defaultMode).isNotNull(); + return createDirectorFromModeArray(modes, defaultMode); + } + private DisplayModeDirector createDirectorFromModeArray(Display.Mode[] modes, + Display.Mode defaultMode) { + DisplayModeDirector director = + new DisplayModeDirector(mContext, mHandler, mInjector); + director.setLoggingEnabled(true); SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>(); supportedModesByDisplay.put(DISPLAY_ID, modes); director.injectSupportedModesByDisplay(supportedModesByDisplay); @@ -218,8 +226,9 @@ public class DisplayModeDirectorTest { votesByDisplay.put(DISPLAY_ID, votes); float error = FLOAT_TOLERANCE / 4; votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(0, 60)); - votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forRefreshRates(60 + error, 60 + error)); - votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, + Vote.forRefreshRates(60 + error, 60 + error)); + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forRefreshRates(60 - error, 60 - error)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); @@ -230,44 +239,159 @@ public class DisplayModeDirectorTest { } @Test - public void testFlickerHasLowerPriorityThanUser() { - assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE); - assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_SIZE); - - DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + public void testFlickerHasLowerPriorityThanUserAndRangeIsSingle() { + assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE + < Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); + assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE + < Vote.PRIORITY_APP_REQUEST_SIZE); + + assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH + > Vote.PRIORITY_LOW_POWER_MODE); + + Display.Mode[] modes = new Display.Mode[4]; + modes[0] = new Display.Mode( + /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60); + modes[1] = new Display.Mode( + /*modeId=*/2, /*width=*/2000, /*height=*/2000, 60); + modes[2] = new Display.Mode( + /*modeId=*/3, /*width=*/1000, /*height=*/1000, 90); + modes[3] = new Display.Mode( + /*modeId=*/4, /*width=*/2000, /*height=*/2000, 90); + + DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); - votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90)); - votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60)); + Display.Mode appRequestedMode = modes[1]; + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), + appRequestedMode.getPhysicalHeight())); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(2); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primaryRefreshRateRange.max) + .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primaryRefreshRateRange.min); votes.clear(); - votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90)); - votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90)); + appRequestedMode = modes[3]; + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), + appRequestedMode.getPhysicalHeight())); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(90, 90)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(4); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.max) + .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primaryRefreshRateRange.min); votes.clear(); - votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90)); - votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60)); + appRequestedMode = modes[3]; + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), + appRequestedMode.getPhysicalHeight())); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(4); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.max) + .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primaryRefreshRateRange.min); + + votes.clear(); + appRequestedMode = modes[1]; + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), + appRequestedMode.getPhysicalHeight())); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(90, 90)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(2); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primaryRefreshRateRange.max) + .isWithin(FLOAT_TOLERANCE).of(desiredSpecs.primaryRefreshRateRange.min); + } + + @Test + public void testLPMHasHigherPriorityThanUser() { + assertTrue(Vote.PRIORITY_LOW_POWER_MODE > Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); + assertTrue(Vote.PRIORITY_LOW_POWER_MODE > Vote.PRIORITY_APP_REQUEST_SIZE); + + + Display.Mode[] modes = new Display.Mode[4]; + modes[0] = new Display.Mode( + /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60); + modes[1] = new Display.Mode( + /*modeId=*/2, /*width=*/2000, /*height=*/2000, 60); + modes[2] = new Display.Mode( + /*modeId=*/3, /*width=*/1000, /*height=*/1000, 90); + modes[3] = new Display.Mode( + /*modeId=*/4, /*width=*/2000, /*height=*/2000, 90); + + DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(DISPLAY_ID, votes); + Display.Mode appRequestedMode = modes[1]; + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), + appRequestedMode.getPhysicalHeight())); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(60, 60)); + director.injectVotesByDisplay(votesByDisplay); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(2); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + + votes.clear(); + appRequestedMode = modes[3]; + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), + appRequestedMode.getPhysicalHeight())); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(90, 90)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(4); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); votes.clear(); - votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60)); - votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90)); + appRequestedMode = modes[3]; + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), + appRequestedMode.getPhysicalHeight())); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(2); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + + votes.clear(); + appRequestedMode = modes[1]; + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), + appRequestedMode.getPhysicalHeight())); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(90, 90)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(4); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); } @Test @@ -275,19 +399,30 @@ public class DisplayModeDirectorTest { // Confirm that the app request range doesn't include flicker or min refresh rate settings, // but does include everything else. assertTrue( - PRIORITY_FLICKER < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); + Vote.PRIORITY_FLICKER_REFRESH_RATE + < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); assertTrue(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); - assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE + assertTrue(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); - DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + Display.Mode[] modes = new Display.Mode[3]; + modes[0] = new Display.Mode( + /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60); + modes[1] = new Display.Mode( + /*modeId=*/75, /*width=*/2000, /*height=*/2000, 75); + modes[2] = new Display.Mode( + /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90); + + DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]); SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); - votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(60); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); @@ -297,22 +432,24 @@ public class DisplayModeDirectorTest { Vote.forRefreshRates(90, Float.POSITIVE_INFINITY)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(90); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f); assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); - votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(75, 75)); + Display.Mode appRequestedMode = modes[1]; + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), + appRequestedMode.getPhysicalHeight())); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(75); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75); assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75); - assertThat(desiredSpecs.appRequestRefreshRateRange.min) - .isWithin(FLOAT_TOLERANCE) - .of(75); - assertThat(desiredSpecs.appRequestRefreshRateRange.max) - .isWithin(FLOAT_TOLERANCE) - .of(75); + assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); + assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); } void verifySpecsWithRefreshRateSettings(DisplayModeDirector director, float minFps, @@ -374,18 +511,29 @@ public class DisplayModeDirectorTest { @Test public void testVotingWithAlwaysRespectAppRequest() { - DisplayModeDirector director = createDirectorFromFpsRange(50, 90); + Display.Mode[] modes = new Display.Mode[3]; + modes[0] = new Display.Mode( + /*modeId=*/50, /*width=*/1000, /*height=*/1000, 50); + modes[1] = new Display.Mode( + /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60); + modes[2] = new Display.Mode( + /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90); + + DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]); + + SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); - votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(0, 60)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(0, 60)); votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(60, 90)); - votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90)); + Display.Mode appRequestedMode = modes[2]; + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(60, 60)); votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); - - director.injectVotesByDisplay(votesByDisplay); + assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse(); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); @@ -396,8 +544,8 @@ public class DisplayModeDirectorTest { director.setShouldAlwaysRespectAppRequestedMode(true); assertThat(director.shouldAlwaysRespectAppRequestedMode()).isTrue(); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); - assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isAtMost(50); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90); assertThat(desiredSpecs.baseModeId).isEqualTo(90); director.setShouldAlwaysRespectAppRequestedMode(false); @@ -497,7 +645,8 @@ public class DisplayModeDirectorTest { // Inject votes SparseArray<Vote> votes = new SparseArray<>(); votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(1920, 1080)); - votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(50, 50)); + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(60)); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID, votes); director.injectVotesByDisplay(votesByDisplay); @@ -592,14 +741,19 @@ public class DisplayModeDirectorTest { // Sensor reads 20 lux, listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/)); - Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER); + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); assertVoteForRefreshRate(vote, 90 /*fps*/); + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH); + assertThat(vote).isNotNull(); + assertThat(vote.disableRefreshRateSwitching).isTrue(); setBrightness(125); // Sensor reads 1000 lux, listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/)); - vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER); + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); + assertThat(vote).isNull(); + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH); assertThat(vote).isNull(); } @@ -635,15 +789,20 @@ public class DisplayModeDirectorTest { // Sensor reads 2000 lux, listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000)); - Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER); + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); + assertThat(vote).isNull(); + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH); assertThat(vote).isNull(); setBrightness(255); // Sensor reads 9000 lux, listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000)); - vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER); + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); assertVoteForRefreshRate(vote, 60 /*fps*/); + vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH); + assertThat(vote).isNotNull(); + assertThat(vote.disableRefreshRateSwitching).isTrue(); } @Test @@ -724,6 +883,336 @@ public class DisplayModeDirectorTest { assertNull(vote); } + @Test + public void testAppRequestMaxRefreshRate() { + // Confirm that the app max request range doesn't include flicker or min refresh rate + // settings but does include everything else. + assertTrue(Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE + >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); + + Display.Mode[] modes = new Display.Mode[3]; + modes[0] = new Display.Mode( + /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60); + modes[1] = new Display.Mode( + /*modeId=*/75, /*width=*/1000, /*height=*/1000, 75); + modes[2] = new Display.Mode( + /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90); + + DisplayModeDirector director = createDirectorFromModeArray(modes, modes[1]); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(DISPLAY_ID, votes); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60)); + director.injectVotesByDisplay(votesByDisplay); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); + assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + + votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, + Vote.forRefreshRates(90, Float.POSITIVE_INFINITY)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f); + assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); + assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + + votes.put(Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, Vote.forRefreshRates(0, 75)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75); + assertThat(desiredSpecs.appRequestRefreshRateRange.min).isZero(); + assertThat(desiredSpecs.appRequestRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75); + } + + @Test + public void testAppRequestObserver_modeId() { + DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 0); + + Vote appRequestRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); + assertNotNull(appRequestRefreshRate); + assertThat(appRequestRefreshRate.refreshRateRange.min).isZero(); + assertThat(appRequestRefreshRate.refreshRateRange.max).isPositiveInfinity(); + assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse(); + assertThat(appRequestRefreshRate.baseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE); + + Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); + assertNotNull(appRequestSize); + assertThat(appRequestSize.refreshRateRange.min).isZero(); + assertThat(appRequestSize.refreshRateRange.max).isPositiveInfinity(); + assertThat(appRequestSize.disableRefreshRateSwitching).isFalse(); + assertThat(appRequestSize.baseModeRefreshRate).isZero(); + assertThat(appRequestSize.height).isEqualTo(1000); + assertThat(appRequestSize.width).isEqualTo(1000); + + Vote appRequestMaxRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE); + assertNull(appRequestMaxRefreshRate); + + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 90, 0); + + appRequestRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); + assertNotNull(appRequestRefreshRate); + assertThat(appRequestRefreshRate.refreshRateRange.min).isZero(); + assertThat(appRequestRefreshRate.refreshRateRange.max).isPositiveInfinity(); + assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse(); + assertThat(appRequestRefreshRate.baseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE); + + appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); + assertNotNull(appRequestSize); + assertThat(appRequestSize.refreshRateRange.min).isZero(); + assertThat(appRequestSize.refreshRateRange.max).isPositiveInfinity(); + assertThat(appRequestSize.height).isEqualTo(1000); + assertThat(appRequestSize.width).isEqualTo(1000); + + appRequestMaxRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE); + assertNull(appRequestMaxRefreshRate); + } + + @Test + public void testAppRequestObserver_maxRefreshRate() { + DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 90); + Vote appRequestRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); + assertNull(appRequestRefreshRate); + + Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); + assertNull(appRequestSize); + + Vote appRequestMaxRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE); + assertNotNull(appRequestMaxRefreshRate); + assertThat(appRequestMaxRefreshRate.refreshRateRange.min).isZero(); + assertThat(appRequestMaxRefreshRate.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(appRequestMaxRefreshRate.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestMaxRefreshRate.width).isEqualTo(INVALID_SIZE); + + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 60); + appRequestRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); + assertNull(appRequestRefreshRate); + + appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); + assertNull(appRequestSize); + + appRequestMaxRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE); + assertNotNull(appRequestMaxRefreshRate); + assertThat(appRequestMaxRefreshRate.refreshRateRange.min).isZero(); + assertThat(appRequestMaxRefreshRate.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(appRequestMaxRefreshRate.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestMaxRefreshRate.width).isEqualTo(INVALID_SIZE); + } + + @Test + public void testAppRequestObserver_modeIdAndMaxRefreshRate() { + DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 90); + + Vote appRequestRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); + assertNotNull(appRequestRefreshRate); + assertThat(appRequestRefreshRate.refreshRateRange.min).isZero(); + assertThat(appRequestRefreshRate.refreshRateRange.max).isPositiveInfinity(); + assertThat(appRequestRefreshRate.disableRefreshRateSwitching).isFalse(); + assertThat(appRequestRefreshRate.baseModeRefreshRate).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE); + + Vote appRequestSize = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); + assertNotNull(appRequestSize); + assertThat(appRequestSize.refreshRateRange.min).isZero(); + assertThat(appRequestSize.refreshRateRange.max).isPositiveInfinity(); + assertThat(appRequestSize.height).isEqualTo(1000); + assertThat(appRequestSize.width).isEqualTo(1000); + + Vote appRequestMaxRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE); + assertNotNull(appRequestMaxRefreshRate); + assertThat(appRequestMaxRefreshRate.refreshRateRange.min).isZero(); + assertThat(appRequestMaxRefreshRate.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(appRequestMaxRefreshRate.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestMaxRefreshRate.width).isEqualTo(INVALID_SIZE); + } + + @Test + public void testAppRequestsIsTheDefaultMode() { + Display.Mode[] modes = new Display.Mode[2]; + modes[0] = new Display.Mode( + /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60); + modes[1] = new Display.Mode( + /*modeId=*/2, /*width=*/1000, /*height=*/1000, 90); + + DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(1); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isAtMost(60); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90); + + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(DISPLAY_ID, votes); + Display.Mode appRequestedMode = modes[1]; + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), + appRequestedMode.getPhysicalHeight())); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(2); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isAtMost(60); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90); + } + + @Test + public void testDisableRefreshRateSwitchingVote() { + DisplayModeDirector director = createDirectorFromFpsRange(50, 90); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(DISPLAY_ID, votes); + votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, + Vote.forRefreshRates(90, Float.POSITIVE_INFINITY)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); + director.injectVotesByDisplay(votesByDisplay); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(50); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(50); + assertThat(desiredSpecs.baseModeId).isEqualTo(50); + + votes.clear(); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, + Vote.forRefreshRates(70, Float.POSITIVE_INFINITY)); + votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, + Vote.forRefreshRates(80, Float.POSITIVE_INFINITY)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 90)); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(80); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(80); + assertThat(desiredSpecs.baseModeId).isEqualTo(80); + + votes.clear(); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, + Vote.forRefreshRates(90, Float.POSITIVE_INFINITY)); + votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, + Vote.forRefreshRates(80, Float.POSITIVE_INFINITY)); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 90)); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.baseModeId).isEqualTo(90); + } + + @Test + public void testBaseModeIdInPrimaryRange() { + DisplayModeDirector director = createDirectorFromFpsRange(50, 90); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(DISPLAY_ID, votes); + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(70)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); + director.injectVotesByDisplay(votesByDisplay); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.baseModeId).isEqualTo(50); + + votes.clear(); + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(55)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.baseModeId).isEqualTo(55); + + votes.clear(); + votes.put(Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, Vote.forRefreshRates(0, 52)); + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(55)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.baseModeId).isEqualTo(55); + + votes.clear(); + votes.put(Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, Vote.forRefreshRates(0, 58)); + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(55)); + votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(0); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(58); + assertThat(desiredSpecs.baseModeId).isEqualTo(55); + } + + @Test + public void testStaleAppVote() { + Display.Mode[] modes = new Display.Mode[4]; + modes[0] = new Display.Mode( + /*modeId=*/1, /*width=*/1000, /*height=*/1000, 60); + modes[1] = new Display.Mode( + /*modeId=*/2, /*width=*/2000, /*height=*/2000, 60); + modes[2] = new Display.Mode( + /*modeId=*/3, /*width=*/1000, /*height=*/1000, 90); + modes[3] = new Display.Mode( + /*modeId=*/4, /*width=*/2000, /*height=*/2000, 90); + + DisplayModeDirector director = createDirectorFromModeArray(modes, modes[0]); + SparseArray<Vote> votes = new SparseArray<>(); + SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(DISPLAY_ID, votes); + Display.Mode appRequestedMode = modes[3]; + votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, + Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate())); + votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(), + appRequestedMode.getPhysicalHeight())); + director.injectVotesByDisplay(votesByDisplay); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(4); + + // Change mode Id's to simulate that a hotplug has occurred. + Display.Mode[] newModes = new Display.Mode[4]; + newModes[0] = new Display.Mode( + /*modeId=*/5, /*width=*/1000, /*height=*/1000, 60); + newModes[1] = new Display.Mode( + /*modeId=*/6, /*width=*/2000, /*height=*/2000, 60); + newModes[2] = new Display.Mode( + /*modeId=*/7, /*width=*/1000, /*height=*/1000, 90); + newModes[3] = new Display.Mode( + /*modeId=*/8, /*width=*/2000, /*height=*/2000, 90); + + SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>(); + supportedModesByDisplay.put(DISPLAY_ID, newModes); + director.injectSupportedModesByDisplay(supportedModesByDisplay); + SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>(); + defaultModesByDisplay.put(DISPLAY_ID, newModes[0]); + director.injectDefaultModeByDisplay(defaultModesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.baseModeId).isEqualTo(8); + } + private void assertVoteForRefreshRate(Vote vote, float refreshRate) { assertThat(vote).isNotNull(); final DisplayModeDirector.RefreshRateRange expectedRange = diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java index 5caff3d01825..c862febbd62b 100644 --- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java +++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java @@ -92,7 +92,7 @@ public final class UpdatableFontDirTest { } @Override - public void tryToCreateTypeface(File file) throws IOException { + public void tryToCreateTypeface(File file) throws Throwable { } } @@ -139,9 +139,11 @@ public final class UpdatableFontDirTest { private File mUpdatableFontFilesDir; private File mConfigFile; private List<File> mPreinstalledFontDirs; - private Supplier<Long> mCurrentTimeSupplier = () -> CURRENT_TIME; - private Function<Map<String, File>, FontConfig> mConfigSupplier = + private final Supplier<Long> mCurrentTimeSupplier = () -> CURRENT_TIME; + private final Function<Map<String, File>, FontConfig> mConfigSupplier = (map) -> SystemFonts.getSystemFontConfig(map, 0, 0); + private FakeFontFileParser mParser; + private FakeFsverityUtil mFakeFsverityUtil; @SuppressWarnings("ResultOfMethodCallIgnored") @Before @@ -159,6 +161,8 @@ public final class UpdatableFontDirTest { dir.mkdir(); } mConfigFile = new File(mCacheDir, "config.xml"); + mParser = new FakeFontFileParser(); + mFakeFsverityUtil = new FakeFsverityUtil(); } @After @@ -169,13 +173,11 @@ public final class UpdatableFontDirTest { @Test public void construct() throws Exception { long expectedModifiedDate = CURRENT_TIME / 2; - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); config.lastModifiedMillis = expectedModifiedDate; writeConfig(config, mConfigFile); UpdatableFontDir dirForPreparation = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dirForPreparation.loadFontFileMap(); assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis()) @@ -198,13 +200,13 @@ public final class UpdatableFontDirTest { .isNotEqualTo(expectedModifiedDate); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); assertThat(dir.getPostScriptMap()).containsKey("foo"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(3); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(3); assertThat(dir.getPostScriptMap()).containsKey("bar"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4); // Outdated font dir should be deleted. assertThat(mUpdatableFontFilesDir.list()).hasLength(2); assertNamedFamilyExists(dir.getSystemFontConfig(), "foobar"); @@ -219,10 +221,8 @@ public final class UpdatableFontDirTest { @Test public void construct_empty() { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); assertThat(dir.getPostScriptMap()).isEmpty(); @@ -231,10 +231,8 @@ public final class UpdatableFontDirTest { @Test public void construct_missingFsverity() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dirForPreparation = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( @@ -249,10 +247,10 @@ public final class UpdatableFontDirTest { // Four font dirs are created. assertThat(mUpdatableFontFilesDir.list()).hasLength(4); - fakeFsverityUtil.remove( + mFakeFsverityUtil.remove( dirForPreparation.getPostScriptMap().get("foo").getAbsolutePath()); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); assertThat(dir.getPostScriptMap()).isEmpty(); @@ -263,10 +261,8 @@ public final class UpdatableFontDirTest { @Test public void construct_fontNameMismatch() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dirForPreparation = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( @@ -285,7 +281,7 @@ public final class UpdatableFontDirTest { FileUtils.stringToFile(dirForPreparation.getPostScriptMap().get("foo"), "bar,4"); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); assertThat(dir.getPostScriptMap()).isEmpty(); @@ -296,8 +292,6 @@ public final class UpdatableFontDirTest { @Test public void construct_olderThanPreinstalledFont() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); Function<Map<String, File>, FontConfig> configSupplier = (map) -> { FontConfig.Font fooFont = new FontConfig.Font( new File(mPreinstalledFontDirs.get(0), "foo.ttf"), null, "foo", @@ -314,7 +308,7 @@ public final class UpdatableFontDirTest { }; UpdatableFontDir dirForPreparation = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, configSupplier); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( @@ -334,14 +328,14 @@ public final class UpdatableFontDirTest { FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,1,bar"); FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,2,bar"); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, configSupplier); dir.loadFontFileMap(); // For foo.ttf, preinstalled font (revision 5) should be used. assertThat(dir.getPostScriptMap()).doesNotContainKey("foo"); // For bar.ttf, updated font (revision 4) should be used. assertThat(dir.getPostScriptMap()).containsKey("bar"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(4); // Outdated font dir should be deleted. // We don't delete bar.ttf in this case, because it's normal that OTA updates preinstalled // fonts. @@ -352,10 +346,8 @@ public final class UpdatableFontDirTest { @Test public void construct_failedToLoadConfig() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, new File("/dev/null"), mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); assertThat(dir.getPostScriptMap()).isEmpty(); @@ -364,10 +356,8 @@ public final class UpdatableFontDirTest { @Test public void construct_afterBatchFailure() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dirForPreparation = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dirForPreparation.loadFontFileMap(); dirForPreparation.update(Arrays.asList( @@ -389,12 +379,12 @@ public final class UpdatableFontDirTest { } UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); // The state should be rolled back as a whole if one of the update requests fail. assertThat(dir.getPostScriptMap()).containsKey("foo"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); assertThat(dir.getFontFamilyMap()).containsKey("foobar"); FontConfig.FontFamily foobar = dir.getFontFamilyMap().get("foobar"); assertThat(foobar.getFontList()).hasSize(1); @@ -404,10 +394,8 @@ public final class UpdatableFontDirTest { @Test public void loadFontFileMap_twice() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", @@ -420,17 +408,15 @@ public final class UpdatableFontDirTest { @Test public void installFontFile() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE))); assertThat(dir.getPostScriptMap()).containsKey("test"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1); File fontFile = dir.getPostScriptMap().get("test"); assertThat(Os.stat(fontFile.getAbsolutePath()).st_mode & 0777).isEqualTo(0644); File fontDir = fontFile.getParentFile(); @@ -439,10 +425,8 @@ public final class UpdatableFontDirTest { @Test public void installFontFile_upgrade() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); @@ -452,10 +436,10 @@ public final class UpdatableFontDirTest { dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2,test", GOOD_SIGNATURE))); assertThat(dir.getPostScriptMap()).containsKey("test"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2); assertThat(mapBeforeUpgrade).containsKey("test"); assertWithMessage("Older fonts should not be deleted until next loadFontFileMap") - .that(parser.getRevision(mapBeforeUpgrade.get("test"))).isEqualTo(1); + .that(mParser.getRevision(mapBeforeUpgrade.get("test"))).isEqualTo(1); // Check that updatedFontDirs is pruned. assertWithMessage("config.updatedFontDirs should only list latest active dirs") .that(readConfig(mConfigFile).updatedFontDirs) @@ -464,15 +448,13 @@ public final class UpdatableFontDirTest { @Test public void installFontFile_systemFontHasPSNameDifferentFromFileName() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); // Setup the environment that the system installed font file named "foo.ttf" has PostScript // name "bar". File file = new File(mPreinstalledFontDirs.get(0), "foo.ttf"); FileUtils.stringToFile(file, "foo.ttf,1,bar"); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, (map) -> { FontConfig.Font font = new FontConfig.Font( file, null, "bar", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), @@ -489,7 +471,7 @@ public final class UpdatableFontDirTest { GOOD_SIGNATURE))); assertThat(dir.getPostScriptMap()).containsKey("bar"); assertThat(dir.getPostScriptMap().size()).isEqualTo(1); - assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2); File fontFile = dir.getPostScriptMap().get("bar"); assertThat(Os.stat(fontFile.getAbsolutePath()).st_mode & 0777).isEqualTo(0644); File fontDir = fontFile.getParentFile(); @@ -498,10 +480,8 @@ public final class UpdatableFontDirTest { @Test public void installFontFile_sameVersion() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); @@ -510,15 +490,13 @@ public final class UpdatableFontDirTest { dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE))); assertThat(dir.getPostScriptMap()).containsKey("test"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1); } @Test public void installFontFile_downgrade() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); @@ -533,7 +511,7 @@ public final class UpdatableFontDirTest { } assertThat(dir.getPostScriptMap()).containsKey("test"); assertWithMessage("Font should not be downgraded to an older revision") - .that(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2); + .that(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2); // Check that updatedFontDirs is not updated. assertWithMessage("config.updatedFontDirs should only list latest active dirs") .that(readConfig(mConfigFile).updatedFontDirs) @@ -542,10 +520,8 @@ public final class UpdatableFontDirTest { @Test public void installFontFile_multiple() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); @@ -554,17 +530,15 @@ public final class UpdatableFontDirTest { dir.update(Collections.singletonList(newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE))); assertThat(dir.getPostScriptMap()).containsKey("foo"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); assertThat(dir.getPostScriptMap()).containsKey("bar"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2); } @Test public void installFontFile_batch() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); @@ -572,17 +546,15 @@ public final class UpdatableFontDirTest { newFontUpdateRequest("foo.ttf,1,foo", GOOD_SIGNATURE), newFontUpdateRequest("bar.ttf,2,bar", GOOD_SIGNATURE))); assertThat(dir.getPostScriptMap()).containsKey("foo"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); assertThat(dir.getPostScriptMap()).containsKey("bar"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("bar"))).isEqualTo(2); } @Test public void installFontFile_invalidSignature() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); @@ -600,46 +572,40 @@ public final class UpdatableFontDirTest { @Test public void installFontFile_preinstalled_upgrade() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1,test"); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2,test", GOOD_SIGNATURE))); assertThat(dir.getPostScriptMap()).containsKey("test"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(2); } @Test public void installFontFile_preinstalled_sameVersion() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1,test"); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1,test", GOOD_SIGNATURE))); assertThat(dir.getPostScriptMap()).containsKey("test"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("test"))).isEqualTo(1); } @Test public void installFontFile_preinstalled_downgrade() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); File file = new File(mPreinstalledFontDirs.get(0), "test.ttf"); FileUtils.stringToFile(file, "test.ttf,2,test"); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, (map) -> { FontConfig.Font font = new FontConfig.Font( file, null, "test", new FontStyle(400, FontStyle.FONT_SLANT_UPRIGHT), 0, null, @@ -664,8 +630,6 @@ public final class UpdatableFontDirTest { @Test public void installFontFile_failedToWriteConfigXml() throws Exception { long expectedModifiedDate = 1234567890; - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1,test"); @@ -680,7 +644,7 @@ public final class UpdatableFontDirTest { assertThat(readonlyDir.setWritable(false, false)).isTrue(); try { UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, readonlyFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); @@ -702,7 +666,6 @@ public final class UpdatableFontDirTest { @Test public void installFontFile_failedToParsePostScript() throws Exception { - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( mUpdatableFontFilesDir, new UpdatableFontDir.FontFileParser() { @@ -725,7 +688,7 @@ public final class UpdatableFontDirTest { @Override public void tryToCreateTypeface(File file) throws IOException { } - }, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); + }, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); try { @@ -741,7 +704,6 @@ public final class UpdatableFontDirTest { @Test public void installFontFile_failedToParsePostScriptName_invalidFont() throws Exception { - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( mUpdatableFontFilesDir, new UpdatableFontDir.FontFileParser() { @@ -763,7 +725,7 @@ public final class UpdatableFontDirTest { @Override public void tryToCreateTypeface(File file) throws IOException { } - }, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); + }, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); try { @@ -779,31 +741,29 @@ public final class UpdatableFontDirTest { @Test public void installFontFile_failedToCreateTypeface() throws Exception { - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); - FakeFontFileParser parser = new FakeFontFileParser(); UpdatableFontDir dir = new UpdatableFontDir( mUpdatableFontFilesDir, new UpdatableFontDir.FontFileParser() { @Override public String getPostScriptName(File file) throws IOException { - return parser.getPostScriptName(file); + return mParser.getPostScriptName(file); } @Override public String buildFontFileName(File file) throws IOException { - return parser.buildFontFileName(file); + return mParser.buildFontFileName(file); } @Override public long getRevision(File file) throws IOException { - return parser.getRevision(file); + return mParser.getRevision(file); } @Override public void tryToCreateTypeface(File file) throws IOException { throw new IOException(); } - }, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); + }, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); try { @@ -820,16 +780,15 @@ public final class UpdatableFontDirTest { @Test public void installFontFile_renameToPsNameFailure() throws Exception { UpdatableFontDir.FsverityUtil fakeFsverityUtil = new UpdatableFontDir.FsverityUtil() { - private final FakeFsverityUtil mFake = new FakeFsverityUtil(); @Override public boolean hasFsverity(String path) { - return mFake.hasFsverity(path); + return mFakeFsverityUtil.hasFsverity(path); } @Override public void setUpFsverity(String path, byte[] pkcs7Signature) throws IOException { - mFake.setUpFsverity(path, pkcs7Signature); + mFakeFsverityUtil.setUpFsverity(path, pkcs7Signature); } @Override @@ -837,9 +796,8 @@ public final class UpdatableFontDirTest { return false; } }; - FakeFontFileParser parser = new FakeFontFileParser(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, fakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); @@ -856,10 +814,8 @@ public final class UpdatableFontDirTest { @Test public void installFontFile_batchFailure() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); @@ -875,15 +831,13 @@ public final class UpdatableFontDirTest { } // The state should be rolled back as a whole if one of the update requests fail. assertThat(dir.getPostScriptMap()).containsKey("foo"); - assertThat(parser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); + assertThat(mParser.getRevision(dir.getPostScriptMap().get("foo"))).isEqualTo(1); } @Test public void addFontFamily() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); @@ -902,10 +856,8 @@ public final class UpdatableFontDirTest { @Test public void addFontFamily_noName() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); @@ -924,10 +876,8 @@ public final class UpdatableFontDirTest { @Test public void addFontFamily_fontNotAvailable() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); @@ -944,10 +894,8 @@ public final class UpdatableFontDirTest { @Test public void getSystemFontConfig() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); // We assume we have monospace. @@ -976,10 +924,8 @@ public final class UpdatableFontDirTest { @Test public void getSystemFontConfig_preserveFirstFontFamily() throws Exception { - FakeFontFileParser parser = new FakeFontFileParser(); - FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); UpdatableFontDir dir = new UpdatableFontDir( - mUpdatableFontFilesDir, parser, fakeFsverityUtil, + mUpdatableFontFilesDir, mParser, mFakeFsverityUtil, mConfigFile, mCurrentTimeSupplier, mConfigSupplier); dir.loadFontFileMap(); assertThat(dir.getSystemFontConfig().getFontFamilies()).isNotEmpty(); @@ -1011,12 +957,12 @@ public final class UpdatableFontDirTest { } private static FontUpdateRequest newAddFontFamilyRequest(String xml) throws Exception { - XmlPullParser parser = Xml.newPullParser(); + XmlPullParser mParser = Xml.newPullParser(); ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)); - parser.setInput(is, "UTF-8"); - parser.nextTag(); + mParser.setInput(is, "UTF-8"); + mParser.nextTag(); - FontConfig.FontFamily fontFamily = FontListParser.readFamily(parser, "", null, true); + FontConfig.FontFamily fontFamily = FontListParser.readFamily(mParser, "", null, true); List<FontUpdateRequest.Font> fonts = new ArrayList<>(); for (FontConfig.Font font : fontFamily.getFontList()) { String name = font.getFile().getName(); diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java index 709b009c2feb..1b6bddc158b8 100644 --- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java @@ -25,6 +25,7 @@ import android.util.ArraySet; import android.util.LongSparseArray; import com.android.internal.util.ArrayUtils; +import com.android.server.utils.WatchedArrayMap; import java.io.File; import java.io.IOException; @@ -33,7 +34,7 @@ import java.security.cert.CertificateException; public class KeySetManagerServiceTest extends AndroidTestCase { - private ArrayMap<String, PackageSetting> mPackagesMap; + private WatchedArrayMap<String, PackageSetting> mPackagesMap; private KeySetManagerService mKsms; public PackageSetting generateFakePackageSetting(String name) { @@ -46,7 +47,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase { @Override public void setUp() throws Exception { super.setUp(); - mPackagesMap = new ArrayMap<String, PackageSetting>(); + mPackagesMap = new WatchedArrayMap<String, PackageSetting>(); mKsms = new KeySetManagerService(mPackagesMap); } @@ -94,7 +95,8 @@ public class KeySetManagerServiceTest extends AndroidTestCase { } public void testEncodePublicKey() throws IOException { - ArrayMap<String, PackageSetting> packagesMap = new ArrayMap<String, PackageSetting>(); + WatchedArrayMap<String, PackageSetting> packagesMap = + new WatchedArrayMap<String, PackageSetting>(); KeySetManagerService ksms = new KeySetManagerService(packagesMap); PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index a2311690744e..29f4aa976ef6 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -47,7 +47,6 @@ import android.os.BaseBundle; import android.os.PersistableBundle; import android.os.Process; import android.os.UserHandle; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Log; @@ -64,6 +63,7 @@ import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.permission.LegacyPermissionDataProvider; import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; import com.android.server.utils.WatchableTester; +import com.android.server.utils.WatchedArrayMap; import com.google.common.truth.Truth; @@ -1202,9 +1202,8 @@ public class PackageManagerSettingsTests { private void verifyKeySetMetaData(Settings settings) throws ReflectiveOperationException, IllegalAccessException { - ArrayMap<String, PackageSetting> packages = - settings.mPackages.untrackedStorage(); - KeySetManagerService ksms = settings.mKeySetManagerService; + WatchedArrayMap<String, PackageSetting> packages = settings.mPackages; + KeySetManagerService ksms = settings.getKeySetManagerService(); /* verify keyset and public key ref counts */ assertThat(KeySetUtils.getKeySetRefCount(ksms, 1), is(2)); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java index ab545266466e..50ebffc31035 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -197,7 +197,7 @@ public class NotificationListenersTest extends UiServiceTestCase { si.packageName = "new"; si.name = "comp"; si.metaData = new Bundle(); - si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1,2"); + si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1|2"); mListeners.ensureFilters(si, 0); @@ -213,7 +213,7 @@ public class NotificationListenersTest extends UiServiceTestCase { si.name = "comp"; si.metaData = new Bundle(); si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, - "conversations,ALERTING"); + "conversations|ALERTING"); mListeners.ensureFilters(si, 0); @@ -243,7 +243,7 @@ public class NotificationListenersTest extends UiServiceTestCase { si.packageName = "new"; si.name = "comp"; si.metaData = new Bundle(); - si.metaData.putString(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES, "1,2"); + si.metaData.putString(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES, "1|2"); mListeners.ensureFilters(si, 0); @@ -259,7 +259,7 @@ public class NotificationListenersTest extends UiServiceTestCase { si.name = "comp"; si.metaData = new Bundle(); si.metaData.putString(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES, - "1,alerting"); + "1|alerting"); mListeners.ensureFilters(si, 0); @@ -274,7 +274,7 @@ public class NotificationListenersTest extends UiServiceTestCase { si.packageName = "new"; si.name = "comp"; si.metaData = new Bundle(); - si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1,2"); + si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1|2"); si.metaData.putInt(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES, 1); mListeners.ensureFilters(si, 0); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index ddaf3ab617d0..9a89626458c7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -2034,20 +2034,29 @@ public class ActivityRecordTests extends WindowTestsBase { .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) .build(); - // Non-resizable + // Not allow non-resizable mAtm.mForceResizableActivities = false; + mAtm.mSupportsNonResizableMultiWindow = -1; mAtm.mDevEnableNonResizableMultiWindow = false; assertFalse(activity.supportsSplitScreenWindowingMode()); // Force resizable mAtm.mForceResizableActivities = true; + mAtm.mSupportsNonResizableMultiWindow = -1; mAtm.mDevEnableNonResizableMultiWindow = false; assertTrue(activity.supportsSplitScreenWindowingMode()); - // Allow non-resizable + // Use development option to allow non-resizable mAtm.mForceResizableActivities = false; + mAtm.mSupportsNonResizableMultiWindow = -1; mAtm.mDevEnableNonResizableMultiWindow = true; assertTrue(activity.supportsSplitScreenWindowingMode()); + + // Always allow non-resizable + mAtm.mForceResizableActivities = false; + mAtm.mSupportsNonResizableMultiWindow = 1; + mAtm.mDevEnableNonResizableMultiWindow = false; + assertTrue(activity.supportsSplitScreenWindowingMode()); } @Test @@ -2058,20 +2067,29 @@ public class ActivityRecordTests extends WindowTestsBase { .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) .build(); - // Non-resizable + // Not allow non-resizable mAtm.mForceResizableActivities = false; + mAtm.mSupportsNonResizableMultiWindow = -1; mAtm.mDevEnableNonResizableMultiWindow = false; assertFalse(activity.supportsFreeform()); // Force resizable mAtm.mForceResizableActivities = true; + mAtm.mSupportsNonResizableMultiWindow = -1; mAtm.mDevEnableNonResizableMultiWindow = false; assertTrue(activity.supportsFreeform()); - // Allow non-resizable + // Use development option to allow non-resizable mAtm.mForceResizableActivities = false; + mAtm.mSupportsNonResizableMultiWindow = -1; mAtm.mDevEnableNonResizableMultiWindow = true; assertTrue(activity.supportsFreeform()); + + // Always allow non-resizable + mAtm.mForceResizableActivities = false; + mAtm.mSupportsNonResizableMultiWindow = 1; + mAtm.mDevEnableNonResizableMultiWindow = false; + assertTrue(activity.supportsFreeform()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 618de21bb158..2558259370d2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -320,8 +320,8 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { .build(); final Task task = activity.getTask(); - assertTrue(activity.supportsMultiWindow2()); - assertTrue(task.supportsMultiWindow2()); + assertTrue(activity.supportsMultiWindow()); + assertTrue(task.supportsMultiWindow()); } @Test @@ -336,14 +336,14 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { // Device config as not support. mAtm.mSupportsNonResizableMultiWindow = -1; - assertFalse(activity.supportsMultiWindow2()); - assertFalse(task.supportsMultiWindow2()); + assertFalse(activity.supportsMultiWindow()); + assertFalse(task.supportsMultiWindow()); // Device config as always support. mAtm.mSupportsNonResizableMultiWindow = 1; - assertTrue(activity.supportsMultiWindow2()); - assertTrue(task.supportsMultiWindow2()); + assertTrue(activity.supportsMultiWindow()); + assertTrue(task.supportsMultiWindow()); // The default config is relying on the screen size. mAtm.mSupportsNonResizableMultiWindow = 0; @@ -351,14 +351,14 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { // Supports on large screen. tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp; - assertTrue(activity.supportsMultiWindow2()); - assertTrue(task.supportsMultiWindow2()); + assertTrue(activity.supportsMultiWindow()); + assertTrue(task.supportsMultiWindow()); // Not supports on small screen. tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp - 1; - assertFalse(activity.supportsMultiWindow2()); - assertFalse(task.supportsMultiWindow2()); + assertFalse(activity.supportsMultiWindow()); + assertFalse(task.supportsMultiWindow()); } @Test @@ -381,14 +381,14 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { // Ignore the activity min width/height for determine multi window eligibility. mAtm.mRespectsActivityMinWidthHeightMultiWindow = -1; - assertTrue(activity.supportsMultiWindow2()); - assertTrue(task.supportsMultiWindow2()); + assertTrue(activity.supportsMultiWindow()); + assertTrue(task.supportsMultiWindow()); // Always check the activity min width/height. mAtm.mRespectsActivityMinWidthHeightMultiWindow = 1; - assertFalse(activity.supportsMultiWindow2()); - assertFalse(task.supportsMultiWindow2()); + assertFalse(activity.supportsMultiWindow()); + assertFalse(task.supportsMultiWindow()); // The default config is relying on the screen size. mAtm.mRespectsActivityMinWidthHeightMultiWindow = 0; @@ -396,14 +396,14 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { // Ignore on large screen. tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp; - assertTrue(activity.supportsMultiWindow2()); - assertTrue(task.supportsMultiWindow2()); + assertTrue(activity.supportsMultiWindow()); + assertTrue(task.supportsMultiWindow()); // Check on small screen. tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp - 1; - assertFalse(activity.supportsMultiWindow2()); - assertFalse(task.supportsMultiWindow2()); + assertFalse(activity.supportsMultiWindow()); + assertFalse(task.supportsMultiWindow()); } @Test @@ -429,14 +429,14 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { // Always check the activity min width/height. mAtm.mSupportsNonResizableMultiWindow = 1; - assertTrue(activity.supportsMultiWindow2()); - assertTrue(task.supportsMultiWindow2()); + assertTrue(activity.supportsMultiWindow()); + assertTrue(task.supportsMultiWindow()); // The default config is relying on the screen size. Check for small screen mAtm.mSupportsNonResizableMultiWindow = 0; - assertTrue(activity.supportsMultiWindow2()); - assertTrue(task.supportsMultiWindow2()); + assertTrue(activity.supportsMultiWindow()); + assertTrue(task.supportsMultiWindow()); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java index 325bca418d13..1ee646c57b14 100644 --- a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java @@ -77,12 +77,12 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { assertNotNull("Window state is created", appWindow); assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority doesn't change. assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); // Call the function a few times. appWindow.updateFrameRateSelectionPriorityIfNeeded(); @@ -92,7 +92,7 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority( any(SurfaceControl.class), anyInt()); verify(appWindow.getPendingTransaction(), never()).setFrameRate( - any(SurfaceControl.class), anyInt(), anyInt()); + any(SurfaceControl.class), anyInt(), anyInt(), anyInt()); } @Test @@ -101,16 +101,16 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy() .getPreferredModeId(appWindow), 0); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy() .getPreferredRefreshRate(appWindow), 0, FLOAT_TOLERANCE); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority stays MAX_VALUE. assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET); @@ -119,38 +119,38 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority changes to 1. assertEquals(appWindow.mFrameRateSelectionPriority, 1); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), 1); verify(appWindow.getPendingTransaction(), never()).setFrameRate( - any(SurfaceControl.class), anyInt(), anyInt()); + any(SurfaceControl.class), anyInt(), anyInt(), anyInt()); } @Test public void testApplicationInFocusWithModeId() { final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); // Application is in focus. appWindow.mToken.mDisplayContent.mCurrentFocus = appWindow; appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority changes. assertEquals(appWindow.mFrameRateSelectionPriority, 1); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); // Update the mode ID to a requested number. appWindow.mAttrs.preferredDisplayModeId = 1; appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority changes. assertEquals(appWindow.mFrameRateSelectionPriority, 0); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 60, FLOAT_TOLERANCE); // Remove the mode ID request. appWindow.mAttrs.preferredDisplayModeId = 0; appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority changes. assertEquals(appWindow.mFrameRateSelectionPriority, 1); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); // Verify we called actions on Transactions correctly. verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority( @@ -160,14 +160,14 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { verify(appWindow.getPendingTransaction(), times(2)).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), 1); verify(appWindow.getPendingTransaction(), never()).setFrameRate( - any(SurfaceControl.class), anyInt(), anyInt()); + any(SurfaceControl.class), anyInt(), anyInt(), anyInt()); } @Test public void testApplicationNotInFocusWithModeId() { final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus"); appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow; @@ -175,28 +175,28 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { appWindow.updateFrameRateSelectionPriorityIfNeeded(); // The window is not in focus. assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); // Update the mode ID to a requested number. appWindow.mAttrs.preferredDisplayModeId = 1; appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority changes. assertEquals(appWindow.mFrameRateSelectionPriority, 2); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 60, FLOAT_TOLERANCE); verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET); verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), 2); verify(appWindow.getPendingTransaction(), never()).setFrameRate( - any(SurfaceControl.class), anyInt(), anyInt()); + any(SurfaceControl.class), anyInt(), anyInt(), anyInt()); } @Test public void testApplicationNotInFocusWithoutModeId() { final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus"); appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow; @@ -204,19 +204,19 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { appWindow.updateFrameRateSelectionPriorityIfNeeded(); // The window is not in focus. assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); // Make sure that the mode ID is not set. appWindow.mAttrs.preferredDisplayModeId = 0; appWindow.updateFrameRateSelectionPriorityIfNeeded(); // Priority doesn't change. assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET); - assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE); + assertEquals(appWindow.mAppPreferredFrameRate, 0, FLOAT_TOLERANCE); verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority( appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET); verify(appWindow.getPendingTransaction(), never()).setFrameRate( - any(SurfaceControl.class), anyInt(), anyInt()); + any(SurfaceControl.class), anyInt(), anyInt(), anyInt()); } @Test @@ -233,7 +233,7 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { appWindow.updateFrameRateSelectionPriorityIfNeeded(); assertEquals(RefreshRatePolicy.LAYER_PRIORITY_UNSET, appWindow.mFrameRateSelectionPriority); - assertEquals(60, appWindow.mDenyListFrameRate, FLOAT_TOLERANCE); + assertEquals(60, appWindow.mAppPreferredFrameRate, FLOAT_TOLERANCE); // Call the function a few times. appWindow.updateFrameRateSelectionPriorityIfNeeded(); @@ -243,6 +243,7 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority( any(SurfaceControl.class), anyInt()); verify(appWindow.getPendingTransaction(), times(1)).setFrameRate( - appWindow.getSurfaceControl(), 60, Surface.FRAME_RATE_COMPATIBILITY_EXACT); + appWindow.getSurfaceControl(), 60, + Surface.FRAME_RATE_COMPATIBILITY_EXACT, Surface.CHANGE_FRAME_RATE_ALWAYS); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java index ef3c7ae91fed..20b987de9aa1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java @@ -71,9 +71,11 @@ public class RefreshRatePolicyTest extends WindowTestsBase { cameraUsingWindow.mAttrs.packageName = "com.android.test"; assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); mPolicy.addNonHighRefreshRatePackage("com.android.test"); - assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(60, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); mPolicy.removeNonHighRefreshRatePackage("com.android.test"); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); @@ -109,6 +111,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase { mPolicy.addNonHighRefreshRatePackage("com.android.test"); assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE); } @Test @@ -123,6 +126,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase { mPolicy.addNonHighRefreshRatePackage("com.android.test"); assertEquals(0, mPolicy.getPreferredModeId(overrideWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE); } @Test @@ -132,13 +136,31 @@ public class RefreshRatePolicyTest extends WindowTestsBase { cameraUsingWindow.mAttrs.packageName = "com.android.test"; mPolicy.addNonHighRefreshRatePackage("com.android.test"); - assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(60, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); cameraUsingWindow.mActivityRecord.mSurfaceAnimator.startAnimation( cameraUsingWindow.getPendingTransaction(), mock(AnimationAdapter.class), false /* hidden */, ANIMATION_TYPE_APP_TRANSITION); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + } + + @Test + public void testAppMaxRefreshRate() { + final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, "window"); + window.mAttrs.preferredMaxDisplayRefreshRate = 60f; + assertEquals(0, mPolicy.getPreferredModeId(window)); + assertEquals(0, mPolicy.getPreferredRefreshRate(window), FLOAT_TOLERANCE); + assertEquals(60, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE); + + window.mActivityRecord.mSurfaceAnimator.startAnimation( + window.getPendingTransaction(), mock(AnimationAdapter.class), + false /* hidden */, ANIMATION_TYPE_APP_TRANSITION); + assertEquals(0, mPolicy.getPreferredModeId(window)); + assertEquals(0, mPolicy.getPreferredRefreshRate(window), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index e280a363d7d4..0c6545c2438f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -586,38 +586,30 @@ public class RootTaskTests extends WindowTestsBase { @Test public void testShouldBeVisible_SplitScreen() { - final Task homeRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - // Home root task should always be fullscreen for this test. - doReturn(false).when(homeRootTask).supportsSplitScreenWindowingMode(); + // task not supporting split should be fullscreen for this test. + final Task notSupportingSplitTask = createTaskForShouldBeVisibleTest( + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + true /* onTop */); + doReturn(false).when(notSupportingSplitTask).supportsSplitScreenWindowingMode(); final Task splitScreenPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); final Task splitScreenSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); - // Home root task shouldn't be visible if both halves of split-screen are opaque. + // root task not supporting split shouldn't be visible if both halves of split-screen are + // opaque. doReturn(false).when(splitScreenPrimary).isTranslucent(any()); doReturn(false).when(splitScreenSecondary).isTranslucent(any()); - assertFalse(homeRootTask.shouldBeVisible(null /* starting */)); + assertFalse(notSupportingSplitTask.shouldBeVisible(null /* starting */)); assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, homeRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, - splitScreenPrimary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, - splitScreenSecondary.getVisibility(null /* starting */)); - // Home root task should be visible if one of the halves of split-screen is translucent. + // root task not supporting split shouldn't be visible if one of the halves of split-screen + // is translucent. doReturn(true).when(splitScreenPrimary).isTranslucent(any()); - assertTrue(homeRootTask.shouldBeVisible(null /* starting */)); + assertFalse(notSupportingSplitTask.shouldBeVisible(null /* starting */)); assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, - homeRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, - splitScreenPrimary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, - splitScreenSecondary.getVisibility(null /* starting */)); final Task splitScreenSecondary2 = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java index 619aee6eb919..b89539cabbfe 100644 --- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java +++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java @@ -231,7 +231,7 @@ public class StubTransaction extends SurfaceControl.Transaction { @Override public SurfaceControl.Transaction setFrameRate(SurfaceControl sc, float frameRate, - int compatibility) { + int compatibility, int changeFrameRateStrategy) { return this; } diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 61b700253d6b..42ef08630500 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -488,6 +488,7 @@ public class SystemServicesTestRule implements TestRule { mLargeScreenSmallestScreenWidthDp = 600; mSupportsNonResizableMultiWindow = 0; mRespectsActivityMinWidthHeightMultiWindow = 0; + mForceResizableActivities = false; doReturn(mock(IPackageManager.class)).when(this).getPackageManager(); // allow background activity starts by default diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 8c87befef4f9..3f1248a5fff7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -1193,13 +1193,13 @@ public class WindowOrganizerTests extends WindowTestsBase { splitPrimaryRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */); // Can't reparent non-resizable to split screen - mAtm.mDevEnableNonResizableMultiWindow = false; + mAtm.mSupportsNonResizableMultiWindow = -1; mAtm.mWindowOrganizerController.applyTransaction(wct); assertEquals(rootTask, activity.getRootTask()); // Allow reparent non-resizable to split screen - mAtm.mDevEnableNonResizableMultiWindow = true; + mAtm.mSupportsNonResizableMultiWindow = 1; mAtm.mWindowOrganizerController.applyTransaction(wct); assertEquals(splitPrimaryRootTask, activity.getRootTask()); diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java index 4198d3b86500..2b01cdfdc27f 100644 --- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java +++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java @@ -175,10 +175,9 @@ final class TranslationManagerServiceImpl extends return; } try { - // TODO: Pipe uiTranslationSpec through to the UiTranslationController. taskTopActivityTokens.getApplicationThread().updateUiTranslationState( taskTopActivityTokens.getActivityToken(), state, sourceSpec, targetSpec, - viewIds); + viewIds, uiTranslationSpec); } catch (RemoteException e) { Slog.w(TAG, "Update UiTranslationState fail: " + e); } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index d1bd159ee7ff..aec06431c5fb 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -164,6 +164,11 @@ final class HotwordDetectionConnection { 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); } catch (RemoteException e) { Slog.w(TAG, "Failed to report initialization status: " + e); @@ -612,7 +617,7 @@ final class HotwordDetectionConnection { options, new IDspHotwordDetectionCallback.Stub() { @Override - public void onRejected(@Nullable HotwordRejectedResult result) + public void onRejected(HotwordRejectedResult result) throws RemoteException { bestEffortClose(serviceAudioSink); bestEffortClose(serviceAudioSource); @@ -622,7 +627,7 @@ final class HotwordDetectionConnection { } @Override - public void onDetected(@Nullable HotwordDetectedResult triggerResult) + public void onDetected(HotwordDetectedResult triggerResult) throws RemoteException { bestEffortClose(serviceAudioSink); bestEffortClose(serviceAudioSource); diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java index 898b8d4cbdc1..44f96c5a987b 100644 --- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java +++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java @@ -56,6 +56,11 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; /** * Tests if fonts can be updated by {@link FontManager} API. @@ -95,6 +100,12 @@ public class UpdatableSystemFontTest { EMOJI_RENDERING_TEST_APP_ID + "/.EmojiRenderingTestActivity"; private static final long ACTIVITY_TIMEOUT_MILLIS = SECONDS.toMillis(10); + private static final Pattern PATTERN_FONT_FILES = Pattern.compile("\\.(ttf|otf|ttc|otc)$"); + private static final Pattern PATTERN_TMP_FILES = Pattern.compile("^/data/local/tmp/"); + private static final Pattern PATTERN_DATA_FONT_FILES = Pattern.compile("^/data/fonts/files/"); + private static final Pattern PATTERN_SYSTEM_FONT_FILES = + Pattern.compile("^/(system|product)/fonts/"); + private String mKeyId; private FontManager mFontManager; @@ -236,6 +247,32 @@ public class UpdatableSystemFontTest { assertThat(fontPathAfterReboot).isEqualTo(fontPath); } + + @Test + public void fdLeakTest() throws Exception { + long originalOpenFontCount = + countMatch(getOpenFiles("system_server"), PATTERN_FONT_FILES); + Pattern patternEmojiVPlus1 = + Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF)); + for (int i = 0; i < 10; i++) { + assertThat(updateFontFile( + TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG)) + .isEqualTo(FontManager.RESULT_SUCCESS); + List<String> openFiles = getOpenFiles("system_server"); + for (Pattern p : Arrays.asList(PATTERN_FONT_FILES, PATTERN_SYSTEM_FONT_FILES, + PATTERN_DATA_FONT_FILES, PATTERN_TMP_FILES)) { + Log.i(TAG, String.format("num of %s: %d", p, countMatch(openFiles, p))); + } + // system_server should not keep /data/fonts files open. + assertThat(countMatch(openFiles, PATTERN_DATA_FONT_FILES)).isEqualTo(0); + // system_server should not keep passed FD open. + assertThat(countMatch(openFiles, patternEmojiVPlus1)).isEqualTo(0); + // The number of open font FD should not increase. + assertThat(countMatch(openFiles, PATTERN_FONT_FILES)) + .isAtMost(originalOpenFontCount); + } + } + private static String insertCert(String certPath) throws Exception { Pair<String, String> result; try (InputStream is = new FileInputStream(certPath)) { @@ -338,7 +375,37 @@ public class UpdatableSystemFontTest { return !expectCommandToSucceed(cmd).trim().isEmpty(); } + private static List<String> getOpenFiles(String appId) throws Exception { + String pid = pidOf(appId); + if (pid.isEmpty()) { + return Collections.emptyList(); + } + String cmd = String.format("lsof -p %s", pid); + String out = expectCommandToSucceed(cmd); + List<String> paths = new ArrayList<>(); + boolean first = true; + for (String line : out.split("\n")) { + // Skip the header. + if (first) { + first = false; + continue; + } + String[] records = line.split(" "); + if (records.length > 0) { + paths.add(records[records.length - 1]); + } + } + return paths; + } + private static String pidOf(String appId) throws Exception { return expectCommandToSucceed("pidof " + appId).trim(); } + + private static long countMatch(List<String> paths, Pattern pattern) { + // Note: asPredicate() returns true for partial matching. + return paths.stream() + .filter(pattern.asPredicate()) + .count(); + } } diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java index 9410886c3549..c59dcf879b1c 100644 --- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java +++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java @@ -16,13 +16,17 @@ package android.net.vcn; +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.assertTrue; import static org.junit.Assert.fail; import android.net.NetworkCapabilities; +import android.net.ipsec.ike.IkeSessionParams; import android.net.ipsec.ike.IkeTunnelConnectionParams; +import android.net.vcn.persistablebundleutils.IkeSessionParamsUtilsTest; import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtilsTest; import androidx.test.filters.SmallTest; @@ -120,6 +124,21 @@ public class VcnGatewayConnectionConfigTest { } @Test + public void testBuilderRequiresMobikeEnabled() { + try { + final IkeSessionParams ikeParams = + IkeSessionParamsUtilsTest.createBuilderMinimum() + .removeIkeOption(IKE_OPTION_MOBIKE) + .build(); + final IkeTunnelConnectionParams tunnelParams = + TunnelConnectionParamsUtilsTest.buildTestParams(ikeParams); + new VcnGatewayConnectionConfig.Builder(GATEWAY_CONNECTION_NAME_PREFIX, tunnelParams); + fail("Expected exception due to MOBIKE not enabled"); + } catch (IllegalArgumentException e) { + } + } + + @Test public void testBuilderRequiresNonEmptyExposedCaps() { try { newBuilder() diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java index 582275d0547d..00a0bffa15c5 100644 --- a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java +++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java @@ -16,14 +16,17 @@ package android.net.vcn; -import static android.net.NetworkCapabilities.REDACT_ALL; +import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION; +import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS; import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; +import static android.net.NetworkCapabilities.REDACT_NONE; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; +import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.os.Parcel; @@ -39,12 +42,6 @@ public class VcnTransportInfoTest { private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO); @Test - public void testRedactionDefaults() { - assertEquals(REDACT_ALL, CELL_UNDERLYING_INFO.getRedaction()); - assertEquals(REDACT_ALL, WIFI_UNDERLYING_INFO.getRedaction()); - } - - @Test public void testGetWifiInfo() { assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo()); @@ -59,15 +56,15 @@ public class VcnTransportInfoTest { } @Test - public void testMakeCopySetsRedactions() { + public void testMakeCopyRedactForAccessFineLocation() { assertEquals( - REDACT_FOR_NETWORK_SETTINGS, - ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS)) - .getRedaction()); + SUB_ID, + ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION)) + .getSubId()); assertEquals( - REDACT_FOR_NETWORK_SETTINGS, - ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS)) - .getRedaction()); + WifiConfiguration.INVALID_NETWORK_ID, + ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION)) + .getWifiInfo().getNetworkId()); } @Test @@ -78,35 +75,31 @@ public class VcnTransportInfoTest { } @Test - public void testParcelUnparcel() { - verifyParcelingIsNull(CELL_UNDERLYING_INFO); - verifyParcelingIsNull(WIFI_UNDERLYING_INFO); - } - - private void verifyParcelingIsNull(VcnTransportInfo vcnTransportInfo) { - // Verify redacted by default - Parcel parcel = Parcel.obtain(); - vcnTransportInfo.writeToParcel(parcel, 0 /* flags */); - parcel.setDataPosition(0); - - assertNull(VcnTransportInfo.CREATOR.createFromParcel(parcel)); + public void testApplicableRedactions() { + assertEquals(REDACT_NONE, CELL_UNDERLYING_INFO.getApplicableRedactions()); + assertEquals(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS + | REDACT_FOR_NETWORK_SETTINGS, + WIFI_UNDERLYING_INFO.getApplicableRedactions()); } @Test - public void testParcelUnparcelNotRedactedForSysUi() { - verifyParcelingForSysUi(CELL_UNDERLYING_INFO); - verifyParcelingForSysUi(WIFI_UNDERLYING_INFO); + public void testParcelNotRedactedForSysUi() { + VcnTransportInfo cellRedacted = parcelForSysUi(CELL_UNDERLYING_INFO); + assertEquals(SUB_ID, cellRedacted.getSubId()); + VcnTransportInfo wifiRedacted = parcelForSysUi(WIFI_UNDERLYING_INFO); + assertEquals(NETWORK_ID, wifiRedacted.getWifiInfo().getNetworkId()); } - private void verifyParcelingForSysUi(VcnTransportInfo vcnTransportInfo) { + private VcnTransportInfo parcelForSysUi(VcnTransportInfo vcnTransportInfo) { // Allow fully unredacted; SysUI will have all the relevant permissions. - final VcnTransportInfo unRedacted = (VcnTransportInfo) vcnTransportInfo.makeCopy(0); + final VcnTransportInfo unRedacted = (VcnTransportInfo) vcnTransportInfo.makeCopy( + REDACT_NONE); final Parcel parcel = Parcel.obtain(); unRedacted.writeToParcel(parcel, 0 /* flags */); parcel.setDataPosition(0); final VcnTransportInfo unparceled = VcnTransportInfo.CREATOR.createFromParcel(parcel); assertEquals(vcnTransportInfo, unparceled); - assertEquals(REDACT_ALL, unparceled.getRedaction()); + return unparceled; } } diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java index 393787f1a8b8..f3851130c68a 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java @@ -52,8 +52,8 @@ import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @SmallTest public class IkeSessionParamsUtilsTest { - // Package private for use in EncryptedTunnelParamsUtilsTest - static IkeSessionParams.Builder createBuilderMinimum() { + // Public for use in VcnGatewayConnectionConfigTest, EncryptedTunnelParamsUtilsTest + public static IkeSessionParams.Builder createBuilderMinimum() { final InetAddress serverAddress = InetAddresses.parseNumericAddress("192.0.2.100"); // TODO: b/185941731 Make sure all valid IKE_OPTIONS are added and validated. @@ -63,6 +63,7 @@ public class IkeSessionParamsUtilsTest { .setLocalIdentification(new IkeFqdnIdentification("client.test.android.net")) .setRemoteIdentification(new IkeFqdnIdentification("server.test.android.net")) .addIkeOption(IkeSessionParams.IKE_OPTION_FORCE_PORT_4500) + .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE) .setAuthPsk("psk".getBytes()); } diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java index 0c8ad32b0c27..f9dc9eb4d5ae 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java @@ -18,6 +18,7 @@ package android.net.vcn.persistablebundleutils; import static org.junit.Assert.assertEquals; +import android.net.ipsec.ike.IkeSessionParams; import android.net.ipsec.ike.IkeTunnelConnectionParams; import androidx.test.filters.SmallTest; @@ -31,9 +32,13 @@ import org.junit.runner.RunWith; public class TunnelConnectionParamsUtilsTest { // Public for use in VcnGatewayConnectionConfigTest public static IkeTunnelConnectionParams buildTestParams() { + return buildTestParams(IkeSessionParamsUtilsTest.createBuilderMinimum().build()); + } + + // Public for use in VcnGatewayConnectionConfigTest + public static IkeTunnelConnectionParams buildTestParams(IkeSessionParams params) { return new IkeTunnelConnectionParams( - IkeSessionParamsUtilsTest.createBuilderMinimum().build(), - TunnelModeChildSessionParamsUtilsTest.createBuilderMinimum().build()); + params, TunnelModeChildSessionParamsUtilsTest.createBuilderMinimum().build()); } @Test diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 9ecd82ff6bcb..3360d40062a3 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -37,6 +37,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; 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.CALLS_REAL_METHODS; @@ -66,6 +67,7 @@ import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnConfigTest; +import android.net.vcn.VcnGatewayConnectionConfigTest; import android.net.vcn.VcnManager; import android.net.vcn.VcnUnderlyingNetworkPolicy; import android.os.IBinder; @@ -197,7 +199,8 @@ public class VcnManagementServiceTest { .newVcnContext( eq(mMockContext), eq(mTestLooper.getLooper()), - any(VcnNetworkProvider.class)); + any(VcnNetworkProvider.class), + anyBoolean()); doReturn(mSubscriptionTracker) .when(mMockDeps) .newTelephonySubscriptionTracker( @@ -371,6 +374,12 @@ public class VcnManagementServiceTest { TelephonySubscriptionSnapshot snapshot = triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1)); verify(mMockDeps) + .newVcnContext( + eq(mMockContext), + eq(mTestLooper.getLooper()), + any(VcnNetworkProvider.class), + anyBoolean()); + verify(mMockDeps) .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any()); } @@ -528,6 +537,28 @@ public class VcnManagementServiceTest { } @Test + public void testSetVcnConfigTestModeRequiresPermission() throws Exception { + doThrow(new SecurityException("Requires MANAGE_TEST_NETWORKS")) + .when(mMockContext) + .enforceCallingPermission( + eq(android.Manifest.permission.MANAGE_TEST_NETWORKS), any()); + + final VcnConfig vcnConfig = + new VcnConfig.Builder(mMockContext) + .addGatewayConnectionConfig( + VcnGatewayConnectionConfigTest.buildTestConfig()) + .setIsTestModeProfile() + .build(); + + try { + mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, vcnConfig, TEST_PACKAGE_NAME); + fail("Expected exception due to using test-mode without permission"); + } catch (SecurityException e) { + verify(mMockPolicyListener, never()).onPolicyChanged(); + } + } + + @Test public void testSetVcnConfigNotifiesStatusCallback() throws Exception { triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2)); diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java index 8289e85dadf9..0b72cd93e8b0 100644 --- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -112,8 +113,14 @@ public class UnderlyingNetworkTrackerTest { MockitoAnnotations.initMocks(this); mTestLooper = new TestLooper(); - mVcnContext = spy(new VcnContext(mContext, mTestLooper.getLooper(), mVcnNetworkProvider)); - doNothing().when(mVcnContext).ensureRunningOnLooperThread(); + mVcnContext = + spy( + new VcnContext( + mContext, + mTestLooper.getLooper(), + mVcnNetworkProvider, + false /* isInTestMode */)); + resetVcnContext(); setupSystemService( mContext, @@ -132,6 +139,11 @@ public class UnderlyingNetworkTrackerTest { mNetworkTrackerCb); } + private void resetVcnContext() { + reset(mVcnContext); + doNothing().when(mVcnContext).ensureRunningOnLooperThread(); + } + private static LinkProperties getLinkPropertiesWithName(String iface) { LinkProperties linkProperties = new LinkProperties(); linkProperties.setInterfaceName(iface); @@ -149,6 +161,31 @@ public class UnderlyingNetworkTrackerTest { verifyNetworkRequestsRegistered(INITIAL_SUB_IDS); } + @Test + public void testNetworkCallbacksRegisteredOnStartupForTestMode() { + final VcnContext vcnContext = + spy( + new VcnContext( + mContext, + mTestLooper.getLooper(), + mVcnNetworkProvider, + true /* isInTestMode */)); + + mUnderlyingNetworkTracker = + new UnderlyingNetworkTracker( + vcnContext, + SUB_GROUP, + mSubscriptionSnapshot, + Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET), + mNetworkTrackerCb); + + verify(mConnectivityManager) + .requestBackgroundNetwork( + eq(getTestNetworkRequest(INITIAL_SUB_IDS)), + any(RouteSelectionCallback.class), + any()); + } + private void verifyNetworkRequestsRegistered(Set<Integer> expectedSubIds) { verify(mConnectivityManager) .requestBackgroundNetwork( @@ -165,7 +202,8 @@ public class UnderlyingNetworkTrackerTest { verify(mConnectivityManager) .requestBackgroundNetwork( eq(getRouteSelectionRequest(expectedSubIds)), - any(RouteSelectionCallback.class), any()); + any(RouteSelectionCallback.class), + any()); } @Test @@ -204,6 +242,14 @@ public class UnderlyingNetworkTrackerTest { return getExpectedRequestBase().setSubscriptionIds(netCapsSubIds).build(); } + private NetworkRequest getTestNetworkRequest(Set<Integer> netCapsSubIds) { + return new NetworkRequest.Builder() + .clearCapabilities() + .addTransportType(NetworkCapabilities.TRANSPORT_TEST) + .setSubscriptionIds(netCapsSubIds) + .build(); + } + private NetworkRequest.Builder getExpectedRequestBase() { final NetworkRequest.Builder builder = new NetworkRequest.Builder() diff --git a/tools/aapt/pseudolocalize.cpp b/tools/aapt/pseudolocalize.cpp index 5c47e0fa8a16..4e8dcb1bc6ee 100644 --- a/tools/aapt/pseudolocalize.cpp +++ b/tools/aapt/pseudolocalize.cpp @@ -194,7 +194,8 @@ static String16 pseudo_generate_expansion(const unsigned int length) { break; } } - result.remove(length + ext, 0); + // Just keep the first length + ext characters + result = String16(result, length + ext); } return result; } diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt index 9ceb2042d74e..7cfa7847fcff 100644 --- a/tools/codegen/src/com/android/codegen/Utils.kt +++ b/tools/codegen/src/com/android/codegen/Utils.kt @@ -43,8 +43,8 @@ inline infix fun Int.times(action: () -> Unit) { * cccc dd */ fun Iterable<Pair<String, String>>.columnize(separator: String = " | "): String { - val col1w = map { (a, _) -> a.length }.maxOrNull()!! - val col2w = map { (_, b) -> b.length }.maxOrNull()!! + val col1w = map { (a, _) -> a.length }.max()!! + val col2w = map { (_, b) -> b.length }.max()!! return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n") } |