Merge "enum migration" into sc-dev
diff --git a/Android.bp b/Android.bp
index ca014bc..da52c23 100644
--- a/Android.bp
+++ b/Android.bp
@@ -408,6 +408,7 @@
"core/java/android/annotation/MainThread.java",
"core/java/android/annotation/NonNull.java",
"core/java/android/annotation/Nullable.java",
+ "core/java/android/annotation/RequiresNoPermission.java",
"core/java/android/annotation/RequiresPermission.java",
"core/java/android/annotation/SdkConstant.java",
"core/java/android/annotation/StringDef.java",
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 175fb38..65b2511 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -22,7 +22,7 @@
strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
-hidden_api_txt_checksorted_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 23dc720..bc3f131 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -96,6 +96,8 @@
],
api_levels_annotations_enabled: false,
filter_packages: packages_to_document,
+ defaults_visibility: ["//visibility:private"],
+ visibility: ["//frameworks/base/api"],
}
/////////////////////////////////////////////////////////////////////
@@ -352,6 +354,8 @@
tag: ".jar",
dest: "android-non-updatable.jar",
},
+ defaults_visibility: ["//visibility:private"],
+ visibility: ["//visibility:private"],
}
java_library_static {
@@ -405,6 +409,8 @@
system_modules: "none",
java_version: "1.8",
compile_dex: true,
+ defaults_visibility: ["//visibility:private"],
+ visibility: ["//visibility:public"],
}
java_defaults {
@@ -417,6 +423,7 @@
tag: ".jar",
dest: "android.jar",
},
+ defaults_visibility: ["//frameworks/base/services"],
}
java_library_static {
@@ -516,6 +523,7 @@
"metalava-manual",
],
args: priv_apps,
+ visibility: ["//visibility:private"],
}
java_library_static {
@@ -525,4 +533,5 @@
srcs: [
":hwbinder-stubs-docs",
],
+ visibility: ["//visibility:public"],
}
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
index 5473690..d272507 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceSerializationPerfTest.java
@@ -20,6 +20,7 @@
import android.os.SharedMemory;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
+import android.util.ArrayMap;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -55,9 +56,10 @@
ByteBuffer buffer = memory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ ArrayMap<String, Typeface> out = new ArrayMap<>();
while (state.keepRunning()) {
buffer.position(0);
- Typeface.deserializeFontMap(buffer);
+ Typeface.deserializeFontMap(buffer, out);
}
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
index 35cea3e..6c62426 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
@@ -26,6 +26,7 @@
import java.util.Collections;
import java.util.Map;
+import java.util.Objects;
/**
* Provides results for AppSearch batch operations which encompass multiple documents.
@@ -180,7 +181,7 @@
public Builder<KeyType, ValueType> setSuccess(
@NonNull KeyType key, @Nullable ValueType result) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
return setResult(key, AppSearchResult.newSuccessfulResult(result));
}
@@ -198,7 +199,7 @@
@AppSearchResult.ResultCode int resultCode,
@Nullable String errorMessage) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
return setResult(key, AppSearchResult.newFailedResult(resultCode, errorMessage));
}
@@ -214,8 +215,8 @@
public Builder<KeyType, ValueType> setResult(
@NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(key);
- Preconditions.checkNotNull(result);
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(result);
if (result.isSuccess()) {
mSuccesses.put(key, result.getResultValue());
mFailures.remove(key);
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 9776827..8af91b4 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -29,14 +29,16 @@
/**
* Provides access to the centralized AppSearch index maintained by the system.
*
- * <p>AppSearch is a search library for managing structured data featuring:
+ * <p>AppSearch is an offline, on-device search library for managing structured data featuring:
*
* <ul>
- * <li>A fully offline on-device solution
- * <li>A set of APIs for applications to index documents and retrieve them via full-text search
- * <li>APIs for applications to allow the System to display their content on system UI surfaces
- * <li>Similarly, APIs for applications to allow the System to share their content with other
- * specified applications.
+ * <li>APIs to index and retrieve data via full-text search.
+ * <li>An API for applications to explicitly grant read-access permission of their data to other
+ * applications.
+ * <b>See: {@link SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage}</b>
+ * <li>An API for applications to opt into or out of having their data displayed on System UI
+ * surfaces by the System-designated global querier.
+ * <b>See: {@link SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem}</b>
* </ul>
*
* <p>Applications create a database by opening an {@link AppSearchSession}.
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
index b66837d..b06e215 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
@@ -37,6 +37,8 @@
* @param <ValueType> The type of result object for successful calls.
*/
public final class AppSearchResult<ValueType> implements Parcelable {
+ private static final String TAG = "AppSearchResult";
+
/**
* Result codes from {@link AppSearchSession} methods.
* @hide
@@ -246,14 +248,22 @@
@NonNull
public static <ValueType> AppSearchResult<ValueType> throwableToFailedResult(
@NonNull Throwable t) {
- Log.d("AppSearchResult", "Converting throwable to failed result.", t);
+ // Log for traceability. NOT_FOUND is logged at VERBOSE because this error can occur during
+ // the regular operation of the system (b/183550974). Everything else is logged at DEBUG.
+ if (t instanceof AppSearchException
+ && ((AppSearchException) t).getResultCode() == RESULT_NOT_FOUND) {
+ Log.v(TAG, "Converting throwable to failed result: " + t);
+ } else {
+ Log.d(TAG, "Converting throwable to failed result.", t);
+ }
if (t instanceof AppSearchException) {
return ((AppSearchException) t).toAppSearchResult();
}
+ String exceptionClass = t.getClass().getSimpleName();
@AppSearchResult.ResultCode int resultCode;
- if (t instanceof IllegalStateException) {
+ if (t instanceof IllegalStateException || t instanceof NullPointerException) {
resultCode = AppSearchResult.RESULT_INTERNAL_ERROR;
} else if (t instanceof IllegalArgumentException) {
resultCode = AppSearchResult.RESULT_INVALID_ARGUMENT;
@@ -262,6 +272,6 @@
} else {
resultCode = AppSearchResult.RESULT_UNKNOWN_ERROR;
}
- return AppSearchResult.newFailedResult(resultCode, t.getMessage());
+ return AppSearchResult.newFailedResult(resultCode, exceptionClass + ": " + t.getMessage());
}
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index ac91bdb..c85c4c3 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -19,9 +19,9 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.app.appsearch.exceptions.AppSearchException;
import android.app.appsearch.util.SchemaMigrationUtil;
import android.os.Bundle;
-import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.ArrayMap;
@@ -274,12 +274,14 @@
mService.putDocuments(mPackageName, mDatabaseName, documentBundles, mUserId,
/*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
new IAppSearchBatchResultCallback.Stub() {
+ @Override
public void onResult(AppSearchBatchResult result) {
executor.execute(() -> callback.onResult(result));
}
- public void onSystemError(ParcelableException exception) {
- executor.execute(() -> callback.onSystemError(exception.getCause()));
+ @Override
+ public void onSystemError(AppSearchResult result) {
+ executor.execute(() -> sendSystemErrorToCallback(result, callback));
}
});
mIsMutated = true;
@@ -321,6 +323,7 @@
request.getProjectionsInternal(),
mUserId,
new IAppSearchBatchResultCallback.Stub() {
+ @Override
public void onResult(AppSearchBatchResult result) {
executor.execute(() -> {
AppSearchBatchResult.Builder<String, GenericDocument>
@@ -359,8 +362,9 @@
});
}
- public void onSystemError(ParcelableException exception) {
- executor.execute(() -> callback.onSystemError(exception.getCause()));
+ @Override
+ public void onSystemError(AppSearchResult result) {
+ executor.execute(() -> sendSystemErrorToCallback(result, callback));
}
});
} catch (RemoteException e) {
@@ -515,12 +519,14 @@
mService.removeByUri(mPackageName, mDatabaseName, request.getNamespace(),
new ArrayList<>(request.getUris()), mUserId,
new IAppSearchBatchResultCallback.Stub() {
+ @Override
public void onResult(AppSearchBatchResult result) {
executor.execute(() -> callback.onResult(result));
}
- public void onSystemError(ParcelableException exception) {
- executor.execute(() -> callback.onSystemError(exception.getCause()));
+ @Override
+ public void onSystemError(AppSearchResult result) {
+ executor.execute(() -> sendSystemErrorToCallback(result, callback));
}
});
mIsMutated = true;
@@ -817,4 +823,21 @@
}
});
}
+
+ /**
+ * Calls {@link BatchResultCallback#onSystemError} with a throwable derived from the given
+ * failed {@link AppSearchResult}.
+ *
+ * <p>The {@link AppSearchResult} generally comes from
+ * {@link IAppSearchBatchResultCallback#onSystemError}.
+ *
+ * <p>This method should be called from the callback executor thread.
+ */
+ private void sendSystemErrorToCallback(
+ @NonNull AppSearchResult<?> failedResult, @NonNull BatchResultCallback<?, ?> callback) {
+ Preconditions.checkArgument(!failedResult.isSuccess());
+ Throwable throwable = new AppSearchException(
+ failedResult.getResultCode(), failedResult.getErrorMessage());
+ callback.onSystemError(throwable);
+ }
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/BatchResultCallback.java b/apex/appsearch/framework/java/android/app/appsearch/BatchResultCallback.java
index 49049b6..28f8a7a 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/BatchResultCallback.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/BatchResultCallback.java
@@ -36,13 +36,23 @@
void onResult(@NonNull AppSearchBatchResult<KeyType, ValueType> result);
/**
- * Called when a system error occurred.
+ * Called when a system error occurs.
*
- * @param throwable The cause throwable.
+ * <p>This method is only called the infrastructure is fundamentally broken or unavailable, such
+ * that none of the requests could be started. For example, it will be called if the AppSearch
+ * service unexpectedly fails to initialize and can't be recovered by any means, or if
+ * communicating to the server over Binder fails (e.g. system service crashed or device is
+ * rebooting).
+ *
+ * <p>The error is not expected to be recoverable and there is no specific recommended action
+ * other than displaying a permanent message to the user.
+ *
+ * <p>Normal errors that are caused by invalid inputs or recoverable/retriable situations
+ * are reported associated with the input that caused them via the {@link #onResult} method.
+ *
+ * @param throwable an exception describing the system error
*/
default void onSystemError(@Nullable Throwable throwable) {
- if (throwable != null) {
- throw new RuntimeException(throwable);
- }
+ throw new RuntimeException("Unrecoverable system error", throwable);
}
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchBatchResultCallback.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchBatchResultCallback.aidl
index b1bbd18..64b331e 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchBatchResultCallback.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchBatchResultCallback.aidl
@@ -16,10 +16,10 @@
package android.app.appsearch;
import android.app.appsearch.AppSearchBatchResult;
-import android.os.ParcelableException;
+import android.app.appsearch.AppSearchResult;
/** {@hide} */
oneway interface IAppSearchBatchResultCallback {
void onResult(in AppSearchBatchResult result);
- void onSystemError(in ParcelableException exception);
-}
\ No newline at end of file
+ void onSystemError(in AppSearchResult result);
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl
index 27729a5..299c9957 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl
@@ -16,9 +16,8 @@
package android.app.appsearch;
import android.app.appsearch.AppSearchResult;
-import android.os.ParcelableException;
/** {@hide} */
oneway interface IAppSearchResultCallback {
void onResult(in AppSearchResult result);
-}
\ No newline at end of file
+}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
index a8048dc..2368bdb 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -51,7 +51,7 @@
/** @hide */
public AppSearchSchema(@NonNull Bundle bundle) {
- Preconditions.checkNotNull(bundle);
+ Objects.requireNonNull(bundle);
mBundle = bundle;
}
@@ -125,7 +125,7 @@
/** Creates a new {@link AppSearchSchema.Builder}. */
public Builder(@NonNull String schemaType) {
- Preconditions.checkNotNull(schemaType);
+ Objects.requireNonNull(schemaType);
mSchemaType = schemaType;
}
@@ -133,7 +133,7 @@
@NonNull
public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(propertyConfig);
+ Objects.requireNonNull(propertyConfig);
String name = propertyConfig.getName();
if (!mPropertyNames.add(name)) {
throw new IllegalSchemaException("Property defined more than once: " + name);
@@ -246,7 +246,7 @@
@Nullable private Integer mHashCode;
PropertyConfig(@NonNull Bundle bundle) {
- mBundle = Preconditions.checkNotNull(bundle);
+ mBundle = Objects.requireNonNull(bundle);
}
@Override
@@ -712,7 +712,7 @@
/** Returns the logical schema-type of the contents of this document property. */
@NonNull
public String getSchemaType() {
- return Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD));
+ return Objects.requireNonNull(mBundle.getString(SCHEMA_TYPE_FIELD));
}
/**
@@ -755,7 +755,7 @@
@NonNull
public DocumentPropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(schemaType);
+ Objects.requireNonNull(schemaType);
mBundle.putString(SCHEMA_TYPE_FIELD, schemaType);
return this;
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
index 8c9d950..e3b3a85 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -31,6 +31,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Objects;
import java.util.Set;
/**
@@ -101,11 +102,11 @@
* @hide
*/
public GenericDocument(@NonNull Bundle bundle) {
- Preconditions.checkNotNull(bundle);
+ Objects.requireNonNull(bundle);
mBundle = bundle;
- mProperties = Preconditions.checkNotNull(bundle.getParcelable(PROPERTIES_FIELD));
- mUri = Preconditions.checkNotNull(mBundle.getString(URI_FIELD));
- mSchemaType = Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD));
+ mProperties = Objects.requireNonNull(bundle.getParcelable(PROPERTIES_FIELD));
+ mUri = Objects.requireNonNull(mBundle.getString(URI_FIELD));
+ mSchemaType = Objects.requireNonNull(mBundle.getString(SCHEMA_TYPE_FIELD));
mCreationTimestampMillis =
mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis());
}
@@ -199,7 +200,7 @@
*/
@Nullable
public Object getProperty(@NonNull String key) {
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
Object property = mProperties.get(key);
if (property instanceof ArrayList) {
return getPropertyBytesArray(key);
@@ -218,7 +219,7 @@
*/
@Nullable
public String getPropertyString(@NonNull String key) {
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
String[] propertyArray = getPropertyStringArray(key);
if (propertyArray == null || propertyArray.length == 0) {
return null;
@@ -235,7 +236,7 @@
* there is no such key or the value is of a different type.
*/
public long getPropertyLong(@NonNull String key) {
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
long[] propertyArray = getPropertyLongArray(key);
if (propertyArray == null || propertyArray.length == 0) {
return 0;
@@ -252,7 +253,7 @@
* if there is no such key or the value is of a different type.
*/
public double getPropertyDouble(@NonNull String key) {
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
double[] propertyArray = getPropertyDoubleArray(key);
if (propertyArray == null || propertyArray.length == 0) {
return 0.0;
@@ -269,7 +270,7 @@
* false} if there is no such key or the value is of a different type.
*/
public boolean getPropertyBoolean(@NonNull String key) {
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
boolean[] propertyArray = getPropertyBooleanArray(key);
if (propertyArray == null || propertyArray.length == 0) {
return false;
@@ -287,7 +288,7 @@
*/
@Nullable
public byte[] getPropertyBytes(@NonNull String key) {
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
byte[][] propertyArray = getPropertyBytesArray(key);
if (propertyArray == null || propertyArray.length == 0) {
return null;
@@ -305,7 +306,7 @@
*/
@Nullable
public GenericDocument getPropertyDocument(@NonNull String key) {
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
GenericDocument[] propertyArray = getPropertyDocumentArray(key);
if (propertyArray == null || propertyArray.length == 0) {
return null;
@@ -342,7 +343,7 @@
*/
@Nullable
public String[] getPropertyStringArray(@NonNull String key) {
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
return getAndCastPropertyArray(key, String[].class);
}
@@ -355,7 +356,7 @@
*/
@Nullable
public long[] getPropertyLongArray(@NonNull String key) {
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
return getAndCastPropertyArray(key, long[].class);
}
@@ -368,7 +369,7 @@
*/
@Nullable
public double[] getPropertyDoubleArray(@NonNull String key) {
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
return getAndCastPropertyArray(key, double[].class);
}
@@ -381,7 +382,7 @@
*/
@Nullable
public boolean[] getPropertyBooleanArray(@NonNull String key) {
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
return getAndCastPropertyArray(key, boolean[].class);
}
@@ -396,7 +397,7 @@
@Nullable
@SuppressWarnings("unchecked")
public byte[][] getPropertyBytesArray(@NonNull String key) {
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
ArrayList<Bundle> bundles = getAndCastPropertyArray(key, ArrayList.class);
if (bundles == null || bundles.size() == 0) {
return null;
@@ -428,7 +429,7 @@
@SuppressLint("ArrayReturn")
@Nullable
public GenericDocument[] getPropertyDocumentArray(@NonNull String key) {
- Preconditions.checkNotNull(key);
+ Objects.requireNonNull(key);
Parcelable[] bundles = getAndCastPropertyArray(key, Parcelable[].class);
if (bundles == null || bundles.length == 0) {
return null;
@@ -591,9 +592,9 @@
*/
@SuppressWarnings("unchecked")
public Builder(@NonNull String namespace, @NonNull String uri, @NonNull String schemaType) {
- Preconditions.checkNotNull(namespace);
- Preconditions.checkNotNull(uri);
- Preconditions.checkNotNull(schemaType);
+ Objects.requireNonNull(namespace);
+ Objects.requireNonNull(uri);
+ Objects.requireNonNull(schemaType);
mBuilderTypeInstance = (BuilderType) this;
mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace);
mBundle.putString(GenericDocument.URI_FIELD, uri);
@@ -682,8 +683,8 @@
@NonNull
public BuilderType setPropertyString(@NonNull String key, @NonNull String... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(key);
- Preconditions.checkNotNull(values);
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(values);
putInPropertyBundle(key, values);
return mBuilderTypeInstance;
}
@@ -694,15 +695,14 @@
*
* @param key the key associated with the {@code values}.
* @param values the {@code boolean} values of the property.
- * @throws IllegalArgumentException if no values are provided or if values exceed maximum
- * repeated property length.
+ * @throws IllegalArgumentException if values exceed maximum repeated property length.
* @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyBoolean(@NonNull String key, @NonNull boolean... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(key);
- Preconditions.checkNotNull(values);
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(values);
putInPropertyBundle(key, values);
return mBuilderTypeInstance;
}
@@ -712,15 +712,14 @@
*
* @param key the key associated with the {@code values}.
* @param values the {@code long} values of the property.
- * @throws IllegalArgumentException if no values are provided or if values exceed maximum
- * repeated property length.
+ * @throws IllegalArgumentException if values exceed maximum repeated property length.
* @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyLong(@NonNull String key, @NonNull long... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(key);
- Preconditions.checkNotNull(values);
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(values);
putInPropertyBundle(key, values);
return mBuilderTypeInstance;
}
@@ -730,15 +729,14 @@
*
* @param key the key associated with the {@code values}.
* @param values the {@code double} values of the property.
- * @throws IllegalArgumentException if no values are provided or if values exceed maximum
- * repeated property length.
+ * @throws IllegalArgumentException if values exceed maximum repeated property length.
* @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyDouble(@NonNull String key, @NonNull double... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(key);
- Preconditions.checkNotNull(values);
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(values);
putInPropertyBundle(key, values);
return mBuilderTypeInstance;
}
@@ -755,8 +753,8 @@
@NonNull
public BuilderType setPropertyBytes(@NonNull String key, @NonNull byte[]... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(key);
- Preconditions.checkNotNull(values);
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(values);
putInPropertyBundle(key, values);
return mBuilderTypeInstance;
}
@@ -776,8 +774,8 @@
public BuilderType setPropertyDocument(
@NonNull String key, @NonNull GenericDocument... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(key);
- Preconditions.checkNotNull(values);
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(values);
putInPropertyBundle(key, values);
return mBuilderTypeInstance;
}
@@ -850,9 +848,7 @@
}
private static void validateRepeatedPropertyLength(@NonNull String key, int length) {
- if (length == 0) {
- throw new IllegalArgumentException("The input array is empty.");
- } else if (length > MAX_REPEATED_PROPERTY_LENGTH) {
+ if (length > MAX_REPEATED_PROPERTY_LENGTH) {
throw new IllegalArgumentException(
"Repeated property \""
+ key
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
index 1719e14..4dc3225 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -28,6 +28,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -52,9 +53,9 @@
@NonNull String namespace,
@NonNull Set<String> uris,
@NonNull Map<String, List<String>> typePropertyPathsMap) {
- mNamespace = Preconditions.checkNotNull(namespace);
- mUris = Preconditions.checkNotNull(uris);
- mTypePropertyPathsMap = Preconditions.checkNotNull(typePropertyPathsMap);
+ mNamespace = Objects.requireNonNull(namespace);
+ mUris = Objects.requireNonNull(uris);
+ mTypePropertyPathsMap = Objects.requireNonNull(typePropertyPathsMap);
}
/** Returns the namespace attached to the request. */
@@ -114,7 +115,7 @@
/** Creates a {@link GetByUriRequest.Builder} instance. */
public Builder(@NonNull String namespace) {
- mNamespace = Preconditions.checkNotNull(namespace);
+ mNamespace = Objects.requireNonNull(namespace);
}
/**
@@ -124,7 +125,7 @@
*/
@NonNull
public Builder addUris(@NonNull String... uris) {
- Preconditions.checkNotNull(uris);
+ Objects.requireNonNull(uris);
return addUris(Arrays.asList(uris));
}
@@ -136,7 +137,7 @@
@NonNull
public Builder addUris(@NonNull Collection<String> uris) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(uris);
+ Objects.requireNonNull(uris);
mUris.addAll(uris);
return this;
}
@@ -161,11 +162,11 @@
public Builder addProjection(
@NonNull String schemaType, @NonNull Collection<String> propertyPaths) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(schemaType);
- Preconditions.checkNotNull(propertyPaths);
+ Objects.requireNonNull(schemaType);
+ Objects.requireNonNull(propertyPaths);
List<String> propertyPathsList = new ArrayList<>(propertyPaths.size());
for (String propertyPath : propertyPaths) {
- Preconditions.checkNotNull(propertyPath);
+ Objects.requireNonNull(propertyPath);
propertyPathsList.add(propertyPath);
}
mProjectionTypePropertyPaths.put(schemaType, propertyPathsList);
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java
index 1f56ef3..691ef4f 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java
@@ -24,6 +24,7 @@
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.Set;
/** The response class of {@link AppSearchSession#getSchema} */
@@ -34,7 +35,7 @@
private final Bundle mBundle;
GetSchemaResponse(@NonNull Bundle bundle) {
- mBundle = Preconditions.checkNotNull(bundle);
+ mBundle = Objects.requireNonNull(bundle);
}
/**
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java b/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java
index bfb9323..4f63bae 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java
@@ -20,7 +20,7 @@
import android.app.appsearch.util.BundleUtil;
import android.os.Bundle;
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
/** This class represents a uniquely identifiable package. */
public class PackageIdentifier {
@@ -43,7 +43,7 @@
/** @hide */
public PackageIdentifier(@NonNull Bundle bundle) {
- mBundle = Preconditions.checkNotNull(bundle);
+ mBundle = Objects.requireNonNull(bundle);
}
/** @hide */
@@ -54,12 +54,12 @@
@NonNull
public String getPackageName() {
- return Preconditions.checkNotNull(mBundle.getString(PACKAGE_NAME_FIELD));
+ return Objects.requireNonNull(mBundle.getString(PACKAGE_NAME_FIELD));
}
@NonNull
public byte[] getSha256Certificate() {
- return Preconditions.checkNotNull(mBundle.getByteArray(SHA256_CERTIFICATE_FIELD));
+ return Objects.requireNonNull(mBundle.getByteArray(SHA256_CERTIFICATE_FIELD));
}
@Override
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
index 01473be..b49e0e8 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java
@@ -26,6 +26,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* Encapsulates a request to index documents into an {@link AppSearchSession} database.
@@ -61,7 +62,7 @@
*/
@NonNull
public Builder addGenericDocuments(@NonNull GenericDocument... documents) {
- Preconditions.checkNotNull(documents);
+ Objects.requireNonNull(documents);
return addGenericDocuments(Arrays.asList(documents));
}
@@ -74,7 +75,7 @@
public Builder addGenericDocuments(
@NonNull Collection<? extends GenericDocument> documents) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(documents);
+ Objects.requireNonNull(documents);
mDocuments.addAll(documents);
return this;
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
index 8da68c0..4dcad68 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java
@@ -24,6 +24,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Objects;
import java.util.Set;
/**
@@ -65,7 +66,7 @@
/** Creates a {@link RemoveByUriRequest.Builder} instance. */
public Builder(@NonNull String namespace) {
- mNamespace = Preconditions.checkNotNull(namespace);
+ mNamespace = Objects.requireNonNull(namespace);
}
/**
@@ -75,7 +76,7 @@
*/
@NonNull
public Builder addUris(@NonNull String... uris) {
- Preconditions.checkNotNull(uris);
+ Objects.requireNonNull(uris);
return addUris(Arrays.asList(uris));
}
@@ -87,7 +88,7 @@
@NonNull
public Builder addUris(@NonNull Collection<String> uris) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(uris);
+ Objects.requireNonNull(uris);
mUris.addAll(uris);
return this;
}
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 2e152f8..8aff3b4 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java
@@ -20,6 +20,8 @@
import com.android.internal.util.Preconditions;
+import java.util.Objects;
+
/**
* A request to report usage of a document owned by another app from a system UI surface.
*
@@ -42,10 +44,10 @@
@NonNull String namespace,
@NonNull String uri,
long usageTimeMillis) {
- mPackageName = Preconditions.checkNotNull(packageName);
- mDatabase = Preconditions.checkNotNull(database);
- mNamespace = Preconditions.checkNotNull(namespace);
- mUri = Preconditions.checkNotNull(uri);
+ mPackageName = Objects.requireNonNull(packageName);
+ mDatabase = Objects.requireNonNull(database);
+ mNamespace = Objects.requireNonNull(namespace);
+ mUri = Objects.requireNonNull(uri);
mUsageTimeMillis = usageTimeMillis;
}
@@ -95,9 +97,9 @@
/** Creates a {@link ReportSystemUsageRequest.Builder} instance. */
public Builder(
@NonNull String packageName, @NonNull String database, @NonNull String namespace) {
- mPackageName = Preconditions.checkNotNull(packageName);
- mDatabase = Preconditions.checkNotNull(database);
- mNamespace = Preconditions.checkNotNull(namespace);
+ mPackageName = Objects.requireNonNull(packageName);
+ mDatabase = Objects.requireNonNull(database);
+ mNamespace = Objects.requireNonNull(namespace);
}
/**
@@ -110,7 +112,7 @@
@NonNull
public ReportSystemUsageRequest.Builder setUri(@NonNull String uri) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(uri);
+ Objects.requireNonNull(uri);
mUri = uri;
return this;
}
@@ -142,7 +144,7 @@
@NonNull
public ReportSystemUsageRequest build() {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(mUri, "ReportUsageRequest is missing a URI");
+ Objects.requireNonNull(mUri, "ReportUsageRequest is missing a URI");
if (mUsageTimeMillis == null) {
mUsageTimeMillis = System.currentTimeMillis();
}
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 646e73c..925bde9 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java
@@ -20,6 +20,8 @@
import com.android.internal.util.Preconditions;
+import java.util.Objects;
+
/**
* A request to report usage of a document.
*
@@ -33,8 +35,8 @@
private final long mUsageTimeMillis;
ReportUsageRequest(@NonNull String namespace, @NonNull String uri, long usageTimeMillis) {
- mNamespace = Preconditions.checkNotNull(namespace);
- mUri = Preconditions.checkNotNull(uri);
+ mNamespace = Objects.requireNonNull(namespace);
+ mUri = Objects.requireNonNull(uri);
mUsageTimeMillis = usageTimeMillis;
}
@@ -69,7 +71,7 @@
/** Creates a {@link ReportUsageRequest.Builder} instance. */
public Builder(@NonNull String namespace) {
- mNamespace = Preconditions.checkNotNull(namespace);
+ mNamespace = Objects.requireNonNull(namespace);
}
/**
@@ -82,7 +84,7 @@
@NonNull
public ReportUsageRequest.Builder setUri(@NonNull String uri) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(uri);
+ Objects.requireNonNull(uri);
mUri = uri;
return this;
}
@@ -114,7 +116,7 @@
@NonNull
public ReportUsageRequest build() {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(mUri, "ReportUsageRequest is missing a URI");
+ Objects.requireNonNull(mUri, "ReportUsageRequest is missing a URI");
if (mUsageTimeMillis == null) {
mUsageTimeMillis = System.currentTimeMillis();
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
index 55a228d..432f838 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java
@@ -59,7 +59,7 @@
/** @hide */
public SearchResult(@NonNull Bundle bundle) {
- mBundle = Preconditions.checkNotNull(bundle);
+ mBundle = Objects.requireNonNull(bundle);
}
/** @hide */
@@ -77,8 +77,7 @@
public GenericDocument getGenericDocument() {
if (mDocument == null) {
mDocument =
- new GenericDocument(
- Preconditions.checkNotNull(mBundle.getBundle(DOCUMENT_FIELD)));
+ new GenericDocument(Objects.requireNonNull(mBundle.getBundle(DOCUMENT_FIELD)));
}
return mDocument;
}
@@ -95,7 +94,7 @@
public List<MatchInfo> getMatches() {
if (mMatches == null) {
List<Bundle> matchBundles =
- Preconditions.checkNotNull(mBundle.getParcelableArrayList(MATCHES_FIELD));
+ Objects.requireNonNull(mBundle.getParcelableArrayList(MATCHES_FIELD));
mMatches = new ArrayList<>(matchBundles.size());
for (int i = 0; i < matchBundles.size(); i++) {
MatchInfo matchInfo = new MatchInfo(matchBundles.get(i), getGenericDocument());
@@ -112,7 +111,7 @@
*/
@NonNull
public String getPackageName() {
- return Preconditions.checkNotNull(mBundle.getString(PACKAGE_NAME_FIELD));
+ return Objects.requireNonNull(mBundle.getString(PACKAGE_NAME_FIELD));
}
/**
@@ -122,7 +121,7 @@
*/
@NonNull
public String getDatabaseName() {
- return Preconditions.checkNotNull(mBundle.getString(DATABASE_NAME_FIELD));
+ return Objects.requireNonNull(mBundle.getString(DATABASE_NAME_FIELD));
}
/**
@@ -169,8 +168,8 @@
* @param databaseName the database name the matched document belongs to.
*/
public Builder(@NonNull String packageName, @NonNull String databaseName) {
- mBundle.putString(PACKAGE_NAME_FIELD, Preconditions.checkNotNull(packageName));
- mBundle.putString(DATABASE_NAME_FIELD, Preconditions.checkNotNull(databaseName));
+ mBundle.putString(PACKAGE_NAME_FIELD, Objects.requireNonNull(packageName));
+ mBundle.putString(DATABASE_NAME_FIELD, Objects.requireNonNull(databaseName));
}
/**
@@ -312,9 +311,9 @@
@Nullable private MatchRange mWindowRange;
MatchInfo(@NonNull Bundle bundle, @Nullable GenericDocument document) {
- mBundle = Preconditions.checkNotNull(bundle);
+ mBundle = Objects.requireNonNull(bundle);
mDocument = document;
- mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD));
+ mPropertyPath = Objects.requireNonNull(bundle.getString(PROPERTY_PATH_FIELD));
}
/**
@@ -449,7 +448,7 @@
Preconditions.checkState(!mBuilt, "Builder has already been used");
mBundle.putString(
SearchResult.MatchInfo.PROPERTY_PATH_FIELD,
- Preconditions.checkNotNull(propertyPath));
+ Objects.requireNonNull(propertyPath));
return this;
}
@@ -461,7 +460,7 @@
@NonNull
public Builder setExactMatchRange(@NonNull MatchRange matchRange) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(matchRange);
+ Objects.requireNonNull(matchRange);
mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_LOWER_FIELD, matchRange.getStart());
mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_UPPER_FIELD, matchRange.getEnd());
return this;
@@ -475,7 +474,7 @@
@NonNull
public Builder setSnippetRange(@NonNull MatchRange matchRange) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(matchRange);
+ Objects.requireNonNull(matchRange);
mBundle.putInt(MatchInfo.SNIPPET_RANGE_LOWER_FIELD, matchRange.getStart());
mBundle.putInt(MatchInfo.SNIPPET_RANGE_UPPER_FIELD, matchRange.getEnd());
return this;
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java
index dbd09d6..4853b5b 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java
@@ -20,11 +20,10 @@
import android.annotation.Nullable;
import android.os.Bundle;
-import com.android.internal.util.Preconditions;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* This class represents a page of {@link SearchResult}s
@@ -41,7 +40,7 @@
@NonNull private final Bundle mBundle;
public SearchResultPage(@NonNull Bundle bundle) {
- mBundle = Preconditions.checkNotNull(bundle);
+ mBundle = Objects.requireNonNull(bundle);
mNextPageToken = mBundle.getLong(NEXT_PAGE_TOKEN_FIELD);
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
index 19d9430..d466bf1 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
@@ -34,6 +34,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -176,7 +177,7 @@
/** @hide */
public SearchSpec(@NonNull Bundle bundle) {
- Preconditions.checkNotNull(bundle);
+ Objects.requireNonNull(bundle);
mBundle = bundle;
}
@@ -342,7 +343,7 @@
*/
@NonNull
public Builder addFilterSchemas(@NonNull String... schemas) {
- Preconditions.checkNotNull(schemas);
+ Objects.requireNonNull(schemas);
Preconditions.checkState(!mBuilt, "Builder has already been used");
return addFilterSchemas(Arrays.asList(schemas));
}
@@ -355,7 +356,7 @@
*/
@NonNull
public Builder addFilterSchemas(@NonNull Collection<String> schemas) {
- Preconditions.checkNotNull(schemas);
+ Objects.requireNonNull(schemas);
Preconditions.checkState(!mBuilt, "Builder has already been used");
mSchemas.addAll(schemas);
return this;
@@ -369,7 +370,7 @@
*/
@NonNull
public Builder addFilterNamespaces(@NonNull String... namespaces) {
- Preconditions.checkNotNull(namespaces);
+ Objects.requireNonNull(namespaces);
Preconditions.checkState(!mBuilt, "Builder has already been used");
return addFilterNamespaces(Arrays.asList(namespaces));
}
@@ -382,7 +383,7 @@
*/
@NonNull
public Builder addFilterNamespaces(@NonNull Collection<String> namespaces) {
- Preconditions.checkNotNull(namespaces);
+ Objects.requireNonNull(namespaces);
Preconditions.checkState(!mBuilt, "Builder has already been used");
mNamespaces.addAll(namespaces);
return this;
@@ -398,7 +399,7 @@
*/
@NonNull
public Builder addFilterPackageNames(@NonNull String... packageNames) {
- Preconditions.checkNotNull(packageNames);
+ Objects.requireNonNull(packageNames);
Preconditions.checkState(!mBuilt, "Builder has already been used");
return addFilterPackageNames(Arrays.asList(packageNames));
}
@@ -413,7 +414,7 @@
*/
@NonNull
public Builder addFilterPackageNames(@NonNull Collection<String> packageNames) {
- Preconditions.checkNotNull(packageNames);
+ Objects.requireNonNull(packageNames);
Preconditions.checkState(!mBuilt, "Builder has already been used");
mPackageNames.addAll(packageNames);
return this;
@@ -586,11 +587,11 @@
public SearchSpec.Builder addProjection(
@NonNull String schema, @NonNull Collection<String> propertyPaths) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(schema);
- Preconditions.checkNotNull(propertyPaths);
+ Objects.requireNonNull(schema);
+ Objects.requireNonNull(propertyPaths);
ArrayList<String> propertyPathsArrayList = new ArrayList<>(propertyPaths.size());
for (String propertyPath : propertyPaths) {
- Preconditions.checkNotNull(propertyPath);
+ Objects.requireNonNull(propertyPath);
propertyPathsArrayList.add(propertyPath);
}
mProjectionTypePropertyMasks.putStringArrayList(schema, propertyPathsArrayList);
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
index 5672bc7..8f7a0bf 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -28,6 +28,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -94,10 +95,10 @@
@NonNull Map<String, Migrator> migrators,
boolean forceOverride,
int version) {
- mSchemas = Preconditions.checkNotNull(schemas);
- mSchemasNotDisplayedBySystem = Preconditions.checkNotNull(schemasNotDisplayedBySystem);
- mSchemasVisibleToPackages = Preconditions.checkNotNull(schemasVisibleToPackages);
- mMigrators = Preconditions.checkNotNull(migrators);
+ mSchemas = Objects.requireNonNull(schemas);
+ mSchemasNotDisplayedBySystem = Objects.requireNonNull(schemasNotDisplayedBySystem);
+ mSchemasVisibleToPackages = Objects.requireNonNull(schemasVisibleToPackages);
+ mMigrators = Objects.requireNonNull(migrators);
mForceOverride = forceOverride;
mVersion = version;
}
@@ -192,7 +193,7 @@
*/
@NonNull
public Builder addSchemas(@NonNull AppSearchSchema... schemas) {
- Preconditions.checkNotNull(schemas);
+ Objects.requireNonNull(schemas);
return addSchemas(Arrays.asList(schemas));
}
@@ -206,7 +207,7 @@
@NonNull
public Builder addSchemas(@NonNull Collection<AppSearchSchema> schemas) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- Preconditions.checkNotNull(schemas);
+ Objects.requireNonNull(schemas);
mSchemas.addAll(schemas);
return this;
}
@@ -231,7 +232,7 @@
@NonNull
public Builder setSchemaTypeDisplayedBySystem(
@NonNull String schemaType, boolean displayed) {
- Preconditions.checkNotNull(schemaType);
+ Objects.requireNonNull(schemaType);
Preconditions.checkState(!mBuilt, "Builder has already been used");
if (displayed) {
@@ -270,8 +271,8 @@
@NonNull String schemaType,
boolean visible,
@NonNull PackageIdentifier packageIdentifier) {
- Preconditions.checkNotNull(schemaType);
- Preconditions.checkNotNull(packageIdentifier);
+ Objects.requireNonNull(schemaType);
+ Objects.requireNonNull(packageIdentifier);
Preconditions.checkState(!mBuilt, "Builder has already been used");
Set<PackageIdentifier> packageIdentifiers = mSchemasVisibleToPackages.get(schemaType);
@@ -321,8 +322,8 @@
@NonNull
@SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects.
public Builder setMigrator(@NonNull String schemaType, @NonNull Migrator migrator) {
- Preconditions.checkNotNull(schemaType);
- Preconditions.checkNotNull(migrator);
+ Objects.requireNonNull(schemaType);
+ Objects.requireNonNull(migrator);
mMigrators.put(schemaType, migrator);
return this;
}
@@ -350,7 +351,7 @@
*/
@NonNull
public Builder setMigrators(@NonNull Map<String, Migrator> migrators) {
- Preconditions.checkNotNull(migrators);
+ Objects.requireNonNull(migrators);
mMigrators.putAll(migrators);
return this;
}
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 d63e437..7be589f 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -27,6 +27,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/** The response class of {@link AppSearchSession#setSchema} */
@@ -61,8 +62,8 @@
@Nullable private Set<String> mIncompatibleTypes;
SetSchemaResponse(@NonNull Bundle bundle, @NonNull List<MigrationFailure> migrationFailures) {
- mBundle = Preconditions.checkNotNull(bundle);
- mMigrationFailures = Preconditions.checkNotNull(migrationFailures);
+ mBundle = Objects.requireNonNull(bundle);
+ mMigrationFailures = Objects.requireNonNull(migrationFailures);
}
SetSchemaResponse(@NonNull Bundle bundle) {
@@ -103,7 +104,7 @@
if (mDeletedTypes == null) {
mDeletedTypes =
new ArraySet<>(
- Preconditions.checkNotNull(
+ Objects.requireNonNull(
mBundle.getStringArrayList(DELETED_TYPES_FIELD)));
}
return Collections.unmodifiableSet(mDeletedTypes);
@@ -118,7 +119,7 @@
if (mMigratedTypes == null) {
mMigratedTypes =
new ArraySet<>(
- Preconditions.checkNotNull(
+ Objects.requireNonNull(
mBundle.getStringArrayList(MIGRATED_TYPES_FIELD)));
}
return Collections.unmodifiableSet(mMigratedTypes);
@@ -139,7 +140,7 @@
if (mIncompatibleTypes == null) {
mIncompatibleTypes =
new ArraySet<>(
- Preconditions.checkNotNull(
+ Objects.requireNonNull(
mBundle.getStringArrayList(INCOMPATIBLE_TYPES_FIELD)));
}
return Collections.unmodifiableSet(mIncompatibleTypes);
@@ -173,7 +174,7 @@
public Builder addMigrationFailures(
@NonNull Collection<MigrationFailure> migrationFailures) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mMigrationFailures.addAll(Preconditions.checkNotNull(migrationFailures));
+ mMigrationFailures.addAll(Objects.requireNonNull(migrationFailures));
return this;
}
@@ -181,7 +182,7 @@
@NonNull
public Builder addMigrationFailure(@NonNull MigrationFailure migrationFailure) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mMigrationFailures.add(Preconditions.checkNotNull(migrationFailure));
+ mMigrationFailures.add(Objects.requireNonNull(migrationFailure));
return this;
}
@@ -189,7 +190,7 @@
@NonNull
public Builder addDeletedTypes(@NonNull Collection<String> deletedTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mDeletedTypes.addAll(Preconditions.checkNotNull(deletedTypes));
+ mDeletedTypes.addAll(Objects.requireNonNull(deletedTypes));
return this;
}
@@ -197,7 +198,7 @@
@NonNull
public Builder addDeletedType(@NonNull String deletedType) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mDeletedTypes.add(Preconditions.checkNotNull(deletedType));
+ mDeletedTypes.add(Objects.requireNonNull(deletedType));
return this;
}
@@ -205,7 +206,7 @@
@NonNull
public Builder addIncompatibleTypes(@NonNull Collection<String> incompatibleTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mIncompatibleTypes.addAll(Preconditions.checkNotNull(incompatibleTypes));
+ mIncompatibleTypes.addAll(Objects.requireNonNull(incompatibleTypes));
return this;
}
@@ -213,7 +214,7 @@
@NonNull
public Builder addIncompatibleType(@NonNull String incompatibleType) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mIncompatibleTypes.add(Preconditions.checkNotNull(incompatibleType));
+ mIncompatibleTypes.add(Objects.requireNonNull(incompatibleType));
return this;
}
@@ -221,7 +222,7 @@
@NonNull
public Builder addMigratedTypes(@NonNull Collection<String> migratedTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mMigratedTypes.addAll(Preconditions.checkNotNull(migratedTypes));
+ mMigratedTypes.addAll(Objects.requireNonNull(migratedTypes));
return this;
}
@@ -229,7 +230,7 @@
@NonNull
public Builder addMigratedType(@NonNull String migratedType) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mMigratedTypes.add(Preconditions.checkNotNull(migratedType));
+ mMigratedTypes.add(Objects.requireNonNull(migratedType));
return this;
}
@@ -318,7 +319,7 @@
@NonNull
public Builder setSchemaType(@NonNull String schemaType) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mSchemaType = Preconditions.checkNotNull(schemaType);
+ mSchemaType = Objects.requireNonNull(schemaType);
return this;
}
@@ -326,7 +327,7 @@
@NonNull
public Builder setNamespace(@NonNull String namespace) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mNamespace = Preconditions.checkNotNull(namespace);
+ mNamespace = Objects.requireNonNull(namespace);
return this;
}
@@ -334,7 +335,7 @@
@NonNull
public Builder setUri(@NonNull String uri) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
- mUri = Preconditions.checkNotNull(uri);
+ mUri = Objects.requireNonNull(uri);
return this;
}
@@ -343,7 +344,7 @@
public Builder setAppSearchResult(@NonNull AppSearchResult<Void> appSearchResult) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkState(!appSearchResult.isSuccess(), "Input a success result");
- mFailureResult = Preconditions.checkNotNull(appSearchResult);
+ mFailureResult = Objects.requireNonNull(appSearchResult);
return this;
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/StorageInfo.java b/apex/appsearch/framework/java/external/android/app/appsearch/StorageInfo.java
index dc04cf3..502b939 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/StorageInfo.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/StorageInfo.java
@@ -21,6 +21,8 @@
import com.android.internal.util.Preconditions;
+import java.util.Objects;
+
/** The response class of {@code AppSearchSession#getStorageInfo}. */
public class StorageInfo {
@@ -31,7 +33,7 @@
private final Bundle mBundle;
StorageInfo(@NonNull Bundle bundle) {
- mBundle = Preconditions.checkNotNull(bundle);
+ mBundle = Objects.requireNonNull(bundle);
}
/**
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
index 32d7e043..10e014b 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
@@ -37,7 +37,11 @@
public final class SchemaMigrationUtil {
private SchemaMigrationUtil() {}
- /** Returns all active {@link Migrator}s that need to be triggered in this migration. */
+ /**
+ * Returns all active {@link Migrator}s that need to be triggered in this migration.
+ *
+ * <p>{@link Migrator#shouldMigrate} returns {@code true} will make the {@link Migrator} active.
+ */
@NonNull
public static Map<String, Migrator> getActiveMigrators(
@NonNull Set<AppSearchSchema> existingSchemas,
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 509877e..f6f5c98 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -16,7 +16,6 @@
package com.android.server.appsearch;
import static android.app.appsearch.AppSearchResult.throwableToFailedResult;
-import static android.os.Process.INVALID_UID;
import static android.os.UserHandle.USER_NULL;
import android.annotation.ElapsedRealtimeLong;
@@ -37,7 +36,6 @@
import android.app.appsearch.SearchSpec;
import android.app.appsearch.SetSchemaResponse;
import android.app.appsearch.StorageInfo;
-import android.app.appsearch.exceptions.AppSearchException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -46,7 +44,6 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
-import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -54,9 +51,9 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
@@ -64,6 +61,8 @@
import com.android.server.appsearch.stats.LoggerInstanceManager;
import com.android.server.appsearch.stats.PlatformLogger;
+import com.google.android.icing.proto.PersistType;
+
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
@@ -75,7 +74,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
-import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -94,7 +93,7 @@
// mutate requests will need to gain write lock and query requests need to gain read lock.
private static final Executor EXECUTOR = new ThreadPoolExecutor(/*corePoolSize=*/1,
Runtime.getRuntime().availableProcessors(), /*keepAliveTime*/ 60L, TimeUnit.SECONDS,
- new SynchronousQueue<Runnable>());
+ new LinkedBlockingQueue<>());
// Cache of unlocked user ids so we don't have to query UserManager service each time. The
// "locked" suffix refers to the fact that access to the field should be locked; unrelated to
@@ -121,14 +120,6 @@
mContext.registerReceiverAsUser(new UserActionReceiver(), UserHandle.ALL,
new IntentFilter(Intent.ACTION_USER_REMOVED), /*broadcastPermission=*/ null,
/*scheduler=*/ null);
-
- IntentFilter packageChangedFilter = new IntentFilter();
- packageChangedFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
- packageChangedFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
- packageChangedFilter.addDataScheme("package");
- mContext.registerReceiverAsUser(new PackageChangedReceiver(), UserHandle.ALL,
- packageChangedFilter, /*broadcastPermission=*/ null,
- /*scheduler=*/ null);
}
private class UserActionReceiver extends BroadcastReceiver {
@@ -136,15 +127,15 @@
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
switch (intent.getAction()) {
case Intent.ACTION_USER_REMOVED:
- int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
if (userId == USER_NULL) {
- Log.e(TAG, "userId is missing in the intent: " + intent);
+ Slog.e(TAG, "userId is missing in the intent: " + intent);
return;
}
handleUserRemoved(userId);
break;
default:
- Log.e(TAG, "Received unknown intent: " + intent);
+ Slog.e(TAG, "Received unknown intent: " + intent);
}
}
}
@@ -164,44 +155,9 @@
try {
mImplInstanceManager.removeAppSearchImplForUser(userId);
mLoggerInstanceManager.removePlatformLoggerForUser(userId);
- Log.i(TAG, "Removed AppSearchImpl instance for user: " + userId);
+ Slog.i(TAG, "Removed AppSearchImpl instance for user: " + userId);
} catch (Throwable t) {
- Log.e(TAG, "Unable to remove data for user: " + userId, t);
- }
- }
-
- private class PackageChangedReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(@NonNull Context context, @NonNull Intent intent) {
- switch (intent.getAction()) {
- case Intent.ACTION_PACKAGE_FULLY_REMOVED:
- case Intent.ACTION_PACKAGE_DATA_CLEARED:
- String packageName = intent.getData().getSchemeSpecificPart();
- if (packageName == null) {
- Log.e(TAG, "Package name is missing in the intent: " + intent);
- return;
- }
- int uid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
- if (uid == INVALID_UID) {
- Log.e(TAG, "uid is missing in the intent: " + intent);
- return;
- }
- handlePackageRemoved(packageName, uid);
- break;
- default:
- Log.e(TAG, "Received unknown intent: " + intent);
- }
- }
- }
-
- private void handlePackageRemoved(String packageName, int uid) {
- int userId = UserHandle.getUserId(uid);
- try {
- AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext, userId);
- //TODO(b/145759910) clear visibility setting for package.
- impl.clearPackageData(packageName);
- } catch (AppSearchException e) {
- Log.e(TAG, "Unable to remove data for package: " + packageName, e);
+ Slog.e(TAG, "Unable to remove data for user: " + userId, t);
}
}
@@ -224,10 +180,10 @@
int schemaVersion,
@UserIdInt int userId,
@NonNull IAppSearchResultCallback callback) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkNotNull(databaseName);
- Preconditions.checkNotNull(schemaBundles);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(databaseName);
+ Objects.requireNonNull(schemaBundles);
+ Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
@@ -273,9 +229,9 @@
@NonNull String databaseName,
@UserIdInt int userId,
@NonNull IAppSearchResultCallback callback) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkNotNull(databaseName);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(databaseName);
+ Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
@@ -300,9 +256,9 @@
@NonNull String databaseName,
@UserIdInt int userId,
@NonNull IAppSearchResultCallback callback) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkNotNull(databaseName);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(databaseName);
+ Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
@@ -328,10 +284,10 @@
@UserIdInt int userId,
@ElapsedRealtimeLong long binderCallStartTimeMillis,
@NonNull IAppSearchBatchResultCallback callback) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkNotNull(databaseName);
- Preconditions.checkNotNull(documentBundles);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(databaseName);
+ Objects.requireNonNull(documentBundles);
+ Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
@@ -364,6 +320,8 @@
++operationFailureCount;
}
}
+ // Now that the batch has been written. Persist the newly written data.
+ impl.persistToDisk(PersistType.Code.LITE);
invokeCallbackOnResult(callback, resultBuilder.build());
} catch (Throwable t) {
invokeCallbackOnError(callback, t);
@@ -400,11 +358,11 @@
@NonNull Map<String, List<String>> typePropertyPaths,
@UserIdInt int userId,
@NonNull IAppSearchBatchResultCallback callback) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkNotNull(databaseName);
- Preconditions.checkNotNull(namespace);
- Preconditions.checkNotNull(uris);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(databaseName);
+ Objects.requireNonNull(namespace);
+ Objects.requireNonNull(uris);
+ Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
@@ -445,11 +403,11 @@
@NonNull Bundle searchSpecBundle,
@UserIdInt int userId,
@NonNull IAppSearchResultCallback callback) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkNotNull(databaseName);
- Preconditions.checkNotNull(queryExpression);
- Preconditions.checkNotNull(searchSpecBundle);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(databaseName);
+ Objects.requireNonNull(queryExpression);
+ Objects.requireNonNull(searchSpecBundle);
+ Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
@@ -480,10 +438,10 @@
@NonNull Bundle searchSpecBundle,
@UserIdInt int userId,
@NonNull IAppSearchResultCallback callback) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkNotNull(queryExpression);
- Preconditions.checkNotNull(searchSpecBundle);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(queryExpression);
+ Objects.requireNonNull(searchSpecBundle);
+ Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
@@ -512,7 +470,7 @@
long nextPageToken,
@UserIdInt int userId,
@NonNull IAppSearchResultCallback callback) {
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
// TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally
@@ -631,7 +589,7 @@
}
}
}
- impl.persistToDisk();
+ impl.persistToDisk(PersistType.Code.FULL);
invokeCallbackOnResult(callback,
AppSearchResult.newSuccessfulResult(migrationFailureBundles));
} catch (Throwable t) {
@@ -685,10 +643,10 @@
@NonNull List<String> uris,
@UserIdInt int userId,
@NonNull IAppSearchBatchResultCallback callback) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkNotNull(databaseName);
- Preconditions.checkNotNull(uris);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(databaseName);
+ Objects.requireNonNull(uris);
+ Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
@@ -708,6 +666,8 @@
resultBuilder.setResult(uri, throwableToFailedResult(t));
}
}
+ // Now that the batch has been written. Persist the newly written data.
+ impl.persistToDisk(PersistType.Code.LITE);
invokeCallbackOnResult(callback, resultBuilder.build());
} catch (Throwable t) {
invokeCallbackOnError(callback, t);
@@ -723,11 +683,11 @@
@NonNull Bundle searchSpecBundle,
@UserIdInt int userId,
@NonNull IAppSearchResultCallback callback) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkNotNull(databaseName);
- Preconditions.checkNotNull(queryExpression);
- Preconditions.checkNotNull(searchSpecBundle);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(databaseName);
+ Objects.requireNonNull(queryExpression);
+ Objects.requireNonNull(searchSpecBundle);
+ Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
@@ -741,6 +701,8 @@
databaseName,
queryExpression,
new SearchSpec(searchSpecBundle));
+ // Now that the batch has been written. Persist the newly written data.
+ impl.persistToDisk(PersistType.Code.LITE);
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
} catch (Throwable t) {
invokeCallbackOnError(callback, t);
@@ -754,9 +716,9 @@
@NonNull String databaseName,
@UserIdInt int userId,
@NonNull IAppSearchResultCallback callback) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkNotNull(databaseName);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(databaseName);
+ Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
@@ -785,7 +747,7 @@
verifyUserUnlocked(callingUserId);
AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(callingUserId);
- impl.persistToDisk();
+ impl.persistToDisk(PersistType.Code.FULL);
} catch (Throwable t) {
Log.e(TAG, "Unable to persist the data to disk", t);
}
@@ -794,7 +756,7 @@
@Override
public void initialize(@UserIdInt int userId, @NonNull IAppSearchResultCallback callback) {
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
EXECUTOR.execute(() -> {
@@ -825,7 +787,7 @@
}
private void verifyCallingPackage(int callingUid, @NonNull String callingPackage) {
- Preconditions.checkNotNull(callingPackage);
+ Objects.requireNonNull(callingPackage);
if (mPackageManagerInternal.getPackageUid(
callingPackage, /*flags=*/ 0, UserHandle.getUserId(callingUid))
!= callingUid) {
@@ -873,13 +835,12 @@
/**
* Invokes the {@link IAppSearchBatchResultCallback} with an unexpected internal throwable.
*
- * <p>The throwable is converted to {@link ParcelableException}.
+ * <p>The throwable is converted to {@link AppSearchResult}.
*/
private void invokeCallbackOnError(
- IAppSearchBatchResultCallback callback, Throwable throwable) {
+ @NonNull IAppSearchBatchResultCallback callback, @NonNull Throwable throwable) {
try {
- //TODO(b/175067650) verify ParcelableException could propagate throwable correctly.
- callback.onSystemError(new ParcelableException(throwable));
+ callback.onSystemError(throwableToFailedResult(throwable));
} catch (RemoteException e) {
Log.e(TAG, "Unable to send error to the callback", e);
}
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 1ed26d6..4de52fb 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java
@@ -35,13 +35,14 @@
import android.util.ArraySet;
import android.util.Log;
-import com.android.internal.util.Preconditions;
+import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -153,7 +154,7 @@
* AppSearchImpl.
*/
static final String VISIBILITY_STORE_PREFIX =
- AppSearchImpl.createPrefix(PACKAGE_NAME, DATABASE_NAME);
+ PrefixUtil.createPrefix(PACKAGE_NAME, DATABASE_NAME);
/** Namespace of documents that contain visibility settings */
private static final String NAMESPACE = "";
@@ -332,9 +333,9 @@
@NonNull Set<String> schemasNotPlatformSurfaceable,
@NonNull Map<String, List<PackageIdentifier>> schemasPackageAccessible)
throws AppSearchException {
- Preconditions.checkNotNull(prefix);
- Preconditions.checkNotNull(schemasNotPlatformSurfaceable);
- Preconditions.checkNotNull(schemasPackageAccessible);
+ Objects.requireNonNull(prefix);
+ Objects.requireNonNull(schemasNotPlatformSurfaceable);
+ Objects.requireNonNull(schemasPackageAccessible);
// Persist the document
GenericDocument.Builder<?> visibilityDocument =
@@ -382,8 +383,8 @@
/** Checks whether {@code prefixedSchema} can be searched over by the {@code callerUid}. */
public boolean isSchemaSearchableByCaller(
@NonNull String prefix, @NonNull String prefixedSchema, int callerUid) {
- Preconditions.checkNotNull(prefix);
- Preconditions.checkNotNull(prefixedSchema);
+ Objects.requireNonNull(prefix);
+ Objects.requireNonNull(prefixedSchema);
// We compare appIds here rather than direct uids because the package's uid may change based
// on the user that's running.
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 5f8cbee..50ac054 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
@@ -16,10 +16,18 @@
package com.android.server.appsearch.external.localstorage;
+import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.addPrefixToDocument;
+import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPackagePrefix;
+import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix;
+import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getDatabaseName;
+import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getPackageName;
+import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getPrefix;
+import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.removePrefix;
+import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.removePrefixesFromDocument;
+
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.GetByUriRequest;
@@ -39,7 +47,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.ResultCodeToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
@@ -66,7 +73,6 @@
import com.google.android.icing.proto.PersistToDiskResultProto;
import com.google.android.icing.proto.PersistType;
import com.google.android.icing.proto.PropertyConfigProto;
-import com.google.android.icing.proto.PropertyProto;
import com.google.android.icing.proto.PutResultProto;
import com.google.android.icing.proto.ReportUsageResultProto;
import com.google.android.icing.proto.ResetResultProto;
@@ -89,6 +95,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -132,10 +139,6 @@
public final class AppSearchImpl implements Closeable {
private static final String TAG = "AppSearchImpl";
- @VisibleForTesting static final char DATABASE_DELIMITER = '/';
-
- @VisibleForTesting static final char PACKAGE_DELIMITER = '$';
-
@VisibleForTesting static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000;
@VisibleForTesting static final int OPTIMIZE_THRESHOLD_BYTES = 1_000_000; // 1MB
@VisibleForTesting static final int CHECK_OPTIMIZE_INTERVAL = 100;
@@ -148,11 +151,12 @@
@GuardedBy("mReadWriteLock")
private final VisibilityStore mVisibilityStoreLocked;
- // This map contains schemaTypes for all package-database prefixes. All values in the map are
- // prefixed with the package-database prefix.
- // TODO(b/172360376): Check if this can be replaced with an ArrayMap
+ // This map contains schema types and SchemaTypeConfigProtos for all package-database
+ // prefixes. It maps each package-database prefix to an inner-map. The inner-map maps each
+ // prefixed schema type to its respective SchemaTypeConfigProto.
@GuardedBy("mReadWriteLock")
- private final Map<String, Set<String>> mSchemaMapLocked = new HashMap<>();
+ private final Map<String, Map<String, SchemaTypeConfigProto>> mSchemaMapLocked =
+ new ArrayMap<>();
// This map contains namespaces for all package-database prefixes. All values in the map are
// prefixed with the package-database prefix.
@@ -182,9 +186,9 @@
int userId,
@NonNull String globalQuerierPackage)
throws AppSearchException {
- Preconditions.checkNotNull(icingDir);
- Preconditions.checkNotNull(context);
- Preconditions.checkNotNull(globalQuerierPackage);
+ Objects.requireNonNull(icingDir);
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(globalQuerierPackage);
AppSearchImpl appSearchImpl =
new AppSearchImpl(icingDir, context, userId, globalQuerierPackage);
appSearchImpl.initializeVisibilityStore();
@@ -229,7 +233,7 @@
// Populate schema map
for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) {
String prefixedSchemaType = schema.getSchemaType();
- addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType), prefixedSchemaType);
+ addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType), schema);
}
// Populate namespace map
@@ -278,7 +282,7 @@
return;
}
- persistToDisk();
+ persistToDisk(PersistType.Code.FULL);
mIcingSearchEngineLocked.close();
mClosedLocked = true;
} catch (AppSearchException e) {
@@ -364,7 +368,14 @@
}
// Update derived data structures.
- mSchemaMapLocked.put(prefix, rewrittenSchemaResults.mRewrittenPrefixedTypes);
+ for (SchemaTypeConfigProto schemaTypeConfigProto :
+ rewrittenSchemaResults.mRewrittenPrefixedTypes.values()) {
+ addToMap(mSchemaMapLocked, prefix, schemaTypeConfigProto);
+ }
+
+ for (String schemaType : rewrittenSchemaResults.mDeletedPrefixedTypes) {
+ removeFromMap(mSchemaMapLocked, prefix, schemaType);
+ }
Set<String> prefixedSchemasNotPlatformSurfaceable =
new ArraySet<>(schemasNotPlatformSurfaceable.size());
@@ -586,7 +597,7 @@
mReadWriteLock.readLock().lock();
try {
throwIfClosedLocked();
-
+ String prefix = createPrefix(packageName, databaseName);
List<TypePropertyMask> nonPrefixedPropertyMasks =
TypePropertyPathToProtoConverter.toTypePropertyMaskList(typePropertyPaths);
List<TypePropertyMask> prefixedPropertyMasks =
@@ -597,7 +608,7 @@
String prefixedType =
nonPrefixedType.equals(GetByUriRequest.PROJECTION_SCHEMA_TYPE_WILDCARD)
? nonPrefixedType
- : createPrefix(packageName, databaseName) + nonPrefixedType;
+ : prefix + nonPrefixedType;
prefixedPropertyMasks.add(
typePropertyMask.toBuilder().setSchemaType(prefixedType).build());
}
@@ -607,15 +618,17 @@
.build();
GetResultProto getResultProto =
- mIcingSearchEngineLocked.get(
- createPrefix(packageName, databaseName) + namespace,
- uri,
- getResultSpec);
+ mIcingSearchEngineLocked.get(prefix + namespace, uri, getResultSpec);
checkSuccess(getResultProto.getStatus());
+ // The schema type map cannot be null at this point. It could only be null if no
+ // schema had ever been set for that prefix. Given we have retrieved a document from
+ // the index, we know a schema had to have been set.
+ Map<String, SchemaTypeConfigProto> schemaTypeMap = mSchemaMapLocked.get(prefix);
DocumentProto.Builder documentBuilder = getResultProto.getDocument().toBuilder();
removePrefixesFromDocument(documentBuilder);
- return GenericDocumentToProtoConverter.toGenericDocument(documentBuilder.build());
+ return GenericDocumentToProtoConverter.toGenericDocument(
+ documentBuilder.build(), prefix, schemaTypeMap);
} finally {
mReadWriteLock.readLock().unlock();
}
@@ -726,7 +739,7 @@
}
} else {
// Client didn't specify certain schemas to search over, check all schemas
- Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix);
+ Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix).keySet();
if (prefixedSchemas != null) {
for (String prefixedSchema : prefixedSchemas) {
if (packageName.equals(callerPackageName)
@@ -816,7 +829,7 @@
searchSpecBuilder.build(), scoringSpec, resultSpecBuilder.build());
checkSuccess(searchResultProto.getStatus());
- return rewriteSearchResultProto(searchResultProto);
+ return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
}
/**
@@ -838,7 +851,7 @@
SearchResultProto searchResultProto =
mIcingSearchEngineLocked.getNextPage(nextPageToken);
checkSuccess(searchResultProto.getStatus());
- return rewriteSearchResultProto(searchResultProto);
+ return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
} finally {
mReadWriteLock.readLock().unlock();
}
@@ -1104,22 +1117,31 @@
/**
* Persists all update/delete requests to the disk.
*
- * <p>If the app crashes after a call to PersistToDisk(), Icing would be able to fully recover
- * all data written up to this point without a costly recovery process.
+ * <p>If the app crashes after a call to PersistToDisk with {@link PersistType.Code#FULL}, Icing
+ * would be able to fully recover all data written up to this point without a costly recovery
+ * process.
*
- * <p>If the app crashes before a call to PersistToDisk(), Icing would trigger a costly recovery
- * process in next initialization. After that, Icing would still be able to recover all written
- * data.
+ * <p>If the app crashes after a call to PersistToDisk with {@link PersistType.Code#LITE}, Icing
+ * would trigger a costly recovery process in next initialization. After that, Icing would still
+ * be able to recover all written data - excepting Usage data. Usage data is only guaranteed to
+ * be safe after a call to PersistToDisk with {@link PersistType.Code#FULL}
*
+ * <p>If the app crashes after an update/delete request has been made, but before any call to
+ * PersistToDisk, then all data in Icing will be lost.
+ *
+ * @param persistType the amount of data to persist. {@link PersistType.Code#LITE} will only
+ * persist the minimal amount of data to ensure all data can be recovered. {@link
+ * PersistType.Code#FULL} will persist all data necessary to prevent data loss without
+ * needing data recovery.
* @throws AppSearchException on any error that AppSearch persist data to disk.
*/
- public void persistToDisk() throws AppSearchException {
+ public void persistToDisk(@NonNull PersistType.Code persistType) throws AppSearchException {
mReadWriteLock.writeLock().lock();
try {
throwIfClosedLocked();
PersistToDiskResultProto persistToDiskResultProto =
- mIcingSearchEngineLocked.persistToDisk(PersistType.Code.FULL);
+ mIcingSearchEngineLocked.persistToDisk(persistType);
checkSuccess(persistToDiskResultProto.getStatus());
} finally {
mReadWriteLock.writeLock().unlock();
@@ -1189,8 +1211,8 @@
// Any prefixed types that used to exist in the schema, but are deleted in the new one.
final Set<String> mDeletedPrefixedTypes = new ArraySet<>();
- // Prefixed types that were part of the new schema.
- final Set<String> mRewrittenPrefixedTypes = new ArraySet<>();
+ // Map of prefixed schema types to SchemaTypeConfigProtos that were part of the new schema.
+ final Map<String, SchemaTypeConfigProto> mRewrittenPrefixedTypes = new ArrayMap<>();
}
/**
@@ -1238,7 +1260,7 @@
// newTypesToProto is modified below, so we need a copy first
RewrittenSchemaResults rewrittenSchemaResults = new RewrittenSchemaResults();
- rewrittenSchemaResults.mRewrittenPrefixedTypes.addAll(newTypesToProto.keySet());
+ rewrittenSchemaResults.mRewrittenPrefixedTypes.putAll(newTypesToProto);
// Combine the existing schema (which may have types from other prefixes) with this
// prefix's new schema. Modifies the existingSchemaBuilder.
@@ -1264,99 +1286,6 @@
}
/**
- * Prepends {@code prefix} to all types and namespaces mentioned anywhere in {@code
- * documentBuilder}.
- *
- * @param documentBuilder The document to mutate
- * @param prefix The prefix to add
- */
- @VisibleForTesting
- static void addPrefixToDocument(
- @NonNull DocumentProto.Builder documentBuilder, @NonNull String prefix) {
- // Rewrite the type name to include/remove the prefix.
- String newSchema = prefix + documentBuilder.getSchema();
- documentBuilder.setSchema(newSchema);
-
- // Rewrite the namespace to include/remove the prefix.
- documentBuilder.setNamespace(prefix + documentBuilder.getNamespace());
-
- // Recurse into derived documents
- for (int propertyIdx = 0;
- propertyIdx < documentBuilder.getPropertiesCount();
- propertyIdx++) {
- int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount();
- if (documentCount > 0) {
- PropertyProto.Builder propertyBuilder =
- documentBuilder.getProperties(propertyIdx).toBuilder();
- for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) {
- DocumentProto.Builder derivedDocumentBuilder =
- propertyBuilder.getDocumentValues(documentIdx).toBuilder();
- addPrefixToDocument(derivedDocumentBuilder, prefix);
- propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder);
- }
- documentBuilder.setProperties(propertyIdx, propertyBuilder);
- }
- }
- }
-
- /**
- * Removes any prefixes from types and namespaces mentioned anywhere in {@code documentBuilder}.
- *
- * @param documentBuilder The document to mutate
- * @return Prefix name that was removed from the document.
- * @throws AppSearchException if there are unexpected database prefixing errors.
- */
- @NonNull
- @VisibleForTesting
- static String removePrefixesFromDocument(@NonNull DocumentProto.Builder documentBuilder)
- throws AppSearchException {
- // Rewrite the type name and namespace to remove the prefix.
- String schemaPrefix = getPrefix(documentBuilder.getSchema());
- String namespacePrefix = getPrefix(documentBuilder.getNamespace());
-
- if (!schemaPrefix.equals(namespacePrefix)) {
- throw new AppSearchException(
- AppSearchResult.RESULT_INTERNAL_ERROR,
- "Found unexpected"
- + " multiple prefix names in document: "
- + schemaPrefix
- + ", "
- + namespacePrefix);
- }
-
- documentBuilder.setSchema(removePrefix(documentBuilder.getSchema()));
- documentBuilder.setNamespace(removePrefix(documentBuilder.getNamespace()));
-
- // Recurse into derived documents
- for (int propertyIdx = 0;
- propertyIdx < documentBuilder.getPropertiesCount();
- propertyIdx++) {
- int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount();
- if (documentCount > 0) {
- PropertyProto.Builder propertyBuilder =
- documentBuilder.getProperties(propertyIdx).toBuilder();
- for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) {
- DocumentProto.Builder derivedDocumentBuilder =
- propertyBuilder.getDocumentValues(documentIdx).toBuilder();
- String nestedPrefix = removePrefixesFromDocument(derivedDocumentBuilder);
- if (!nestedPrefix.equals(schemaPrefix)) {
- throw new AppSearchException(
- AppSearchResult.RESULT_INTERNAL_ERROR,
- "Found unexpected multiple prefix names in document: "
- + schemaPrefix
- + ", "
- + nestedPrefix);
- }
- propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder);
- }
- documentBuilder.setProperties(propertyIdx, propertyBuilder);
- }
- }
-
- return schemaPrefix;
- }
-
- /**
* Rewrites the search spec filters with {@code prefixes}.
*
* <p>This method should be only called in query methods and get the READ lock to keep thread
@@ -1443,9 +1372,9 @@
if (allowedPrefixedSchemas.isEmpty()) {
// If the client didn't specify any schema filters, search over all of their schemas
- Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix);
- if (prefixedSchemas != null) {
- allowedPrefixedSchemas.addAll(prefixedSchemas);
+ Map<String, SchemaTypeConfigProto> prefixedSchemaMap = mSchemaMapLocked.get(prefix);
+ if (prefixedSchemaMap != null) {
+ allowedPrefixedSchemas.addAll(prefixedSchemaMap.keySet());
}
}
return allowedPrefixedSchemas;
@@ -1656,86 +1585,6 @@
return mSchemaMapLocked.keySet();
}
- @NonNull
- static String createPrefix(@NonNull String packageName, @NonNull String databaseName) {
- return createPackagePrefix(packageName) + databaseName + DATABASE_DELIMITER;
- }
-
- @NonNull
- private static String createPackagePrefix(@NonNull String packageName) {
- return packageName + PACKAGE_DELIMITER;
- }
-
- /**
- * Returns the package name that's contained within the {@code prefix}.
- *
- * @param prefix Prefix string that contains the package name inside of it. The package name
- * must be in the front of the string, and separated from the rest of the string by the
- * {@link #PACKAGE_DELIMITER}.
- * @return Valid package name.
- */
- @NonNull
- private static String getPackageName(@NonNull String prefix) {
- int delimiterIndex = prefix.indexOf(PACKAGE_DELIMITER);
- if (delimiterIndex == -1) {
- // This should never happen if we construct our prefixes properly
- Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix);
- return "";
- }
- return prefix.substring(0, delimiterIndex);
- }
-
- /**
- * Returns the database name that's contained within the {@code prefix}.
- *
- * @param prefix Prefix string that contains the database name inside of it. The database name
- * must be between the {@link #PACKAGE_DELIMITER} and {@link #DATABASE_DELIMITER}
- * @return Valid database name.
- */
- @NonNull
- private static String getDatabaseName(@NonNull String prefix) {
- int packageDelimiterIndex = prefix.indexOf(PACKAGE_DELIMITER);
- int databaseDelimiterIndex = prefix.indexOf(DATABASE_DELIMITER);
- if (packageDelimiterIndex == -1) {
- // This should never happen if we construct our prefixes properly
- Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix);
- return "";
- }
- if (databaseDelimiterIndex == -1) {
- // This should never happen if we construct our prefixes properly
- Log.wtf(TAG, "Malformed prefix doesn't contain database delimiter: " + prefix);
- return "";
- }
- return prefix.substring(packageDelimiterIndex + 1, databaseDelimiterIndex);
- }
-
- @NonNull
- private static String removePrefix(@NonNull String prefixedString) throws AppSearchException {
- // The prefix is made up of the package, then the database. So we only need to find the
- // database cutoff.
- int delimiterIndex;
- if ((delimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER)) != -1) {
- // Add 1 to include the char size of the DATABASE_DELIMITER
- return prefixedString.substring(delimiterIndex + 1);
- }
- throw new AppSearchException(
- AppSearchResult.RESULT_UNKNOWN_ERROR,
- "The prefixed value doesn't contains a valid database name.");
- }
-
- @NonNull
- private static String getPrefix(@NonNull String prefixedString) throws AppSearchException {
- 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.");
- }
-
- // Add 1 to include the char size of the DATABASE_DELIMITER
- return prefixedString.substring(0, databaseDelimiterIndex + 1);
- }
-
private static void addToMap(
Map<String, Set<String>> map, String prefix, String prefixedValue) {
Set<String> values = map.get(prefix);
@@ -1746,6 +1595,26 @@
values.add(prefixedValue);
}
+ private static void addToMap(
+ Map<String, Map<String, SchemaTypeConfigProto>> map,
+ String prefix,
+ SchemaTypeConfigProto schemaTypeConfigProto) {
+ Map<String, SchemaTypeConfigProto> schemaTypeMap = map.get(prefix);
+ if (schemaTypeMap == null) {
+ schemaTypeMap = new ArrayMap<>();
+ map.put(prefix, schemaTypeMap);
+ }
+ schemaTypeMap.put(schemaTypeConfigProto.getSchemaType(), schemaTypeConfigProto);
+ }
+
+ private static void removeFromMap(
+ Map<String, Map<String, SchemaTypeConfigProto>> map, String prefix, String schemaType) {
+ Map<String, SchemaTypeConfigProto> schemaTypeMap = map.get(prefix);
+ if (schemaTypeMap != null) {
+ schemaTypeMap.remove(schemaType);
+ }
+ }
+
/**
* Checks the given status code and throws an {@link AppSearchException} if code is an error.
*
@@ -1853,7 +1722,9 @@
/** Remove the rewritten schema types from any result documents. */
@NonNull
@VisibleForTesting
- static SearchResultPage rewriteSearchResultProto(@NonNull SearchResultProto searchResultProto)
+ static SearchResultPage rewriteSearchResultProto(
+ @NonNull SearchResultProto searchResultProto,
+ @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap)
throws AppSearchException {
// Parallel array of package names for each document search result.
List<String> packageNames = new ArrayList<>(searchResultProto.getResultsCount());
@@ -1873,7 +1744,7 @@
resultsBuilder.setResults(i, resultBuilder);
}
return SearchResultToProtoConverter.toSearchResultPage(
- resultsBuilder, packageNames, databaseNames);
+ resultsBuilder, packageNames, databaseNames, schemaMap);
}
@GuardedBy("mReadWriteLock")
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
index 5680670..cdd7952 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
@@ -18,11 +18,12 @@
import android.annotation.NonNull;
-import com.android.internal.util.Preconditions;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.google.android.icing.proto.PutDocumentStatsProto;
+import java.util.Objects;
+
/**
* Class contains helper functions for logging.
*
@@ -42,8 +43,8 @@
static void copyNativeStats(
@NonNull PutDocumentStatsProto fromNativeStats,
@NonNull PutDocumentStats.Builder toStatsBuilder) {
- Preconditions.checkNotNull(fromNativeStats);
- Preconditions.checkNotNull(toStatsBuilder);
+ Objects.requireNonNull(fromNativeStats);
+ Objects.requireNonNull(toStatsBuilder);
toStatsBuilder
.setNativeLatencyMillis(fromNativeStats.getLatencyMs())
.setNativeDocumentStoreLatencyMillis(fromNativeStats.getDocumentStoreLatencyMs())
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
index d6b9da8..5ff56ab 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java
@@ -17,16 +17,18 @@
package com.android.server.appsearch.external.localstorage.converter;
import android.annotation.NonNull;
+import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument;
-import com.android.internal.util.Preconditions;
-
import com.google.android.icing.proto.DocumentProto;
import com.google.android.icing.proto.PropertyProto;
+import com.google.android.icing.proto.SchemaTypeConfigProto;
import com.google.protobuf.ByteString;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
/**
* Translates a {@link GenericDocument} into a {@link DocumentProto}.
@@ -34,13 +36,20 @@
* @hide
*/
public final class GenericDocumentToProtoConverter {
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+ private static final long[] EMPTY_LONG_ARRAY = new long[0];
+ private static final double[] EMPTY_DOUBLE_ARRAY = new double[0];
+ private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0];
+ private static final byte[][] EMPTY_BYTES_ARRAY = new byte[0][0];
+ private static final GenericDocument[] EMPTY_DOCUMENT_ARRAY = new GenericDocument[0];
+
private GenericDocumentToProtoConverter() {}
/** Converts a {@link GenericDocument} into a {@link DocumentProto}. */
@NonNull
@SuppressWarnings("unchecked")
public static DocumentProto toDocumentProto(@NonNull GenericDocument document) {
- Preconditions.checkNotNull(document);
+ Objects.requireNonNull(document);
DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder();
mProtoBuilder
.setUri(document.getUri())
@@ -97,16 +106,34 @@
return mProtoBuilder.build();
}
- /** Converts a {@link DocumentProto} into a {@link GenericDocument}. */
+ /**
+ * Converts a {@link DocumentProto} into a {@link GenericDocument}.
+ *
+ * <p>In the case that the {@link DocumentProto} object proto has no values set, the converter
+ * searches for the matching property name in the {@link SchemaTypeConfigProto} object for the
+ * document, and infers the correct default value to set for the empty property based on the
+ * data type of the property defined by the schema type.
+ *
+ * @param proto the document to convert to a {@link GenericDocument} instance. The document
+ * proto should have its package + database prefix stripped from its fields.
+ * @param prefix the package + database prefix used searching the {@code schemaTypeMap}.
+ * @param schemaTypeMap map of prefixed schema type to {@link SchemaTypeConfigProto}, used for
+ * looking up the default empty value to set for a document property that has all empty
+ * values.
+ */
@NonNull
- public static GenericDocument toGenericDocument(@NonNull DocumentProto proto) {
- Preconditions.checkNotNull(proto);
+ public static GenericDocument toGenericDocument(
+ @NonNull DocumentProto proto,
+ @NonNull String prefix,
+ @NonNull Map<String, SchemaTypeConfigProto> schemaTypeMap) {
+ Objects.requireNonNull(proto);
GenericDocument.Builder<?> documentBuilder =
new GenericDocument.Builder<>(
proto.getNamespace(), proto.getUri(), proto.getSchema())
.setScore(proto.getScore())
.setTtlMillis(proto.getTtlMs())
.setCreationTimestampMillis(proto.getCreationTimestampMs());
+ String prefixedSchemaType = prefix + proto.getSchema();
for (int i = 0; i < proto.getPropertiesCount(); i++) {
PropertyProto property = proto.getProperties(i);
@@ -144,13 +171,51 @@
} else if (property.getDocumentValuesCount() > 0) {
GenericDocument[] values = new GenericDocument[property.getDocumentValuesCount()];
for (int j = 0; j < values.length; j++) {
- values[j] = toGenericDocument(property.getDocumentValues(j));
+ values[j] =
+ toGenericDocument(property.getDocumentValues(j), prefix, schemaTypeMap);
}
documentBuilder.setPropertyDocument(name, values);
} else {
- throw new IllegalStateException("Unknown type of value: " + name);
+ // TODO(b/184966497): Optimize by caching PropertyConfigProto
+ setEmptyProperty(name, documentBuilder, schemaTypeMap.get(prefixedSchemaType));
}
}
return documentBuilder.build();
}
+
+ private static void setEmptyProperty(
+ @NonNull String propertyName,
+ @NonNull GenericDocument.Builder<?> documentBuilder,
+ @NonNull SchemaTypeConfigProto schema) {
+ @AppSearchSchema.PropertyConfig.DataType int dataType = 0;
+ for (int i = 0; i < schema.getPropertiesCount(); ++i) {
+ if (propertyName.equals(schema.getProperties(i).getPropertyName())) {
+ dataType = schema.getProperties(i).getDataType().getNumber();
+ break;
+ }
+ }
+
+ switch (dataType) {
+ case AppSearchSchema.PropertyConfig.DATA_TYPE_STRING:
+ documentBuilder.setPropertyString(propertyName, EMPTY_STRING_ARRAY);
+ break;
+ case AppSearchSchema.PropertyConfig.DATA_TYPE_INT64:
+ documentBuilder.setPropertyLong(propertyName, EMPTY_LONG_ARRAY);
+ break;
+ case AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE:
+ documentBuilder.setPropertyDouble(propertyName, EMPTY_DOUBLE_ARRAY);
+ break;
+ case AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN:
+ documentBuilder.setPropertyBoolean(propertyName, EMPTY_BOOLEAN_ARRAY);
+ break;
+ case AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES:
+ documentBuilder.setPropertyBytes(propertyName, EMPTY_BYTES_ARRAY);
+ break;
+ case AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT:
+ documentBuilder.setPropertyDocument(propertyName, EMPTY_DOCUMENT_ARRAY);
+ break;
+ default:
+ throw new IllegalStateException("Unknown type of value: " + propertyName);
+ }
+ }
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
index 800b073..e3fa7e0 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
@@ -20,8 +20,6 @@
import android.app.appsearch.AppSearchSchema;
import android.util.Log;
-import com.android.internal.util.Preconditions;
-
import com.google.android.icing.proto.DocumentIndexingConfig;
import com.google.android.icing.proto.PropertyConfigProto;
import com.google.android.icing.proto.SchemaTypeConfigProto;
@@ -30,6 +28,7 @@
import com.google.android.icing.proto.TermMatchType;
import java.util.List;
+import java.util.Objects;
/**
* Translates an {@link AppSearchSchema} into a {@link SchemaTypeConfigProto}.
@@ -48,7 +47,7 @@
@NonNull
public static SchemaTypeConfigProto toSchemaTypeConfigProto(
@NonNull AppSearchSchema schema, int version) {
- Preconditions.checkNotNull(schema);
+ Objects.requireNonNull(schema);
SchemaTypeConfigProto.Builder protoBuilder =
SchemaTypeConfigProto.newBuilder()
.setSchemaType(schema.getSchemaType())
@@ -64,7 +63,7 @@
@NonNull
private static PropertyConfigProto toPropertyConfigProto(
@NonNull AppSearchSchema.PropertyConfig property) {
- Preconditions.checkNotNull(property);
+ Objects.requireNonNull(property);
PropertyConfigProto.Builder builder =
PropertyConfigProto.newBuilder().setPropertyName(property.getName());
@@ -116,7 +115,7 @@
*/
@NonNull
public static AppSearchSchema toAppSearchSchema(@NonNull SchemaTypeConfigProtoOrBuilder proto) {
- Preconditions.checkNotNull(proto);
+ Objects.requireNonNull(proto);
AppSearchSchema.Builder builder = new AppSearchSchema.Builder(proto.getSchemaType());
List<PropertyConfigProto> properties = proto.getPropertiesList();
for (int i = 0; i < properties.size(); i++) {
@@ -129,7 +128,7 @@
@NonNull
private static AppSearchSchema.PropertyConfig toPropertyConfig(
@NonNull PropertyConfigProto proto) {
- Preconditions.checkNotNull(proto);
+ Objects.requireNonNull(proto);
switch (proto.getDataType()) {
case STRING:
return toStringPropertyConfig(proto);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
index bf7e533..57c1590 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
@@ -16,6 +16,8 @@
package com.android.server.appsearch.external.localstorage.converter;
+import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix;
+
import android.annotation.NonNull;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.SearchResult;
@@ -24,6 +26,7 @@
import com.android.internal.util.Preconditions;
+import com.google.android.icing.proto.SchemaTypeConfigProto;
import com.google.android.icing.proto.SearchResultProto;
import com.google.android.icing.proto.SearchResultProtoOrBuilder;
import com.google.android.icing.proto.SnippetMatchProto;
@@ -31,6 +34,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Translates a {@link SearchResultProto} into {@link SearchResult}s.
@@ -49,13 +53,17 @@
* @param databaseNames A parallel array of database names. The database name at index 'i' of
* this list shold be the database that indexed the document at index 'i' of
* proto.getResults(i).
+ * @param schemaMap A map of prefixes to an inner-map of prefixed schema type to
+ * SchemaTypeConfigProtos, used for setting a default value for results with DocumentProtos
+ * that have empty values.
* @return {@link SearchResultPage} of results.
*/
@NonNull
public static SearchResultPage toSearchResultPage(
@NonNull SearchResultProtoOrBuilder proto,
@NonNull List<String> packageNames,
- @NonNull List<String> databaseNames) {
+ @NonNull List<String> databaseNames,
+ @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) {
Preconditions.checkArgument(
proto.getResultsCount() == packageNames.size(),
"Size of results does not match the number of package names.");
@@ -63,8 +71,14 @@
bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken());
ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount());
for (int i = 0; i < proto.getResultsCount(); i++) {
+ String prefix = createPrefix(packageNames.get(i), databaseNames.get(i));
+ Map<String, SchemaTypeConfigProto> schemaTypeMap = schemaMap.get(prefix);
SearchResult result =
- toSearchResult(proto.getResults(i), packageNames.get(i), databaseNames.get(i));
+ toSearchResult(
+ proto.getResults(i),
+ packageNames.get(i),
+ databaseNames.get(i),
+ schemaTypeMap);
resultBundles.add(result.getBundle());
}
bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles);
@@ -77,15 +91,21 @@
* @param proto The proto to be converted.
* @param packageName The package name associated with the document in {@code proto}.
* @param databaseName The database name associated with the document in {@code proto}.
+ * @param schemaTypeToProtoMap A map of prefixed schema types to their corresponding
+ * SchemaTypeConfigProto, used for setting a default value for results with DocumentProtos
+ * that have empty values.
* @return A {@link SearchResult} bundle.
*/
@NonNull
private static SearchResult toSearchResult(
@NonNull SearchResultProto.ResultProtoOrBuilder proto,
@NonNull String packageName,
- @NonNull String databaseName) {
+ @NonNull String databaseName,
+ @NonNull Map<String, SchemaTypeConfigProto> schemaTypeToProtoMap) {
+ String prefix = createPrefix(packageName, databaseName);
GenericDocument document =
- GenericDocumentToProtoConverter.toGenericDocument(proto.getDocument());
+ GenericDocumentToProtoConverter.toGenericDocument(
+ proto.getDocument(), prefix, schemaTypeToProtoMap);
SearchResult.Builder builder =
new SearchResult.Builder(packageName, databaseName)
.setGenericDocument(document)
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
index d9e8adb..8f9e9bd 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
@@ -19,13 +19,13 @@
import android.annotation.NonNull;
import android.app.appsearch.SearchSpec;
-import com.android.internal.util.Preconditions;
-
import com.google.android.icing.proto.ResultSpecProto;
import com.google.android.icing.proto.ScoringSpecProto;
import com.google.android.icing.proto.SearchSpecProto;
import com.google.android.icing.proto.TermMatchType;
+import java.util.Objects;
+
/**
* Translates a {@link SearchSpec} into icing search protos.
*
@@ -37,7 +37,7 @@
/** Extracts {@link SearchSpecProto} information from a {@link SearchSpec}. */
@NonNull
public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) {
- Preconditions.checkNotNull(spec);
+ Objects.requireNonNull(spec);
SearchSpecProto.Builder protoBuilder =
SearchSpecProto.newBuilder()
.addAllSchemaTypeFilters(spec.getFilterSchemas())
@@ -56,7 +56,7 @@
/** Extracts {@link ResultSpecProto} information from a {@link SearchSpec}. */
@NonNull
public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) {
- Preconditions.checkNotNull(spec);
+ Objects.requireNonNull(spec);
return ResultSpecProto.newBuilder()
.setNumPerPage(spec.getResultCountPerPage())
.setSnippetSpec(
@@ -73,7 +73,7 @@
/** Extracts {@link ScoringSpecProto} information from a {@link SearchSpec}. */
@NonNull
public static ScoringSpecProto toScoringSpecProto(@NonNull SearchSpec spec) {
- Preconditions.checkNotNull(spec);
+ Objects.requireNonNull(spec);
ScoringSpecProto.Builder protoBuilder = ScoringSpecProto.newBuilder();
@SearchSpec.Order int orderCode = spec.getOrder();
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java
index a0f39ec..ed73593 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java
@@ -19,10 +19,10 @@
import android.annotation.NonNull;
import android.app.appsearch.SetSchemaResponse;
-import com.android.internal.util.Preconditions;
-
import com.google.android.icing.proto.SetSchemaResultProto;
+import java.util.Objects;
+
/**
* Translates a {@link SetSchemaResultProto} into {@link SetSchemaResponse}.
*
@@ -42,8 +42,8 @@
@NonNull
public static SetSchemaResponse toSetSchemaResponse(
@NonNull SetSchemaResultProto proto, @NonNull String prefix) {
- Preconditions.checkNotNull(proto);
- Preconditions.checkNotNull(prefix);
+ Objects.requireNonNull(proto);
+ Objects.requireNonNull(prefix);
SetSchemaResponse.Builder builder = new SetSchemaResponse.Builder();
for (int i = 0; i < proto.getDeletedSchemaTypesCount(); i++) {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java
index 6f6dad2..acf04ef 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java
@@ -18,13 +18,12 @@
import android.annotation.NonNull;
-import com.android.internal.util.Preconditions;
-
import com.google.android.icing.proto.TypePropertyMask;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* Translates a <code>Map<String, List<String>></code> into <code>List<TypePropertyMask></code>.
@@ -38,7 +37,7 @@
@NonNull
public static List<TypePropertyMask> toTypePropertyMaskList(
@NonNull Map<String, List<String>> typePropertyPaths) {
- Preconditions.checkNotNull(typePropertyPaths);
+ Objects.requireNonNull(typePropertyPaths);
List<TypePropertyMask> typePropertyMasks = new ArrayList<>(typePropertyPaths.size());
for (Map.Entry<String, List<String>> e : typePropertyPaths.entrySet()) {
typePropertyMasks.add(
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
index a724f95..cf640c1 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
@@ -19,10 +19,9 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import com.android.internal.util.Preconditions;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* A class for setting basic information to log for all function calls.
@@ -75,8 +74,8 @@
private final int mNumOperationsFailed;
CallStats(@NonNull Builder builder) {
- Preconditions.checkNotNull(builder);
- mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStatsBuilder).build();
+ Objects.requireNonNull(builder);
+ mGeneralStats = Objects.requireNonNull(builder.mGeneralStatsBuilder).build();
mCallType = builder.mCallType;
mEstimatedBinderLatencyMillis = builder.mEstimatedBinderLatencyMillis;
mNumOperationsSucceeded = builder.mNumOperationsSucceeded;
@@ -140,8 +139,8 @@
/** Builder takes {@link GeneralStats.Builder}. */
public Builder(@NonNull String packageName, @NonNull String database) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkNotNull(database);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(database);
mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database);
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java
index 8ce8eda..53c1ee3 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.app.appsearch.AppSearchResult;
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
/**
* A class for holding general logging information.
@@ -48,9 +48,9 @@
private final int mTotalLatencyMillis;
GeneralStats(@NonNull Builder builder) {
- Preconditions.checkNotNull(builder);
- mPackageName = Preconditions.checkNotNull(builder.mPackageName);
- mDatabase = Preconditions.checkNotNull(builder.mDatabase);
+ Objects.requireNonNull(builder);
+ mPackageName = Objects.requireNonNull(builder.mPackageName);
+ mDatabase = Objects.requireNonNull(builder.mDatabase);
mStatusCode = builder.mStatusCode;
mTotalLatencyMillis = builder.mTotalLatencyMillis;
}
@@ -92,8 +92,8 @@
* @param database name of the database logging stats
*/
public Builder(@NonNull String packageName, @NonNull String database) {
- mPackageName = Preconditions.checkNotNull(packageName);
- mDatabase = Preconditions.checkNotNull(database);
+ mPackageName = Objects.requireNonNull(packageName);
+ mDatabase = Objects.requireNonNull(database);
}
/** Sets status code returned from {@link AppSearchResult#getResultCode()} */
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java
index c1f6fb1..d031172 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
/**
* A class for holding detailed stats to log for each individual document put by a {@link
@@ -60,8 +60,8 @@
private final boolean mNativeExceededMaxNumTokens;
PutDocumentStats(@NonNull Builder builder) {
- Preconditions.checkNotNull(builder);
- mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStatsBuilder).build();
+ Objects.requireNonNull(builder);
+ mGeneralStats = Objects.requireNonNull(builder.mGeneralStatsBuilder).build();
mGenerateDocumentProtoLatencyMillis = builder.mGenerateDocumentProtoLatencyMillis;
mRewriteDocumentTypesLatencyMillis = builder.mRewriteDocumentTypesLatencyMillis;
mNativeLatencyMillis = builder.mNativeLatencyMillis;
@@ -142,8 +142,8 @@
/** Builder takes {@link GeneralStats.Builder}. */
public Builder(@NonNull String packageName, @NonNull String database) {
- Preconditions.checkNotNull(packageName);
- Preconditions.checkNotNull(database);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(database);
mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database);
}
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
new file mode 100644
index 0000000..9ae9f18
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch.external.localstorage.util;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import com.google.android.icing.proto.DocumentProto;
+import com.google.android.icing.proto.PropertyProto;
+
+/**
+ * Provides utility functions for working with package + database prefixes.
+ *
+ * @hide
+ */
+public class PrefixUtil {
+ private static final String TAG = "AppSearchPrefixUtil";
+
+ @VisibleForTesting public static final char DATABASE_DELIMITER = '/';
+
+ @VisibleForTesting public static final char PACKAGE_DELIMITER = '$';
+
+ private PrefixUtil() {}
+
+ /** Creates prefix string for given package name and database name. */
+ @NonNull
+ public static String createPrefix(@NonNull String packageName, @NonNull String databaseName) {
+ return packageName + PACKAGE_DELIMITER + databaseName + DATABASE_DELIMITER;
+ }
+ /** Creates prefix string for given package name. */
+ @NonNull
+ public static String createPackagePrefix(@NonNull String packageName) {
+ return packageName + PACKAGE_DELIMITER;
+ }
+
+ /**
+ * Returns the package name that's contained within the {@code prefix}.
+ *
+ * @param prefix Prefix string that contains the package name inside of it. The package name
+ * must be in the front of the string, and separated from the rest of the string by the
+ * {@link #PACKAGE_DELIMITER}.
+ * @return Valid package name.
+ */
+ @NonNull
+ public static String getPackageName(@NonNull String prefix) {
+ int delimiterIndex = prefix.indexOf(PACKAGE_DELIMITER);
+ if (delimiterIndex == -1) {
+ // This should never happen if we construct our prefixes properly
+ Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix);
+ return "";
+ }
+ return prefix.substring(0, delimiterIndex);
+ }
+
+ /**
+ * Returns the database name that's contained within the {@code prefix}.
+ *
+ * @param prefix Prefix string that contains the database name inside of it. The database name
+ * must be between the {@link #PACKAGE_DELIMITER} and {@link #DATABASE_DELIMITER}
+ * @return Valid database name.
+ */
+ @NonNull
+ public static String getDatabaseName(@NonNull String prefix) {
+ // TODO (b/184050178) Start database delimiter index search from after package delimiter
+ int packageDelimiterIndex = prefix.indexOf(PACKAGE_DELIMITER);
+ int databaseDelimiterIndex = prefix.indexOf(DATABASE_DELIMITER);
+ if (packageDelimiterIndex == -1) {
+ // This should never happen if we construct our prefixes properly
+ Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix);
+ return "";
+ }
+ if (databaseDelimiterIndex == -1) {
+ // This should never happen if we construct our prefixes properly
+ Log.wtf(TAG, "Malformed prefix doesn't contain database delimiter: " + prefix);
+ return "";
+ }
+ return prefix.substring(packageDelimiterIndex + 1, databaseDelimiterIndex);
+ }
+
+ /**
+ * Creates a string with the package and database prefix removed from the input string.
+ *
+ * @param prefixedString a string containing a package and database prefix.
+ * @return a string with the package and database prefix removed.
+ * @throws AppSearchException if the prefixed value does not contain a valid database name.
+ */
+ @NonNull
+ public static String removePrefix(@NonNull String prefixedString) throws AppSearchException {
+ // The prefix is made up of the package, then the database. So we only need to find the
+ // database cutoff.
+ int delimiterIndex;
+ if ((delimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER)) != -1) {
+ // Add 1 to include the char size of the DATABASE_DELIMITER
+ return prefixedString.substring(delimiterIndex + 1);
+ }
+ throw new AppSearchException(
+ AppSearchResult.RESULT_UNKNOWN_ERROR,
+ "The prefixed value doesn't contains a valid database name.");
+ }
+
+ /**
+ * Creates a package and database prefix string from the input string.
+ *
+ * @param prefixedString a string containing a package and database prefix.
+ * @return a string with the package and database prefix
+ * @throws AppSearchException if the prefixed value does not contain a valid database name.
+ */
+ @NonNull
+ public static String getPrefix(@NonNull String prefixedString) throws AppSearchException {
+ 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.");
+ }
+
+ // Add 1 to include the char size of the DATABASE_DELIMITER
+ return prefixedString.substring(0, databaseDelimiterIndex + 1);
+ }
+
+ /**
+ * Prepends {@code prefix} to all types and namespaces mentioned anywhere in {@code
+ * documentBuilder}.
+ *
+ * @param documentBuilder The document to mutate
+ * @param prefix The prefix to add
+ */
+ public static void addPrefixToDocument(
+ @NonNull DocumentProto.Builder documentBuilder, @NonNull String prefix) {
+ // Rewrite the type name to include/remove the prefix.
+ String newSchema = prefix + documentBuilder.getSchema();
+ documentBuilder.setSchema(newSchema);
+
+ // Rewrite the namespace to include/remove the prefix.
+ documentBuilder.setNamespace(prefix + documentBuilder.getNamespace());
+
+ // Recurse into derived documents
+ for (int propertyIdx = 0;
+ propertyIdx < documentBuilder.getPropertiesCount();
+ propertyIdx++) {
+ int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount();
+ if (documentCount > 0) {
+ PropertyProto.Builder propertyBuilder =
+ documentBuilder.getProperties(propertyIdx).toBuilder();
+ for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) {
+ DocumentProto.Builder derivedDocumentBuilder =
+ propertyBuilder.getDocumentValues(documentIdx).toBuilder();
+ addPrefixToDocument(derivedDocumentBuilder, prefix);
+ propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder);
+ }
+ documentBuilder.setProperties(propertyIdx, propertyBuilder);
+ }
+ }
+ }
+
+ /**
+ * Removes any prefixes from types and namespaces mentioned anywhere in {@code documentBuilder}.
+ *
+ * @param documentBuilder The document to mutate
+ * @return Prefix name that was removed from the document.
+ * @throws AppSearchException if there are unexpected database prefixing errors.
+ */
+ @NonNull
+ public static String removePrefixesFromDocument(@NonNull DocumentProto.Builder documentBuilder)
+ throws AppSearchException {
+ // Rewrite the type name and namespace to remove the prefix.
+ String schemaPrefix = getPrefix(documentBuilder.getSchema());
+ String namespacePrefix = getPrefix(documentBuilder.getNamespace());
+
+ if (!schemaPrefix.equals(namespacePrefix)) {
+ throw new AppSearchException(
+ AppSearchResult.RESULT_INTERNAL_ERROR,
+ "Found unexpected"
+ + " multiple prefix names in document: "
+ + schemaPrefix
+ + ", "
+ + namespacePrefix);
+ }
+
+ documentBuilder.setSchema(removePrefix(documentBuilder.getSchema()));
+ documentBuilder.setNamespace(removePrefix(documentBuilder.getNamespace()));
+
+ // Recurse into derived documents
+ for (int propertyIdx = 0;
+ propertyIdx < documentBuilder.getPropertiesCount();
+ propertyIdx++) {
+ int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount();
+ if (documentCount > 0) {
+ PropertyProto.Builder propertyBuilder =
+ documentBuilder.getProperties(propertyIdx).toBuilder();
+ for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) {
+ DocumentProto.Builder derivedDocumentBuilder =
+ propertyBuilder.getDocumentValues(documentIdx).toBuilder();
+ String nestedPrefix = removePrefixesFromDocument(derivedDocumentBuilder);
+ if (!nestedPrefix.equals(schemaPrefix)) {
+ throw new AppSearchException(
+ AppSearchResult.RESULT_INTERNAL_ERROR,
+ "Found unexpected multiple prefix names in document: "
+ + schemaPrefix
+ + ", "
+ + nestedPrefix);
+ }
+ propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder);
+ }
+ documentBuilder.setProperties(propertyIdx, propertyBuilder);
+ }
+ }
+
+ return schemaPrefix;
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
index 1c04d99..731ab35 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
@@ -29,7 +29,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.Preconditions;
import com.android.server.appsearch.external.localstorage.AppSearchLogger;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
@@ -38,6 +37,7 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
+import java.util.Objects;
import java.util.Random;
/**
@@ -162,15 +162,15 @@
* Westworld constructor
*/
public PlatformLogger(@NonNull Context context, int userId, @NonNull Config config) {
- mContext = Preconditions.checkNotNull(context);
- mConfig = Preconditions.checkNotNull(config);
+ mContext = Objects.requireNonNull(context);
+ mConfig = Objects.requireNonNull(config);
mUserId = userId;
}
/** Logs {@link CallStats}. */
@Override
public void logStats(@NonNull CallStats stats) {
- Preconditions.checkNotNull(stats);
+ Objects.requireNonNull(stats);
synchronized (mLock) {
if (shouldLogForTypeLocked(stats.getCallType())) {
logStatsImplLocked(stats);
@@ -181,7 +181,7 @@
/** Logs {@link PutDocumentStats}. */
@Override
public void logStats(@NonNull PutDocumentStats stats) {
- Preconditions.checkNotNull(stats);
+ Objects.requireNonNull(stats);
synchronized (mLock) {
if (shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)) {
logStatsImplLocked(stats);
@@ -197,7 +197,7 @@
*/
public int removeCachedUidForPackage(@NonNull String packageName) {
// TODO(b/173532925) This needs to be called when we get PACKAGE_REMOVED intent
- Preconditions.checkNotNull(packageName);
+ Objects.requireNonNull(packageName);
synchronized (mLock) {
Integer uid = mPackageUidCacheLocked.remove(packageName);
return uid != null ? uid : Process.INVALID_UID;
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index e46c147..f99664b 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I925ec12f4901c7759976c344ba3428210aada8ad
+If9d1d770d2327d7d0db7d82acfc54787b5de64bc
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
index f0de496..6193367 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
@@ -40,11 +40,11 @@
import androidx.test.core.app.ApplicationProvider;
-import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -81,8 +81,8 @@
private AppSearchSessionShimImpl(
@NonNull AppSearchSession session, @NonNull ExecutorService executor) {
- mAppSearchSession = Preconditions.checkNotNull(session);
- mExecutor = Preconditions.checkNotNull(executor);
+ mAppSearchSession = Objects.requireNonNull(session);
+ mExecutor = Objects.requireNonNull(executor);
}
@Override
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
index 5042ce0..c35849d 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
@@ -30,11 +30,11 @@
import androidx.test.core.app.ApplicationProvider;
-import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
+import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -69,8 +69,8 @@
private GlobalSearchSessionShimImpl(
@NonNull GlobalSearchSession session, @NonNull ExecutorService executor) {
- mGlobalSearchSession = Preconditions.checkNotNull(session);
- mExecutor = Preconditions.checkNotNull(executor);
+ mGlobalSearchSession = Objects.requireNonNull(session);
+ mExecutor = Objects.requireNonNull(executor);
}
@NonNull
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java
index 5f26e8c..72078f8 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java
@@ -22,12 +22,12 @@
import android.app.appsearch.SearchResults;
import android.app.appsearch.SearchResultsShim;
-import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -40,8 +40,8 @@
private final SearchResults mSearchResults;
SearchResultsShimImpl(@NonNull SearchResults searchResults, @NonNull Executor executor) {
- mExecutor = Preconditions.checkNotNull(executor);
- mSearchResults = Preconditions.checkNotNull(searchResults);
+ mExecutor = Objects.requireNonNull(executor);
+ mSearchResults = Objects.requireNonNull(searchResults);
}
@NonNull
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 2069043..494945d 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
@@ -187,9 +187,8 @@
* <p>Removed documents will no longer be surfaced by {@link #search} or {@link #getByUri}
* calls.
*
- * <p><b>NOTE:</b>By default, documents are removed via a soft delete operation. Once the
- * document crosses the count threshold or byte usage threshold, the documents will be removed
- * from disk.
+ * <p>Once the database crosses the document count or byte usage threshold, removed documents
+ * will be deleted from disk.
*
* @param request {@link RemoveByUriRequest} with URIs and namespace to remove from the index.
* @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
index 113f8fe..6dbbcb5 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
@@ -26,8 +26,8 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Base64;
+import android.util.IndentingPrintWriter;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
index ca588c5..09260b7 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java
@@ -37,9 +37,9 @@
import android.util.ArraySet;
import android.util.Base64;
import android.util.DebugUtils;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index 8b12beb..e477156 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -15,6 +15,7 @@
*/
package com.android.server.blob;
+import static android.Manifest.permission.ACCESS_BLOBS_ACROSS_USERS;
import static android.app.blob.XmlTags.ATTR_COMMIT_TIME_MS;
import static android.app.blob.XmlTags.ATTR_DESCRIPTION;
import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_NAME;
@@ -36,6 +37,7 @@
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_COMMIT_TIME;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_DESC_RES_NAME;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC;
+import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ALLOW_ACCESS_ACROSS_USERS;
import static com.android.server.blob.BlobStoreConfig.hasLeaseWaitTimeElapsed;
import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId;
import static com.android.server.blob.BlobStoreUtils.getPackageResources;
@@ -45,15 +47,18 @@
import android.app.blob.BlobHandle;
import android.app.blob.LeaseInfo;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.ResourceId;
import android.content.res.Resources;
import android.os.ParcelFileDescriptor;
import android.os.RevocableFileDescriptor;
import android.os.UserHandle;
+import android.permission.PermissionManager;
import android.system.ErrnoException;
import android.system.Os;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.StatsEvent;
@@ -62,7 +67,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import com.android.server.blob.BlobStoreManagerService.DumpArgs;
@@ -85,7 +89,6 @@
private final long mBlobId;
private final BlobHandle mBlobHandle;
- private final int mUserId;
@GuardedBy("mMetadataLock")
private final ArraySet<Committer> mCommitters = new ArraySet<>();
@@ -94,24 +97,23 @@
private final ArraySet<Leasee> mLeasees = new ArraySet<>();
/**
- * Contains packageName -> {RevocableFileDescriptors}.
+ * Contains Accessor -> {RevocableFileDescriptors}.
*
* Keep track of RevocableFileDescriptors given to clients which are not yet revoked/closed so
* that when clients access is revoked or the blob gets deleted, we can be sure that clients
* do not have any reference to the blob and the space occupied by the blob can be freed.
*/
@GuardedBy("mRevocableFds")
- private final ArrayMap<String, ArraySet<RevocableFileDescriptor>> mRevocableFds =
+ private final ArrayMap<Accessor, ArraySet<RevocableFileDescriptor>> mRevocableFds =
new ArrayMap<>();
// Do not access this directly, instead use #getBlobFile().
private File mBlobFile;
- BlobMetadata(Context context, long blobId, BlobHandle blobHandle, int userId) {
+ BlobMetadata(Context context, long blobId, BlobHandle blobHandle) {
mContext = context;
this.mBlobId = blobId;
this.mBlobHandle = blobHandle;
- this.mUserId = userId;
}
long getBlobId() {
@@ -122,10 +124,6 @@
return mBlobHandle;
}
- int getUserId() {
- return mUserId;
- }
-
void addOrReplaceCommitter(@NonNull Committer committer) {
synchronized (mMetadataLock) {
// We need to override the committer data, so first remove any existing
@@ -155,13 +153,24 @@
}
}
- void removeCommittersFromUnknownPkgs(SparseArray<String> knownPackages) {
+ void removeCommittersFromUnknownPkgs(SparseArray<SparseArray<String>> knownPackages) {
synchronized (mMetadataLock) {
- mCommitters.removeIf(committer ->
- !committer.packageName.equals(knownPackages.get(committer.uid)));
+ mCommitters.removeIf(committer -> {
+ final int userId = UserHandle.getUserId(committer.uid);
+ final SparseArray<String> userPackages = knownPackages.get(userId);
+ if (userPackages == null) {
+ return true;
+ }
+ return !committer.packageName.equals(userPackages.get(committer.uid));
+ });
}
}
+ void addCommittersAndLeasees(BlobMetadata blobMetadata) {
+ mCommitters.addAll(blobMetadata.mCommitters);
+ mLeasees.addAll(blobMetadata.mLeasees);
+ }
+
@Nullable
Committer getExistingCommitter(@NonNull String packageName, int uid) {
synchronized (mCommitters) {
@@ -201,10 +210,16 @@
}
}
- void removeLeaseesFromUnknownPkgs(SparseArray<String> knownPackages) {
+ void removeLeaseesFromUnknownPkgs(SparseArray<SparseArray<String>> knownPackages) {
synchronized (mMetadataLock) {
- mLeasees.removeIf(leasee ->
- !leasee.packageName.equals(knownPackages.get(leasee.uid)));
+ mLeasees.removeIf(leasee -> {
+ final int userId = UserHandle.getUserId(leasee.uid);
+ final SparseArray<String> userPackages = knownPackages.get(userId);
+ if (userPackages == null) {
+ return true;
+ }
+ return !leasee.packageName.equals(userPackages.get(leasee.uid));
+ });
}
}
@@ -214,6 +229,25 @@
}
}
+ void removeDataForUser(int userId) {
+ synchronized (mMetadataLock) {
+ mCommitters.removeIf(committer -> (userId == UserHandle.getUserId(committer.uid)));
+ mLeasees.removeIf(leasee -> (userId == UserHandle.getUserId(leasee.uid)));
+ mRevocableFds.entrySet().removeIf(entry -> {
+ final Accessor accessor = entry.getKey();
+ final ArraySet<RevocableFileDescriptor> rFds = entry.getValue();
+ if (userId != UserHandle.getUserId(accessor.uid)) {
+ return false;
+ }
+ for (int i = 0, fdCount = rFds.size(); i < fdCount; ++i) {
+ rFds.valueAt(i).revoke();
+ }
+ rFds.clear();
+ return true;
+ });
+ }
+ }
+
boolean hasValidLeases() {
synchronized (mMetadataLock) {
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
@@ -244,8 +278,12 @@
}
}
+ final int callingUserId = UserHandle.getUserId(callingUid);
for (int i = 0, size = mCommitters.size(); i < size; ++i) {
final Committer committer = mCommitters.valueAt(i);
+ if (callingUserId != UserHandle.getUserId(committer.uid)) {
+ continue;
+ }
// Check if the caller is the same package that committed the blob.
if (committer.equals(callingPackage, callingUid)) {
@@ -259,38 +297,105 @@
return true;
}
}
+
+ final boolean canCallerAccessBlobsAcrossUsers = checkCallerCanAccessBlobsAcrossUsers(
+ callingPackage, callingUserId);
+ if (!canCallerAccessBlobsAcrossUsers) {
+ return false;
+ }
+ for (int i = 0, size = mCommitters.size(); i < size; ++i) {
+ final Committer committer = mCommitters.valueAt(i);
+ final int committerUserId = UserHandle.getUserId(committer.uid);
+ if (callingUserId == committerUserId) {
+ continue;
+ }
+ if (!checkCallerCanAccessBlobsAcrossUsers(callingPackage, committerUserId)) {
+ continue;
+ }
+
+ // Check if the caller is allowed access as per the access mode specified
+ // by the committer.
+ if (committer.blobAccessMode.isAccessAllowedForCaller(mContext,
+ callingPackage, committer.packageName, callingUid, attributionTag)) {
+ return true;
+ }
+ }
+
+ }
+ return false;
+ }
+
+ private static boolean checkCallerCanAccessBlobsAcrossUsers(
+ String callingPackage, int callingUserId) {
+ return PermissionManager.checkPackageNamePermission(ACCESS_BLOBS_ACROSS_USERS,
+ callingPackage, callingUserId) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ boolean hasACommitterOrLeaseeInUser(int userId) {
+ return hasACommitterInUser(userId) || hasALeaseeInUser(userId);
+ }
+
+ boolean hasACommitterInUser(int userId) {
+ synchronized (mMetadataLock) {
+ for (int i = 0, size = mCommitters.size(); i < size; ++i) {
+ final Committer committer = mCommitters.valueAt(i);
+ if (userId == UserHandle.getUserId(committer.uid)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean hasALeaseeInUser(int userId) {
+ synchronized (mMetadataLock) {
+ for (int i = 0, size = mLeasees.size(); i < size; ++i) {
+ final Leasee leasee = mLeasees.valueAt(i);
+ if (userId == UserHandle.getUserId(leasee.uid)) {
+ return true;
+ }
+ }
}
return false;
}
boolean isACommitter(@NonNull String packageName, int uid) {
synchronized (mMetadataLock) {
- return isAnAccessor(mCommitters, packageName, uid);
+ return isAnAccessor(mCommitters, packageName, uid, UserHandle.getUserId(uid));
}
}
boolean isALeasee(@Nullable String packageName, int uid) {
synchronized (mMetadataLock) {
- final Leasee leasee = getAccessor(mLeasees, packageName, uid);
+ final Leasee leasee = getAccessor(mLeasees, packageName, uid,
+ UserHandle.getUserId(uid));
+ return leasee != null && leasee.isStillValid();
+ }
+ }
+
+ private boolean isALeaseeInUser(@Nullable String packageName, int uid, int userId) {
+ synchronized (mMetadataLock) {
+ final Leasee leasee = getAccessor(mLeasees, packageName, uid, userId);
return leasee != null && leasee.isStillValid();
}
}
private static <T extends Accessor> boolean isAnAccessor(@NonNull ArraySet<T> accessors,
- @Nullable String packageName, int uid) {
+ @Nullable String packageName, int uid, int userId) {
// Check if the package is an accessor of the data blob.
- return getAccessor(accessors, packageName, uid) != null;
+ return getAccessor(accessors, packageName, uid, userId) != null;
}
private static <T extends Accessor> T getAccessor(@NonNull ArraySet<T> accessors,
- @Nullable String packageName, int uid) {
+ @Nullable String packageName, int uid, int userId) {
// Check if the package is an accessor of the data blob.
for (int i = 0, size = accessors.size(); i < size; ++i) {
final Accessor accessor = accessors.valueAt(i);
if (packageName != null && uid != INVALID_UID
&& accessor.equals(packageName, uid)) {
return (T) accessor;
- } else if (packageName != null && accessor.packageName.equals(packageName)) {
+ } else if (packageName != null && accessor.packageName.equals(packageName)
+ && userId == UserHandle.getUserId(accessor.uid)) {
return (T) accessor;
} else if (uid != INVALID_UID && accessor.uid == uid) {
return (T) accessor;
@@ -299,23 +404,29 @@
return null;
}
- boolean isALeasee(@NonNull String packageName) {
- return isALeasee(packageName, INVALID_UID);
+ boolean shouldAttributeToLeasee(@NonNull String packageName, int userId,
+ boolean callerHasStatsPermission) {
+ if (!isALeaseeInUser(packageName, INVALID_UID, userId)) {
+ return false;
+ }
+ if (!callerHasStatsPermission || !hasOtherLeasees(packageName, INVALID_UID, userId)) {
+ return true;
+ }
+ return false;
}
- boolean isALeasee(int uid) {
- return isALeasee(null, uid);
+ boolean shouldAttributeToLeasee(int uid, boolean callerHasStatsPermission) {
+ final int userId = UserHandle.getUserId(uid);
+ if (!isALeaseeInUser(null, uid, userId)) {
+ return false;
+ }
+ if (!callerHasStatsPermission || !hasOtherLeasees(null, uid, userId)) {
+ return true;
+ }
+ return false;
}
- boolean hasOtherLeasees(@NonNull String packageName) {
- return hasOtherLeasees(packageName, INVALID_UID);
- }
-
- boolean hasOtherLeasees(int uid) {
- return hasOtherLeasees(null, uid);
- }
-
- private boolean hasOtherLeasees(@Nullable String packageName, int uid) {
+ private boolean hasOtherLeasees(@Nullable String packageName, int uid, int userId) {
synchronized (mMetadataLock) {
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
final Leasee leasee = mLeasees.valueAt(i);
@@ -326,7 +437,8 @@
if (packageName != null && uid != INVALID_UID
&& !leasee.equals(packageName, uid)) {
return true;
- } else if (packageName != null && !leasee.packageName.equals(packageName)) {
+ } else if (packageName != null && (!leasee.packageName.equals(packageName)
+ || userId != UserHandle.getUserId(leasee.uid))) {
return true;
} else if (uid != INVALID_UID && leasee.uid != uid) {
return true;
@@ -371,7 +483,7 @@
return mBlobFile;
}
- ParcelFileDescriptor openForRead(String callingPackage) throws IOException {
+ ParcelFileDescriptor openForRead(String callingPackage, int callingUid) throws IOException {
// TODO: Add limit on opened fds
FileDescriptor fd;
try {
@@ -381,7 +493,7 @@
}
try {
if (BlobStoreConfig.shouldUseRevocableFdForReads()) {
- return createRevocableFd(fd, callingPackage);
+ return createRevocableFd(fd, callingPackage, callingUid);
} else {
return new ParcelFileDescriptor(fd);
}
@@ -393,26 +505,28 @@
@NonNull
private ParcelFileDescriptor createRevocableFd(FileDescriptor fd,
- String callingPackage) throws IOException {
+ String callingPackage, int callingUid) throws IOException {
final RevocableFileDescriptor revocableFd =
new RevocableFileDescriptor(mContext, fd);
+ final Accessor accessor;
synchronized (mRevocableFds) {
- ArraySet<RevocableFileDescriptor> revocableFdsForPkg =
- mRevocableFds.get(callingPackage);
- if (revocableFdsForPkg == null) {
- revocableFdsForPkg = new ArraySet<>();
- mRevocableFds.put(callingPackage, revocableFdsForPkg);
+ accessor = new Accessor(callingPackage, callingUid);
+ ArraySet<RevocableFileDescriptor> revocableFdsForAccessor =
+ mRevocableFds.get(accessor);
+ if (revocableFdsForAccessor == null) {
+ revocableFdsForAccessor = new ArraySet<>();
+ mRevocableFds.put(accessor, revocableFdsForAccessor);
}
- revocableFdsForPkg.add(revocableFd);
+ revocableFdsForAccessor.add(revocableFd);
}
revocableFd.addOnCloseListener((e) -> {
synchronized (mRevocableFds) {
- final ArraySet<RevocableFileDescriptor> revocableFdsForPkg =
- mRevocableFds.get(callingPackage);
- if (revocableFdsForPkg != null) {
- revocableFdsForPkg.remove(revocableFd);
- if (revocableFdsForPkg.isEmpty()) {
- mRevocableFds.remove(callingPackage);
+ final ArraySet<RevocableFileDescriptor> revocableFdsForAccessor =
+ mRevocableFds.get(accessor);
+ if (revocableFdsForAccessor != null) {
+ revocableFdsForAccessor.remove(revocableFd);
+ if (revocableFdsForAccessor.isEmpty()) {
+ mRevocableFds.remove(accessor);
}
}
}
@@ -421,22 +535,23 @@
}
void destroy() {
- revokeAllFds();
+ revokeAndClearAllFds();
getBlobFile().delete();
}
- private void revokeAllFds() {
+ private void revokeAndClearAllFds() {
synchronized (mRevocableFds) {
- for (int i = 0, pkgCount = mRevocableFds.size(); i < pkgCount; ++i) {
- final ArraySet<RevocableFileDescriptor> packageFds =
+ for (int i = 0, accessorCount = mRevocableFds.size(); i < accessorCount; ++i) {
+ final ArraySet<RevocableFileDescriptor> rFds =
mRevocableFds.valueAt(i);
- if (packageFds == null) {
+ if (rFds == null) {
continue;
}
- for (int j = 0, fdCount = packageFds.size(); j < fdCount; ++j) {
- packageFds.valueAt(j).revoke();
+ for (int j = 0, fdCount = rFds.size(); j < fdCount; ++j) {
+ rFds.valueAt(j).revoke();
}
}
+ mRevocableFds.clear();
}
}
@@ -547,10 +662,10 @@
fout.println("<empty>");
} else {
for (int i = 0, count = mRevocableFds.size(); i < count; ++i) {
- final String packageName = mRevocableFds.keyAt(i);
- final ArraySet<RevocableFileDescriptor> packageFds =
+ final Accessor accessor = mRevocableFds.keyAt(i);
+ final ArraySet<RevocableFileDescriptor> rFds =
mRevocableFds.valueAt(i);
- fout.println(packageName + "#" + packageFds.size());
+ fout.println(accessor + ": #" + rFds.size());
}
}
fout.decreaseIndent();
@@ -560,7 +675,6 @@
void writeToXml(XmlSerializer out) throws IOException {
synchronized (mMetadataLock) {
XmlUtils.writeLongAttribute(out, ATTR_ID, mBlobId);
- XmlUtils.writeIntAttribute(out, ATTR_USER_ID, mUserId);
out.startTag(null, TAG_BLOB_HANDLE);
mBlobHandle.writeToXml(out);
@@ -584,7 +698,9 @@
static BlobMetadata createFromXml(XmlPullParser in, int version, Context context)
throws XmlPullParserException, IOException {
final long blobId = XmlUtils.readLongAttribute(in, ATTR_ID);
- final int userId = XmlUtils.readIntAttribute(in, ATTR_USER_ID);
+ if (version < XML_VERSION_ALLOW_ACCESS_ACROSS_USERS) {
+ XmlUtils.readIntAttribute(in, ATTR_USER_ID);
+ }
BlobHandle blobHandle = null;
final ArraySet<Committer> committers = new ArraySet<>();
@@ -608,7 +724,7 @@
return null;
}
- final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle, userId);
+ final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle);
blobMetadata.setCommitters(committers);
blobMetadata.setLeasees(leasees);
return blobMetadata;
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index 5cebf8d..502b29eb 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -27,12 +27,11 @@
import android.provider.DeviceConfig.Properties;
import android.text.TextUtils;
import android.util.DataUnit;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
-import com.android.internal.util.IndentingPrintWriter;
-
import java.io.File;
import java.util.concurrent.TimeUnit;
@@ -47,8 +46,9 @@
public static final int XML_VERSION_ADD_DESC_RES_NAME = 3;
public static final int XML_VERSION_ADD_COMMIT_TIME = 4;
public static final int XML_VERSION_ADD_SESSION_CREATION_TIME = 5;
+ public static final int XML_VERSION_ALLOW_ACCESS_ACROSS_USERS = 6;
- public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_SESSION_CREATION_TIME;
+ public static final int XML_VERSION_CURRENT = XML_VERSION_ALLOW_ACCESS_ACROSS_USERS;
public static final long INVALID_BLOB_ID = 0;
public static final long INVALID_BLOB_SIZE = 0;
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 0e73547..cc5e31a 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -32,6 +32,7 @@
import static com.android.server.blob.BlobStoreConfig.INVALID_BLOB_SIZE;
import static com.android.server.blob.BlobStoreConfig.LOGV;
import static com.android.server.blob.BlobStoreConfig.TAG;
+import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ALLOW_ACCESS_ACROSS_USERS;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_CURRENT;
import static com.android.server.blob.BlobStoreConfig.getAdjustedCommitTimeMs;
import static com.android.server.blob.BlobStoreConfig.getDeletionOnLastLeaseDelayMs;
@@ -83,6 +84,7 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.ExceptionUtils;
+import android.util.IndentingPrintWriter;
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -96,7 +98,6 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -129,6 +130,7 @@
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -146,9 +148,9 @@
@GuardedBy("mBlobsLock")
private long mCurrentMaxSessionId;
- // Contains data of userId -> {BlobHandle -> {BlobMetadata}}
+ // Contains data of BlobHandle -> BlobMetadata.
@GuardedBy("mBlobsLock")
- private final SparseArray<ArrayMap<BlobHandle, BlobMetadata>> mBlobsMap = new SparseArray<>();
+ private final ArrayMap<BlobHandle, BlobMetadata> mBlobsMap = new ArrayMap<>();
// Contains all ids that are currently in use.
@GuardedBy("mBlobsLock")
@@ -265,16 +267,6 @@
return userSessions;
}
- @GuardedBy("mBlobsLock")
- private ArrayMap<BlobHandle, BlobMetadata> getUserBlobsLocked(int userId) {
- ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.get(userId);
- if (userBlobs == null) {
- userBlobs = new ArrayMap<>();
- mBlobsMap.put(userId, userBlobs);
- }
- return userBlobs;
- }
-
@VisibleForTesting
void addUserSessionsForTest(LongSparseArray<BlobStoreSession> userSessions, int userId) {
synchronized (mBlobsLock) {
@@ -283,9 +275,16 @@
}
@VisibleForTesting
- void addUserBlobsForTest(ArrayMap<BlobHandle, BlobMetadata> userBlobs, int userId) {
+ BlobMetadata getBlobForTest(BlobHandle blobHandle) {
synchronized (mBlobsLock) {
- mBlobsMap.put(userId, userBlobs);
+ return mBlobsMap.get(blobHandle);
+ }
+ }
+
+ @VisibleForTesting
+ int getBlobsCountForTest() {
+ synchronized (mBlobsLock) {
+ return mBlobsMap.size();
}
}
@@ -319,14 +318,9 @@
}
@GuardedBy("mBlobsLock")
- private void addBlobForUserLocked(BlobMetadata blobMetadata, int userId) {
- addBlobForUserLocked(blobMetadata, getUserBlobsLocked(userId));
- }
-
- @GuardedBy("mBlobsLock")
- private void addBlobForUserLocked(BlobMetadata blobMetadata,
- ArrayMap<BlobHandle, BlobMetadata> userBlobs) {
- userBlobs.put(blobMetadata.getBlobHandle(), blobMetadata);
+ @VisibleForTesting
+ void addBlobLocked(BlobMetadata blobMetadata) {
+ mBlobsMap.put(blobMetadata.getBlobHandle(), blobMetadata);
addActiveBlobIdLocked(blobMetadata.getBlobId());
}
@@ -404,8 +398,7 @@
private ParcelFileDescriptor openBlobInternal(BlobHandle blobHandle, int callingUid,
String callingPackage, String attributionTag) throws IOException {
synchronized (mBlobsLock) {
- final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
- .get(blobHandle);
+ final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
callingPackage, callingUid, attributionTag)) {
if (blobMetadata == null) {
@@ -415,7 +408,7 @@
} else {
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
blobMetadata.getBlobId(), blobMetadata.getSize(),
- FrameworkStatsLog.BLOB_LEASED__RESULT__ACCESS_NOT_ALLOWED);
+ FrameworkStatsLog.BLOB_OPENED__RESULT__ACCESS_NOT_ALLOWED);
}
throw new SecurityException("Caller not allowed to access " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
@@ -425,7 +418,7 @@
blobMetadata.getBlobId(), blobMetadata.getSize(),
FrameworkStatsLog.BLOB_OPENED__RESULT__SUCCESS);
- return blobMetadata.openForRead(callingPackage);
+ return blobMetadata.openForRead(callingPackage, callingUid);
}
}
@@ -433,11 +426,11 @@
private int getCommittedBlobsCountLocked(int uid, String packageName) {
// TODO: Maintain a counter instead of traversing all the blobs
final AtomicInteger blobsCount = new AtomicInteger(0);
- forEachBlobInUser((blobMetadata) -> {
+ forEachBlobLocked(blobMetadata -> {
if (blobMetadata.isACommitter(packageName, uid)) {
blobsCount.getAndIncrement();
}
- }, UserHandle.getUserId(uid));
+ });
return blobsCount.get();
}
@@ -445,11 +438,11 @@
private int getLeasedBlobsCountLocked(int uid, String packageName) {
// TODO: Maintain a counter instead of traversing all the blobs
final AtomicInteger blobsCount = new AtomicInteger(0);
- forEachBlobInUser((blobMetadata) -> {
+ forEachBlobLocked(blobMetadata -> {
if (blobMetadata.isALeasee(packageName, uid)) {
blobsCount.getAndIncrement();
}
- }, UserHandle.getUserId(uid));
+ });
return blobsCount.get();
}
@@ -465,8 +458,16 @@
throw new LimitExceededException("Too many leased blobs for the caller: "
+ leasesCount);
}
- final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
- .get(blobHandle);
+ if (leaseExpiryTimeMillis != 0 && blobHandle.expiryTimeMillis != 0
+ && leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
+ INVALID_BLOB_ID, INVALID_BLOB_SIZE,
+ FrameworkStatsLog.BLOB_LEASED__RESULT__LEASE_EXPIRY_INVALID);
+ throw new IllegalArgumentException(
+ "Lease expiry cannot be later than blobs expiry time");
+ }
+
+ final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
callingPackage, callingUid, attributionTag)) {
if (blobMetadata == null) {
@@ -481,15 +482,7 @@
throw new SecurityException("Caller not allowed to access " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
- if (leaseExpiryTimeMillis != 0 && blobHandle.expiryTimeMillis != 0
- && leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) {
- FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
- blobMetadata.getBlobId(), blobMetadata.getSize(),
- FrameworkStatsLog.BLOB_LEASED__RESULT__LEASE_EXPIRY_INVALID);
- throw new IllegalArgumentException(
- "Lease expiry cannot be later than blobs expiry time");
- }
if (blobMetadata.getSize()
> getRemainingLeaseQuotaBytesInternal(callingUid, callingPackage)) {
@@ -518,20 +511,18 @@
@GuardedBy("mBlobsLock")
long getTotalUsageBytesLocked(int callingUid, String callingPackage) {
final AtomicLong totalBytes = new AtomicLong(0);
- forEachBlobInUser((blobMetadata) -> {
+ forEachBlobLocked((blobMetadata) -> {
if (blobMetadata.isALeasee(callingPackage, callingUid)) {
totalBytes.getAndAdd(blobMetadata.getSize());
}
- }, UserHandle.getUserId(callingUid));
+ });
return totalBytes.get();
}
private void releaseLeaseInternal(BlobHandle blobHandle, int callingUid,
String callingPackage, String attributionTag) {
synchronized (mBlobsLock) {
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
- getUserBlobsLocked(UserHandle.getUserId(callingUid));
- final BlobMetadata blobMetadata = userBlobs.get(blobHandle);
+ final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
callingPackage, callingUid, attributionTag)) {
throw new SecurityException("Caller not allowed to access " + blobHandle
@@ -547,12 +538,12 @@
synchronized (mBlobsLock) {
// Check if blobMetadata object is still valid. If it is not, then
// it means that it was already deleted and nothing else to do here.
- if (!Objects.equals(userBlobs.get(blobHandle), blobMetadata)) {
+ if (!Objects.equals(mBlobsMap.get(blobHandle), blobMetadata)) {
return;
}
if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
deleteBlobLocked(blobMetadata);
- userBlobs.remove(blobHandle);
+ mBlobsMap.remove(blobHandle);
}
writeBlobsInfoAsync();
}
@@ -583,12 +574,18 @@
}
return packageResources;
};
- getUserBlobsLocked(userId).forEach((blobHandle, blobMetadata) -> {
+ forEachBlobLocked((blobHandle, blobMetadata) -> {
+ if (!blobMetadata.hasACommitterOrLeaseeInUser(userId)) {
+ return;
+ }
final ArrayList<LeaseInfo> leaseInfos = new ArrayList<>();
blobMetadata.forEachLeasee(leasee -> {
if (!leasee.isStillValid()) {
return;
}
+ if (userId != UserHandle.getUserId(leasee.uid)) {
+ return;
+ }
final int descriptionResId = leasee.descriptionResEntryName == null
? Resources.ID_NULL
: getDescriptionResourceId(resourcesGetter.apply(leasee.packageName),
@@ -608,9 +605,7 @@
private void deleteBlobInternal(long blobId, int callingUid) {
synchronized (mBlobsLock) {
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(
- UserHandle.getUserId(callingUid));
- userBlobs.entrySet().removeIf(entry -> {
+ mBlobsMap.entrySet().removeIf(entry -> {
final BlobMetadata blobMetadata = entry.getValue();
if (blobMetadata.getBlobId() == blobId) {
deleteBlobLocked(blobMetadata);
@@ -625,19 +620,20 @@
private List<BlobHandle> getLeasedBlobsInternal(int callingUid,
@NonNull String callingPackage) {
final ArrayList<BlobHandle> leasedBlobs = new ArrayList<>();
- forEachBlobInUser(blobMetadata -> {
- if (blobMetadata.isALeasee(callingPackage, callingUid)) {
- leasedBlobs.add(blobMetadata.getBlobHandle());
- }
- }, UserHandle.getUserId(callingUid));
+ synchronized (mBlobsLock) {
+ forEachBlobLocked(blobMetadata -> {
+ if (blobMetadata.isALeasee(callingPackage, callingUid)) {
+ leasedBlobs.add(blobMetadata.getBlobHandle());
+ }
+ });
+ }
return leasedBlobs;
}
private LeaseInfo getLeaseInfoInternal(BlobHandle blobHandle,
int callingUid, @NonNull String callingPackage, String attributionTag) {
synchronized (mBlobsLock) {
- final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
- .get(blobHandle);
+ final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
callingPackage, callingUid, attributionTag)) {
throw new SecurityException("Caller not allowed to access " + blobHandle
@@ -699,14 +695,14 @@
FrameworkStatsLog.BLOB_COMMITTED__RESULT__COUNT_LIMIT_EXCEEDED);
break;
}
- final int userId = UserHandle.getUserId(session.getOwnerUid());
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(
- userId);
- BlobMetadata blob = userBlobs.get(session.getBlobHandle());
- if (blob == null) {
+ final BlobMetadata blob;
+ final int blobIndex = mBlobsMap.indexOfKey(session.getBlobHandle());
+ if (blobIndex >= 0) {
+ blob = mBlobsMap.valueAt(blobIndex);
+ } else {
blob = new BlobMetadata(mContext, session.getSessionId(),
- session.getBlobHandle(), userId);
- addBlobForUserLocked(blob, userBlobs);
+ session.getBlobHandle());
+ addBlobLocked(blob);
}
final Committer existingCommitter = blob.getExistingCommitter(
session.getOwnerPackageName(), session.getOwnerUid());
@@ -738,7 +734,7 @@
// But if it is a recommit, just leave it as is.
if (session.getSessionId() == blob.getBlobId()) {
deleteBlobLocked(blob);
- userBlobs.remove(blob.getBlobHandle());
+ mBlobsMap.remove(blob.getBlobHandle());
}
}
// Delete redundant data from recommits.
@@ -874,13 +870,10 @@
out.startTag(null, TAG_BLOBS);
XmlUtils.writeIntAttribute(out, ATTR_VERSION, XML_VERSION_CURRENT);
- for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
- for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
- out.startTag(null, TAG_BLOB);
- userBlobs.valueAt(j).writeToXml(out);
- out.endTag(null, TAG_BLOB);
- }
+ for (int i = 0, count = mBlobsMap.size(); i < count; ++i) {
+ out.startTag(null, TAG_BLOB);
+ mBlobsMap.valueAt(i).writeToXml(out);
+ out.endTag(null, TAG_BLOB);
}
out.endTag(null, TAG_BLOBS);
@@ -925,16 +918,21 @@
if (TAG_BLOB.equals(in.getName())) {
final BlobMetadata blobMetadata = BlobMetadata.createFromXml(
in, version, mContext);
- final SparseArray<String> userPackages = allPackages.get(
- blobMetadata.getUserId());
- if (userPackages == null) {
- blobMetadata.getBlobFile().delete();
- } else {
- addBlobForUserLocked(blobMetadata, blobMetadata.getUserId());
- blobMetadata.removeCommittersFromUnknownPkgs(userPackages);
- blobMetadata.removeLeaseesFromUnknownPkgs(userPackages);
- }
+ blobMetadata.removeCommittersFromUnknownPkgs(allPackages);
+ blobMetadata.removeLeaseesFromUnknownPkgs(allPackages);
mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.getBlobId());
+ if (version >= XML_VERSION_ALLOW_ACCESS_ACROSS_USERS) {
+ addBlobLocked(blobMetadata);
+ } else {
+ final BlobMetadata existingBlobMetadata = mBlobsMap.get(
+ blobMetadata.getBlobHandle());
+ if (existingBlobMetadata == null) {
+ addBlobLocked(blobMetadata);
+ } else {
+ existingBlobMetadata.addCommittersAndLeasees(blobMetadata);
+ blobMetadata.getBlobFile().delete();
+ }
+ }
}
}
if (LOGV) {
@@ -977,14 +975,6 @@
}
}
- private int getPackageUid(String packageName, int userId) {
- final int uid = mPackageManagerInternal.getPackageUid(
- packageName,
- MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_UNINSTALLED_PACKAGES,
- userId);
- return uid;
- }
-
private SparseArray<SparseArray<String>> getAllPackages() {
final SparseArray<SparseArray<String>> allPackages = new SparseArray<>();
final int[] allUsers = LocalServices.getService(UserManagerInternal.class).getUserIds();
@@ -1004,7 +994,7 @@
return allPackages;
}
- AtomicFile prepareSessionsIndexFile() {
+ private AtomicFile prepareSessionsIndexFile() {
final File file = BlobStoreConfig.prepareSessionIndexFile();
if (file == null) {
return null;
@@ -1012,7 +1002,7 @@
return new AtomicFile(file, "session_index" /* commitLogTag */);
}
- AtomicFile prepareBlobsIndexFile() {
+ private AtomicFile prepareBlobsIndexFile() {
final File file = BlobStoreConfig.prepareBlobsIndexFile();
if (file == null) {
return null;
@@ -1037,9 +1027,7 @@
writeBlobSessionsAsync();
// Remove the package from the committer and leasee list
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
- getUserBlobsLocked(UserHandle.getUserId(uid));
- userBlobs.entrySet().removeIf(entry -> {
+ mBlobsMap.entrySet().removeIf(entry -> {
final BlobMetadata blobMetadata = entry.getValue();
final boolean isACommitter = blobMetadata.isACommitter(packageName, uid);
if (isACommitter) {
@@ -1074,14 +1062,15 @@
}
}
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
- mBlobsMap.removeReturnOld(userId);
- if (userBlobs != null) {
- for (int i = 0, count = userBlobs.size(); i < count; ++i) {
- final BlobMetadata blobMetadata = userBlobs.valueAt(i);
+ mBlobsMap.entrySet().removeIf(entry -> {
+ final BlobMetadata blobMetadata = entry.getValue();
+ blobMetadata.removeDataForUser(userId);
+ if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
deleteBlobLocked(blobMetadata);
+ return true;
}
- }
+ return false;
+ });
if (LOGV) {
Slog.v(TAG, "Removed blobs data in user " + userId);
}
@@ -1114,22 +1103,19 @@
}
// Cleanup any stale blobs.
- for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
- userBlobs.entrySet().removeIf(entry -> {
- final BlobMetadata blobMetadata = entry.getValue();
+ mBlobsMap.entrySet().removeIf(entry -> {
+ final BlobMetadata blobMetadata = entry.getValue();
- // Remove expired leases
- blobMetadata.removeExpiredLeases();
+ // Remove expired leases
+ blobMetadata.removeExpiredLeases();
- if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
- deleteBlobLocked(blobMetadata);
- deletedBlobIds.add(blobMetadata.getBlobId());
- return true;
- }
- return false;
- });
- }
+ if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
+ deleteBlobLocked(blobMetadata);
+ deletedBlobIds.add(blobMetadata.getBlobId());
+ return true;
+ }
+ return false;
+ });
writeBlobsInfoAsync();
// Cleanup any stale sessions.
@@ -1195,34 +1181,34 @@
void runClearAllBlobs(@UserIdInt int userId) {
synchronized (mBlobsLock) {
- for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
- final int blobUserId = mBlobsMap.keyAt(i);
- if (userId != UserHandle.USER_ALL && userId != blobUserId) {
- continue;
+ mBlobsMap.entrySet().removeIf(entry -> {
+ final BlobMetadata blobMetadata = entry.getValue();
+ if (userId == UserHandle.USER_ALL) {
+ mActiveBlobIds.remove(blobMetadata.getBlobId());
+ return true;
}
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
- for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
- mActiveBlobIds.remove(userBlobs.valueAt(j).getBlobId());
+ blobMetadata.removeDataForUser(userId);
+ if (blobMetadata.shouldBeDeleted(false /* respectLeaseWaitTime */)) {
+ mActiveBlobIds.remove(blobMetadata.getBlobId());
+ return true;
}
- }
- if (userId == UserHandle.USER_ALL) {
- mBlobsMap.clear();
- } else {
- mBlobsMap.remove(userId);
- }
+ return false;
+ });
writeBlobsInfoAsync();
}
}
void deleteBlob(@NonNull BlobHandle blobHandle, @UserIdInt int userId) {
synchronized (mBlobsLock) {
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId);
- final BlobMetadata blobMetadata = userBlobs.get(blobHandle);
+ final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
if (blobMetadata == null) {
return;
}
- deleteBlobLocked(blobMetadata);
- userBlobs.remove(blobHandle);
+ blobMetadata.removeDataForUser(userId);
+ if (blobMetadata.shouldBeDeleted(false /* respectLeaseWaitTime */)) {
+ deleteBlobLocked(blobMetadata);
+ mBlobsMap.remove(blobHandle);
+ }
writeBlobsInfoAsync();
}
}
@@ -1235,11 +1221,12 @@
boolean isBlobAvailable(long blobId, int userId) {
synchronized (mBlobsLock) {
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId);
- for (BlobMetadata blobMetadata : userBlobs.values()) {
- if (blobMetadata.getBlobId() == blobId) {
- return true;
+ for (int i = 0, blobCount = mBlobsMap.size(); i < blobCount; ++i) {
+ final BlobMetadata blobMetadata = mBlobsMap.valueAt(i);
+ if (blobMetadata.getBlobId() != blobId) {
+ continue;
}
+ return blobMetadata.hasACommitterInUser(userId);
}
return false;
}
@@ -1274,27 +1261,22 @@
@GuardedBy("mBlobsLock")
private void dumpBlobsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) {
- for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
- final int userId = mBlobsMap.keyAt(i);
- if (!dumpArgs.shouldDumpUser(userId)) {
+ fout.println("List of blobs (" + mBlobsMap.size() + "):");
+ fout.increaseIndent();
+ for (int i = 0, blobCount = mBlobsMap.size(); i < blobCount; ++i) {
+ final BlobMetadata blobMetadata = mBlobsMap.valueAt(i);
+ if (!dumpArgs.shouldDumpBlob(blobMetadata.getBlobId())) {
continue;
}
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
- fout.println("List of blobs in user #"
- + userId + " (" + userBlobs.size() + "):");
+ fout.println("Blob #" + blobMetadata.getBlobId());
fout.increaseIndent();
- for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
- final BlobMetadata blobMetadata = userBlobs.valueAt(j);
- if (!dumpArgs.shouldDumpBlob(blobMetadata.getBlobId())) {
- continue;
- }
- fout.println("Blob #" + blobMetadata.getBlobId());
- fout.increaseIndent();
- blobMetadata.dump(fout, dumpArgs);
- fout.decreaseIndent();
- }
+ blobMetadata.dump(fout, dumpArgs);
fout.decreaseIndent();
}
+ if (mBlobsMap.isEmpty()) {
+ fout.println("<empty>");
+ }
+ fout.decreaseIndent();
}
private class BlobStorageStatsAugmenter implements StorageStatsAugmenter {
@@ -1308,13 +1290,12 @@
}
}, userId);
- forEachBlobInUser(blobMetadata -> {
- if (blobMetadata.isALeasee(packageName)) {
- if (!blobMetadata.hasOtherLeasees(packageName) || !callerHasStatsPermission) {
- blobsDataSize.getAndAdd(blobMetadata.getSize());
- }
+ forEachBlob(blobMetadata -> {
+ if (blobMetadata.shouldAttributeToLeasee(packageName, userId,
+ callerHasStatsPermission)) {
+ blobsDataSize.getAndAdd(blobMetadata.getSize());
}
- }, userId);
+ });
stats.dataSize += blobsDataSize.get();
}
@@ -1330,13 +1311,12 @@
}
}, userId);
- forEachBlobInUser(blobMetadata -> {
- if (blobMetadata.isALeasee(uid)) {
- if (!blobMetadata.hasOtherLeasees(uid) || !callerHasStatsPermission) {
- blobsDataSize.getAndAdd(blobMetadata.getSize());
- }
+ forEachBlob(blobMetadata -> {
+ if (blobMetadata.shouldAttributeToLeasee(uid,
+ callerHasStatsPermission)) {
+ blobsDataSize.getAndAdd(blobMetadata.getSize());
}
- }, userId);
+ });
stats.dataSize += blobsDataSize.get();
}
@@ -1352,13 +1332,26 @@
}
}
- private void forEachBlobInUser(Consumer<BlobMetadata> consumer, int userId) {
- synchronized (mBlobsLock) {
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId);
- for (int i = 0, count = userBlobs.size(); i < count; ++i) {
- final BlobMetadata blobMetadata = userBlobs.valueAt(i);
- consumer.accept(blobMetadata);
- }
+ private void forEachBlob(Consumer<BlobMetadata> consumer) {
+ synchronized (mBlobsMap) {
+ forEachBlobLocked(consumer);
+ }
+ }
+
+ @GuardedBy("mBlobsMap")
+ private void forEachBlobLocked(Consumer<BlobMetadata> consumer) {
+ for (int blobIdx = 0, count = mBlobsMap.size(); blobIdx < count; ++blobIdx) {
+ final BlobMetadata blobMetadata = mBlobsMap.valueAt(blobIdx);
+ consumer.accept(blobMetadata);
+ }
+ }
+
+ @GuardedBy("mBlobsMap")
+ private void forEachBlobLocked(BiConsumer<BlobHandle, BlobMetadata> consumer) {
+ for (int blobIdx = 0, count = mBlobsMap.size(); blobIdx < count; ++blobIdx) {
+ final BlobHandle blobHandle = mBlobsMap.keyAt(blobIdx);
+ final BlobMetadata blobMetadata = mBlobsMap.valueAt(blobIdx);
+ consumer.accept(blobHandle, blobMetadata);
}
}
@@ -1886,15 +1879,7 @@
}
private int pullBlobData(int atomTag, List<StatsEvent> data) {
- synchronized (mBlobsLock) {
- for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
- for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
- final BlobMetadata blob = userBlobs.valueAt(j);
- data.add(blob.dumpAsStatsEvent(atomTag));
- }
- }
- }
+ forEachBlob(blobMetadata -> data.add(blobMetadata.dumpAsStatsEvent(atomTag)));
return StatsManager.PULL_SUCCESS;
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 2c3f682..3f0032f 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -56,12 +56,12 @@
import android.system.ErrnoException;
import android.system.Os;
import android.util.ExceptionUtils;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.blob.BlobStoreManagerService.DumpArgs;
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 88f3df8..01f31e4 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
@@ -208,10 +209,10 @@
public static final int FLAG_PRIORITIZE = 1 << 6;
/**
- * For apps targeting {@link Build.VERSION_CODES#S} or above, APIs
- * {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and
- * {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} will require holding a new
- * permission {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM}
+ * For apps targeting {@link Build.VERSION_CODES#S} or above, any APIs setting exact alarms,
+ * e.g. {@link #setExact(int, long, PendingIntent)},
+ * {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} and others will require holding a new
+ * permission {@link Manifest.permission#SCHEDULE_EXACT_ALARM}
*
* @hide
*/
@@ -219,6 +220,21 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
public static final long REQUIRE_EXACT_ALARM_PERMISSION = 171306433L;
+ /**
+ * For apps targeting {@link Build.VERSION_CODES#S} or above, all inexact alarms will require
+ * to have a minimum window size, expected to be on the order of a few minutes.
+ *
+ * Practically, any alarms requiring smaller windows are the same as exact alarms and should use
+ * the corresponding APIs provided, like {@link #setExact(int, long, PendingIntent)}, et al.
+ *
+ * Inexact alarm with shorter windows specified will have their windows elongated by the system.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+ public static final long ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS = 185199076L;
+
@UnsupportedAppUsage
private final IAlarmManager mService;
private final Context mContext;
@@ -483,6 +499,11 @@
* modest timeliness requirements for its alarms.
*
* <p>
+ * Note: Starting with API {@link Build.VERSION_CODES#S}, the system will ensure that the window
+ * specified is at least a few minutes, as smaller windows are considered practically exact
+ * and should use the other APIs provided for exact alarms.
+ *
+ * <p>
* This method can also be used to achieve strict ordering guarantees among
* multiple alarms by ensuring that the windows requested for each alarm do
* not intersect.
@@ -532,6 +553,13 @@
* The OnAlarmListener {@link OnAlarmListener#onAlarm() onAlarm()} method will be
* invoked via the specified target Handler, or on the application's main looper
* if {@code null} is passed as the {@code targetHandler} parameter.
+ *
+ * <p>
+ * Note: Starting with API {@link Build.VERSION_CODES#S}, the system will ensure that the window
+ * specified is at least a few minutes, as smaller windows are considered practically exact
+ * and should use the other APIs provided for exact alarms.
+ *
+ * @see #setWindow(int, long, long, PendingIntent)
*/
public void setWindow(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
String tag, OnAlarmListener listener, Handler targetHandler) {
@@ -564,8 +592,8 @@
* in milliseconds. The alarm will be delivered no later than this many
* milliseconds after {@code windowStartMillis}. Note that this parameter
* is a <i>duration,</i> not the timestamp of the end of the window.
- * @param tag string describing the alarm, used for logging and battery-use
- * attribution
+ * @param tag Optional. A string describing the alarm, used for logging and battery-use
+ * attribution.
* @param listener {@link OnAlarmListener} instance whose
* {@link OnAlarmListener#onAlarm() onAlarm()} method will be
* called when the alarm time is reached. A given OnAlarmListener instance can
@@ -578,9 +606,8 @@
@SystemApi
@RequiresPermission(Manifest.permission.SCHEDULE_PRIORITIZED_ALARM)
public void setPrioritized(@AlarmType int type, long windowStartMillis, long windowLengthMillis,
- @NonNull String tag, @NonNull Executor executor, @NonNull OnAlarmListener listener) {
+ @Nullable String tag, @NonNull Executor executor, @NonNull OnAlarmListener listener) {
Objects.requireNonNull(executor);
- Objects.requireNonNull(tag);
Objects.requireNonNull(listener);
setImpl(type, windowStartMillis, windowLengthMillis, 0, FLAG_PRIORITIZE, null, listener,
tag, executor, null, null);
@@ -755,6 +782,50 @@
targetHandler, workSource, null);
}
+ /**
+ * Exact version of {@link #set(int, long, long, long, OnAlarmListener, Handler, WorkSource)}.
+ * This equivalent to calling the aforementioned API with {@code windowMillis} and
+ * {@code intervalMillis} set to 0.
+ * One subtle difference is that this API requires {@code workSource} to be non-null. If you
+ * don't want to attribute this alarm to another app for battery consumption, you should use
+ * {@link #setExact(int, long, String, OnAlarmListener, Handler)} instead.
+ *
+ * <p>
+ * Note that using this API requires you to hold
+ * {@link Manifest.permission#SCHEDULE_EXACT_ALARM}, unless you are on the system's power
+ * allowlist. This can be set, for example, by marking the app as {@code <allow-in-power-save>}
+ * within the system config.
+ *
+ * @param type type of alarm
+ * @param triggerAtMillis The exact time in milliseconds, that the alarm should be delivered,
+ * expressed in the appropriate clock's units (depending on the alarm
+ * type).
+ * @param listener {@link OnAlarmListener} instance whose
+ * {@link OnAlarmListener#onAlarm() onAlarm()} method will be called when
+ * the alarm time is reached.
+ * @param executor The {@link Executor} on which to execute the listener's onAlarm()
+ * callback.
+ * @param tag Optional. A string tag used to identify this alarm in logs and
+ * battery-attribution.
+ * @param workSource A {@link WorkSource} object to attribute this alarm to the app that
+ * requested this work.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {
+ Manifest.permission.UPDATE_DEVICE_STATS,
+ Manifest.permission.SCHEDULE_EXACT_ALARM}, conditional = true)
+ public void setExact(@AlarmType int type, long triggerAtMillis, @Nullable String tag,
+ @NonNull Executor executor, @NonNull WorkSource workSource,
+ @NonNull OnAlarmListener listener) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(workSource);
+ Objects.requireNonNull(listener);
+ setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, null, listener, tag, executor,
+ workSource, null);
+ }
+
+
private void setImpl(@AlarmType int type, long triggerAtMillis, long windowMillis,
long intervalMillis, int flags, PendingIntent operation, final OnAlarmListener listener,
String listenerTag, Handler targetHandler, WorkSource workSource,
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index 44e87c3..93b6566 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -283,9 +283,10 @@
*/
public static final int REASON_PACKAGE_REPLACED = 311;
/**
- * LocationProviderManager.
+ * LocationProvider.
* @hide
*/
+ @SystemApi
public static final int REASON_LOCATION_PROVIDER = 312;
/**
* MediaButtonReceiver.
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index 29a5dee..4ce31e9 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -284,9 +284,10 @@
*/
public static final int REASON_PACKAGE_REPLACED = PowerExemptionManager.REASON_PACKAGE_REPLACED;
/**
- * LocationProviderManager.
+ * LocationProvider.
* @hide
*/
+ @SystemApi
public static final int REASON_LOCATION_PROVIDER =
PowerExemptionManager.REASON_LOCATION_PROVIDER;
/**
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 7a36141..03d9a96 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -452,7 +452,8 @@
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
private static final long DEFAULT_MAX_INTERVAL = 365 * INTERVAL_DAY;
- private static final long DEFAULT_MIN_WINDOW = 10_000;
+ // TODO (b/185199076): Tune based on breakage reports.
+ private static final long DEFAULT_MIN_WINDOW = 30 * 60 * 1000;
private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10 * 1000;
private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
private static final int DEFAULT_MAX_ALARMS_PER_UID = 500;
@@ -1683,17 +1684,23 @@
}
}
- if ((flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) {
- // Do not support windows for idle-until alarms.
- windowLength = AlarmManager.WINDOW_EXACT;
- }
-
- // Sanity check the window length. This will catch people mistakenly
- // trying to pass an end-of-window timestamp rather than a duration.
- if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
+ // Snap the window to reasonable limits.
+ if (windowLength > INTERVAL_DAY) {
Slog.w(TAG, "Window length " + windowLength
- + "ms suspiciously long; limiting to 1 hour");
- windowLength = AlarmManager.INTERVAL_HOUR;
+ + "ms suspiciously long; limiting to 1 day");
+ windowLength = INTERVAL_DAY;
+ } else if (windowLength > 0 && windowLength < mConstants.MIN_WINDOW
+ && (flags & FLAG_PRIORITIZE) == 0) {
+ if (CompatChanges.isChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS,
+ callingPackage, UserHandle.getUserHandleForUid(callingUid))) {
+ Slog.w(TAG, "Window length " + windowLength + "ms too short; expanding to "
+ + mConstants.MIN_WINDOW + "ms.");
+ windowLength = mConstants.MIN_WINDOW;
+ } else {
+ // TODO (b/185199076): Remove log once we have some data about what apps will break
+ Slog.wtf(TAG, "Short window " + windowLength + "ms specified by "
+ + callingPackage);
+ }
}
// Sanity check the recurrence interval. This will catch people who supply
@@ -1730,14 +1737,13 @@
final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger;
final long maxElapsed;
- if (windowLength == AlarmManager.WINDOW_EXACT) {
+ if (windowLength == 0) {
maxElapsed = triggerElapsed;
} else if (windowLength < 0) {
maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval);
// Fix this window in place, so that as time approaches we don't collapse it.
windowLength = maxElapsed - triggerElapsed;
} else {
- windowLength = Math.max(windowLength, mConstants.MIN_WINDOW);
maxElapsed = triggerElapsed + windowLength;
}
synchronized (mLock) {
@@ -2135,17 +2141,63 @@
+ " does not belong to the calling uid " + callingUid);
}
- final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0;
- final boolean exact = (windowLength == AlarmManager.WINDOW_EXACT);
+ // Repeating alarms must use PendingIntent, not direct listener
+ if (interval != 0 && directReceiver != null) {
+ throw new IllegalArgumentException("Repeating alarms cannot use AlarmReceivers");
+ }
- // make sure the caller is allowed to use the requested kind of alarm, and also
+ if (workSource != null) {
+ getContext().enforcePermission(
+ android.Manifest.permission.UPDATE_DEVICE_STATS,
+ Binder.getCallingPid(), callingUid, "AlarmManager.set");
+ }
+
+ if ((flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) {
+ // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm
+ // manager when to come out of idle mode, which is only for DeviceIdleController.
+ if (callingUid != Process.SYSTEM_UID) {
+ // TODO (b/169463012): Throw instead of tolerating this mistake.
+ flags &= ~AlarmManager.FLAG_IDLE_UNTIL;
+ } else {
+ // Do not support windows for idle-until alarms.
+ windowLength = 0;
+ }
+ }
+
+ // Remove flags reserved for the service, we will apply those later as appropriate.
+ flags &= ~(FLAG_WAKE_FROM_IDLE | FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
+ | FLAG_ALLOW_WHILE_IDLE_COMPAT);
+
+ // If this alarm is for an alarm clock, then it must be exact and we will
+ // use it to wake early from idle if needed.
+ if (alarmClock != null) {
+ flags |= FLAG_WAKE_FROM_IDLE;
+ windowLength = 0;
+
+ // If the caller is a core system component or on the user's allowlist, and not calling
+ // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED.
+ // This means we will allow these alarms to go off as normal even while idle, with no
+ // timing restrictions.
+ } else if (workSource == null && (UserHandle.isCore(callingUid)
+ || UserHandle.isSameApp(callingUid, mSystemUiUid)
+ || ((mAppStateTracker != null)
+ && mAppStateTracker.isUidPowerSaveUserExempt(callingUid)))) {
+ flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
+ flags &= ~(FLAG_ALLOW_WHILE_IDLE | FLAG_PRIORITIZE);
+ }
+
+ final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0;
+ final boolean exact = (windowLength == 0);
+
+ // Make sure the caller is allowed to use the requested kind of alarm, and also
// decide what quota and broadcast options to use.
Bundle idleOptions = null;
if ((flags & FLAG_PRIORITIZE) != 0) {
getContext().enforcePermission(
Manifest.permission.SCHEDULE_PRIORITIZED_ALARM,
Binder.getCallingPid(), callingUid, "AlarmManager.setPrioritized");
- flags &= ~(FLAG_ALLOW_WHILE_IDLE | FLAG_ALLOW_WHILE_IDLE_COMPAT);
+ // The API doesn't allow using both together.
+ flags &= ~FLAG_ALLOW_WHILE_IDLE;
} else if (exact || allowWhileIdle) {
final boolean needsPermission;
boolean lowerQuota;
@@ -2183,55 +2235,11 @@
}
}
- // Repeating alarms must use PendingIntent, not direct listener
- if (interval != 0) {
- if (directReceiver != null) {
- throw new IllegalArgumentException(
- "Repeating alarms cannot use AlarmReceivers");
- }
- }
-
- if (workSource != null) {
- getContext().enforcePermission(
- android.Manifest.permission.UPDATE_DEVICE_STATS,
- Binder.getCallingPid(), callingUid, "AlarmManager.set");
- }
-
- // No incoming callers can request either WAKE_FROM_IDLE or
- // ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate.
- flags &= ~(FLAG_WAKE_FROM_IDLE | FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED);
-
- // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm
- // manager when to come out of idle mode, which is only for DeviceIdleController.
- if (callingUid != Process.SYSTEM_UID) {
- flags &= ~AlarmManager.FLAG_IDLE_UNTIL;
- }
-
// If this is an exact time alarm, then it can't be batched with other alarms.
- if (windowLength == AlarmManager.WINDOW_EXACT) {
+ if (exact) {
flags |= AlarmManager.FLAG_STANDALONE;
}
- // If this alarm is for an alarm clock, then it must be standalone and we will
- // use it to wake early from idle if needed.
- if (alarmClock != null) {
- flags |= FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
-
- // If the caller is a core system component or on the user's whitelist, and not calling
- // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED.
- // This means we will allow these alarms to go off as normal even while idle, with no
- // timing restrictions.
- } else if (workSource == null && (UserHandle.isCore(callingUid)
- || UserHandle.isSameApp(callingUid, mSystemUiUid)
- || ((mAppStateTracker != null)
- && mAppStateTracker.isUidPowerSaveUserExempt(callingUid)))) {
- flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
- flags &= ~FLAG_ALLOW_WHILE_IDLE;
- flags &= ~FLAG_ALLOW_WHILE_IDLE_COMPAT;
- flags &= ~FLAG_PRIORITIZE;
- idleOptions = null;
- }
-
setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
listenerTag, flags, workSource, alarmClock, callingUid, callingPackage,
idleOptions);
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
index c37d2c3..9b1b066 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
@@ -16,7 +16,6 @@
package com.android.server.alarm;
-import static com.android.server.alarm.AlarmManagerService.TAG;
import static com.android.server.alarm.AlarmManagerService.dumpAlarmList;
import static com.android.server.alarm.AlarmManagerService.isTimeTickAlarm;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index d94d638..366e174 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -16,6 +16,7 @@
package com.android.server.job;
+import static com.android.server.job.JobSchedulerService.MAX_JOB_CONTEXTS_COUNT;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.annotation.IntDef;
@@ -39,7 +40,9 @@
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Pair;
+import android.util.Pools;
import android.util.Slog;
+import android.util.SparseArrayMap;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TimeUtils;
@@ -60,6 +63,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Iterator;
import java.util.List;
+import java.util.function.Consumer;
/**
* This class decides, given the various configuration and the system status, which jobs can start
@@ -73,6 +77,12 @@
private static final String KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS =
CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms";
private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000;
+ private static final String KEY_PKG_CONCURRENCY_LIMIT_EJ =
+ CONFIG_KEY_PREFIX_CONCURRENCY + "pkg_concurrency_limit_ej";
+ private static final int DEFAULT_PKG_CONCURRENCY_LIMIT_EJ = 3;
+ private static final String KEY_PKG_CONCURRENCY_LIMIT_REGULAR =
+ CONFIG_KEY_PREFIX_CONCURRENCY + "pkg_concurrency_limit_regular";
+ private static final int DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR = MAX_JOB_CONTEXTS_COUNT / 2;
/**
* Set of possible execution types that a job can have. The actual type(s) of a job are based
@@ -165,8 +175,6 @@
private long mLastScreenOnRealtime;
private long mLastScreenOffRealtime;
- private static final int MAX_JOB_CONTEXTS_COUNT = JobSchedulerService.MAX_JOB_CONTEXTS_COUNT;
-
private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_ON =
new WorkConfigLimitsPerMemoryTrimLevel(
new WorkTypeConfig("screen_on_normal", 11,
@@ -274,11 +282,28 @@
private final WorkCountTracker mWorkCountTracker = new WorkCountTracker();
+ private final Pools.Pool<PackageStats> mPkgStatsPool =
+ new Pools.SimplePool<>(MAX_JOB_CONTEXTS_COUNT);
+
+ private final SparseArrayMap<String, PackageStats> mActivePkgStats = new SparseArrayMap<>();
+
private WorkTypeConfig mWorkTypeConfig = CONFIG_LIMITS_SCREEN_OFF.normal;
/** Wait for this long after screen off before adjusting the job concurrency. */
private long mScreenOffAdjustmentDelayMs = DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS;
+ /**
+ * The maximum number of expedited jobs a single userId-package can have running simultaneously.
+ * TOP apps are not limited.
+ */
+ private long mPkgConcurrencyLimitEj = DEFAULT_PKG_CONCURRENCY_LIMIT_EJ;
+
+ /**
+ * The maximum number of regular jobs a single userId-package can have running simultaneously.
+ * TOP apps are not limited.
+ */
+ private long mPkgConcurrencyLimitRegular = DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR;
+
/** Current memory trim level. */
private int mLastMemoryTrimLevel;
@@ -286,6 +311,9 @@
private long mNextSystemStateRefreshTime;
private static final int SYSTEM_STATE_REFRESH_MIN_INTERVAL = 1000;
+ private final Consumer<PackageStats> mPackageStatsStagingCountClearer =
+ PackageStats::resetStagedCount;
+
private final StatLogger mStatLogger = new StatLogger(new String[]{
"assignJobsToContexts",
"refreshSystemState",
@@ -330,6 +358,21 @@
onInteractiveStateChanged(mPowerManager.isInteractive());
}
+ @GuardedBy("mLock")
+ void onAppRemovedLocked(String pkgName, int uid) {
+ final PackageStats packageStats = mActivePkgStats.get(UserHandle.getUserId(uid), pkgName);
+ if (packageStats != null) {
+ if (packageStats.numRunningEj > 0 || packageStats.numRunningRegular > 0) {
+ // Don't delete the object just yet. We'll remove it in onJobCompleted() when the
+ // jobs officially stop running.
+ Slog.w(TAG,
+ pkgName + "(" + uid + ") marked as removed before jobs stopped running");
+ } else {
+ mActivePkgStats.delete(UserHandle.getUserId(uid), pkgName);
+ }
+ }
+ }
+
void onUserRemoved(int userId) {
mGracePeriodObserver.onUserRemoved(userId);
}
@@ -557,6 +600,7 @@
boolean startingJob = false;
int preemptReasonCode = JobParameters.STOP_REASON_UNDEFINED;
String preemptReason = null;
+ final boolean pkgConcurrencyOkay = !isPkgConcurrencyLimitedLocked(nextPending);
// TODO(141645789): rewrite this to look at empty contexts first so we don't
// unnecessarily preempt
for (int j = 0; j < MAX_JOB_CONTEXTS_COUNT; j++) {
@@ -566,7 +610,7 @@
final boolean preferredUidOkay = (preferredUid == nextPending.getUid())
|| (preferredUid == JobServiceContext.NO_PREFERRED_UID);
- if (preferredUidOkay && workType != WORK_TYPE_NONE) {
+ if (preferredUidOkay && pkgConcurrencyOkay && workType != WORK_TYPE_NONE) {
// This slot is free, and we haven't yet hit the limit on
// concurrent jobs... we can just throw the job in to here.
selectedContextId = j;
@@ -579,9 +623,11 @@
continue;
}
if (job.getUid() != nextPending.getUid()) {
- // Maybe stop the job if it has had its day in the sun.
+ // Maybe stop the job if it has had its day in the sun. Don't let a different
+ // app preempt jobs started for TOP apps though.
final String reason = shouldStopJobReason[j];
- if (reason != null && mWorkCountTracker.canJobStart(allWorkTypes,
+ if (job.lastEvaluatedPriority < JobInfo.PRIORITY_TOP_APP
+ && reason != null && mWorkCountTracker.canJobStart(allWorkTypes,
activeServices.get(j).getRunningJobWorkType()) != WORK_TYPE_NONE) {
// Right now, the way the code is set up, we don't need to explicitly
// assign the new job to this context since we'll reassign when the
@@ -608,23 +654,27 @@
// actually starting a job, so don't set startingJob.
}
}
+ final PackageStats packageStats = getPkgStatsLocked(
+ nextPending.getSourceUserId(), nextPending.getSourcePackageName());
if (selectedContextId != -1) {
contextIdToJobMap[selectedContextId] = nextPending;
slotChanged[selectedContextId] = true;
preemptReasonCodeForContext[selectedContextId] = preemptReasonCode;
preemptReasonForContext[selectedContextId] = preemptReason;
+ packageStats.adjustStagedCount(true, nextPending.shouldTreatAsExpeditedJob());
}
if (startingJob) {
// Increase the counters when we're going to start a job.
workTypeForContext[selectedContextId] = workType;
mWorkCountTracker.stageJob(workType, allWorkTypes);
+ mActivePkgStats.add(
+ nextPending.getSourceUserId(), nextPending.getSourcePackageName(),
+ packageStats);
}
}
if (DEBUG) {
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
- }
- if (DEBUG) {
Slog.d(TAG, "assignJobsToContexts: " + mWorkCountTracker.toString());
}
@@ -660,6 +710,7 @@
}
}
mWorkCountTracker.resetStagingCount();
+ mActivePkgStats.forEach(mPackageStatsStagingCountClearer);
noteConcurrency();
}
@@ -702,18 +753,70 @@
}
@GuardedBy("mLock")
+ @NonNull
+ private PackageStats getPkgStatsLocked(int userId, @NonNull String packageName) {
+ PackageStats packageStats = mActivePkgStats.get(userId, packageName);
+ if (packageStats == null) {
+ packageStats = mPkgStatsPool.acquire();
+ if (packageStats == null) {
+ packageStats = new PackageStats();
+ }
+ packageStats.setPackage(userId, packageName);
+ }
+ return packageStats;
+ }
+
+ @GuardedBy("mLock")
+ private boolean isPkgConcurrencyLimitedLocked(@NonNull JobStatus jobStatus) {
+ if (jobStatus.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
+ // Don't restrict top apps' concurrency. The work type limits will make sure
+ // background jobs have slots to run if the system has resources.
+ return false;
+ }
+ // Use < instead of <= as that gives us a little wiggle room in case a new job comes
+ // along very shortly.
+ if (mService.mPendingJobs.size() + mRunningJobs.size() < mWorkTypeConfig.getMaxTotal()) {
+ // Don't artificially limit a single package if we don't even have enough jobs to use
+ // the maximum number of slots. We'll preempt the job later if we need the slot.
+ return false;
+ }
+ final PackageStats packageStats =
+ mActivePkgStats.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+ if (packageStats == null) {
+ // No currently running jobs.
+ return false;
+ }
+ if (jobStatus.shouldTreatAsExpeditedJob()) {
+ return packageStats.numRunningEj + packageStats.numStagedEj < mPkgConcurrencyLimitEj;
+ } else {
+ return packageStats.numRunningRegular + packageStats.numStagedRegular
+ < mPkgConcurrencyLimitRegular;
+ }
+ }
+
+ @GuardedBy("mLock")
private void startJobLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus,
@WorkType final int workType) {
final List<StateController> controllers = mService.mControllers;
- for (int ic = 0; ic < controllers.size(); ic++) {
+ final int numControllers = controllers.size();
+ for (int ic = 0; ic < numControllers; ic++) {
controllers.get(ic).prepareForExecutionLocked(jobStatus);
}
+ final PackageStats packageStats =
+ getPkgStatsLocked(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+ packageStats.adjustStagedCount(false, jobStatus.shouldTreatAsExpeditedJob());
if (!worker.executeRunnableJob(jobStatus, workType)) {
Slog.e(TAG, "Error executing " + jobStatus);
mWorkCountTracker.onStagedJobFailed(workType);
+ for (int ic = 0; ic < numControllers; ic++) {
+ controllers.get(ic).unprepareFromExecutionLocked(jobStatus);
+ }
} else {
mRunningJobs.add(jobStatus);
mWorkCountTracker.onJobStarted(workType);
+ packageStats.adjustRunningCount(true, jobStatus.shouldTreatAsExpeditedJob());
+ mActivePkgStats.add(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), packageStats);
}
final List<JobStatus> pendingJobs = mService.mPendingJobs;
if (pendingJobs.remove(jobStatus)) {
@@ -726,6 +829,18 @@
@WorkType final int workType) {
mWorkCountTracker.onJobFinished(workType);
mRunningJobs.remove(jobStatus);
+ final PackageStats packageStats =
+ mActivePkgStats.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+ if (packageStats == null) {
+ Slog.wtf(TAG, "Running job didn't have an active PackageStats object");
+ } else {
+ packageStats.adjustRunningCount(false, jobStatus.startedAsExpeditedJob);
+ if (packageStats.numRunningEj <= 0 && packageStats.numRunningRegular <= 0) {
+ mActivePkgStats.delete(packageStats.userId, packageStats.packageName);
+ mPkgStatsPool.release(packageStats);
+ }
+ }
+
final List<JobStatus> pendingJobs = mService.mPendingJobs;
if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) {
updateCounterConfigLocked();
@@ -746,7 +861,7 @@
}
if (worker.getPreferredUid() != nextPending.getUid()) {
- if (backupJob == null) {
+ if (backupJob == null && !isPkgConcurrencyLimitedLocked(nextPending)) {
int allWorkTypes = getJobWorkTypes(nextPending);
int workAsType = mWorkCountTracker.canJobStart(allWorkTypes);
if (workAsType != WORK_TYPE_NONE) {
@@ -758,6 +873,13 @@
continue;
}
+ // Only bypass the concurrent limit if we had preempted the job due to a higher
+ // priority job.
+ if (nextPending.lastEvaluatedPriority <= jobStatus.lastEvaluatedPriority
+ && isPkgConcurrencyLimitedLocked(nextPending)) {
+ continue;
+ }
+
if (highestPriorityJob == null
|| highestPriorityJob.lastEvaluatedPriority
< nextPending.lastEvaluatedPriority) {
@@ -815,6 +937,10 @@
continue;
}
+ if (isPkgConcurrencyLimitedLocked(nextPending)) {
+ continue;
+ }
+
final int allWorkTypes = getJobWorkTypes(nextPending);
final int workAsType = mWorkCountTracker.canJobStart(allWorkTypes);
if (workAsType == WORK_TYPE_NONE) {
@@ -979,8 +1105,16 @@
CONFIG_LIMITS_SCREEN_OFF.moderate.update(properties);
CONFIG_LIMITS_SCREEN_OFF.low.update(properties);
CONFIG_LIMITS_SCREEN_OFF.critical.update(properties);
+
+ // Package concurrency limits must in the range [1, MAX_JOB_CONTEXTS_COUNT].
+ mPkgConcurrencyLimitEj = Math.max(1, Math.min(MAX_JOB_CONTEXTS_COUNT,
+ properties.getInt(KEY_PKG_CONCURRENCY_LIMIT_EJ, DEFAULT_PKG_CONCURRENCY_LIMIT_EJ)));
+ mPkgConcurrencyLimitRegular = Math.max(1, Math.min(MAX_JOB_CONTEXTS_COUNT,
+ properties.getInt(
+ KEY_PKG_CONCURRENCY_LIMIT_REGULAR, DEFAULT_PKG_CONCURRENCY_LIMIT_REGULAR)));
}
+ @GuardedBy("mLock")
public void dumpLocked(IndentingPrintWriter pw, long now, long nowRealtime) {
pw.println("Concurrency:");
@@ -989,6 +1123,8 @@
pw.println("Configuration:");
pw.increaseIndent();
pw.print(KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS, mScreenOffAdjustmentDelayMs).println();
+ pw.print(KEY_PKG_CONCURRENCY_LIMIT_EJ, mPkgConcurrencyLimitEj).println();
+ pw.print(KEY_PKG_CONCURRENCY_LIMIT_REGULAR, mPkgConcurrencyLimitRegular).println();
pw.println();
CONFIG_LIMITS_SCREEN_ON.normal.dump(pw);
pw.println();
@@ -1033,6 +1169,12 @@
pw.println(mLastMemoryTrimLevel);
pw.println();
+ pw.println("Active Package stats:");
+ pw.increaseIndent();
+ mActivePkgStats.forEach(pkgStats -> pkgStats.dumpLocked(pw));
+ pw.decreaseIndent();
+ pw.println();
+
pw.print("User Grace Period: ");
pw.println(mGracePeriodObserver.mGracePeriodExpiration);
pw.println();
@@ -1620,4 +1762,53 @@
return sb.toString();
}
}
+
+ private static class PackageStats {
+ public int userId;
+ public String packageName;
+ public int numRunningEj;
+ public int numRunningRegular;
+ public int numStagedEj;
+ public int numStagedRegular;
+
+ private void setPackage(int userId, @NonNull String packageName) {
+ this.userId = userId;
+ this.packageName = packageName;
+ numRunningEj = numRunningRegular = 0;
+ resetStagedCount();
+ }
+
+ private void resetStagedCount() {
+ numStagedEj = numStagedRegular = 0;
+ }
+
+ private void adjustRunningCount(boolean add, boolean forEj) {
+ if (forEj) {
+ numRunningEj = Math.max(0, numRunningEj + (add ? 1 : -1));
+ } else {
+ numRunningRegular = Math.max(0, numRunningRegular + (add ? 1 : -1));
+ }
+ }
+
+ private void adjustStagedCount(boolean add, boolean forEj) {
+ if (forEj) {
+ numStagedEj = Math.max(0, numStagedEj + (add ? 1 : -1));
+ } else {
+ numStagedRegular = Math.max(0, numStagedRegular + (add ? 1 : -1));
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void dumpLocked(IndentingPrintWriter pw) {
+ pw.print("PackageStats{");
+ pw.print(userId);
+ pw.print("-");
+ pw.print(packageName);
+ pw.print("#runEJ", numRunningEj);
+ pw.print("#runReg", numRunningRegular);
+ pw.print("#stagedEJ", numStagedEj);
+ pw.print("#stagedReg", numStagedRegular);
+ pw.println("}");
+ }
+ }
}
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 d72f565b..1815661 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -816,6 +816,7 @@
mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
}
mDebuggableApps.remove(pkgName);
+ mConcurrencyManager.onAppRemovedLocked(pkgName, pkgUid);
}
} else if (Intent.ACTION_USER_ADDED.equals(action)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
@@ -2233,7 +2234,8 @@
}
/** Returns true if both the calling and source users for the job are started. */
- private boolean areUsersStartedLocked(final JobStatus job) {
+ @GuardedBy("mLock")
+ public boolean areUsersStartedLocked(final JobStatus job) {
boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId());
if (job.getUserId() == job.getSourceUserId()) {
return sourceStarted;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
index 999c53f..12d9c7f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
@@ -34,6 +34,7 @@
import android.util.SparseArrayMap;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.job.JobSchedulerService;
import java.util.Objects;
@@ -58,13 +59,28 @@
return;
}
switch (action) {
+ case Intent.ACTION_PACKAGE_ADDED:
+ if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ // Only do this for app updates since new installs won't have any jobs
+ // scheduled.
+ final Uri uri = intent.getData();
+ final String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
+ if (pkg != null) {
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final int userId = UserHandle.getUserId(pkgUid);
+ updateComponentStateForPackage(userId, pkg);
+ }
+ }
+ break;
case Intent.ACTION_PACKAGE_CHANGED:
final Uri uri = intent.getData();
final String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
final String[] changedComponents = intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
if (pkg != null && changedComponents != null && changedComponents.length > 0) {
- updateComponentStateForPackage(pkg);
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final int userId = UserHandle.getUserId(pkgUid);
+ updateComponentStateForPackage(userId, pkg);
}
break;
case Intent.ACTION_USER_UNLOCKED:
@@ -86,6 +102,7 @@
super(service);
final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
mContext.registerReceiverAsUser(
@@ -99,6 +116,7 @@
}
@Override
+ @GuardedBy("mLock")
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
updateComponentEnabledStateLocked(jobStatus);
}
@@ -108,30 +126,53 @@
boolean forUpdate) {
}
+ @Override
+ @GuardedBy("mLock")
+ public void onAppRemovedLocked(String packageName, int uid) {
+ clearComponentsForPackageLocked(UserHandle.getUserId(uid), packageName);
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ public void onUserRemovedLocked(int userId) {
+ mServiceInfoCache.delete(userId);
+ }
+
@Nullable
- private ServiceInfo getServiceInfo(JobStatus jobStatus) {
+ @GuardedBy("mLock")
+ private ServiceInfo getServiceInfoLocked(JobStatus jobStatus) {
final ComponentName service = jobStatus.getServiceComponent();
final int userId = jobStatus.getUserId();
- ServiceInfo si = mServiceInfoCache.get(userId, service);
- if (si == null) {
- try {
- // createContextAsUser may potentially be expensive
- // TODO: cache user context or improve ContextImpl implementation if this becomes
- // a problem
- si = mContext.createContextAsUser(UserHandle.of(userId), 0)
- .getPackageManager()
- .getServiceInfo(service, PackageManager.MATCH_DIRECT_BOOT_AUTO);
- } catch (NameNotFoundException e) {
- Slog.e(TAG, "Job exists for non-existent package: " + service.getPackageName());
- return null;
- }
- mServiceInfoCache.add(userId, service, si);
+ if (mServiceInfoCache.contains(userId, service)) {
+ // Return whatever is in the cache, even if it's null. When something changes, we
+ // clear the cache.
+ return mServiceInfoCache.get(userId, service);
}
+
+ ServiceInfo si;
+ try {
+ // createContextAsUser may potentially be expensive
+ // TODO: cache user context or improve ContextImpl implementation if this becomes
+ // a problem
+ si = mContext.createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager()
+ .getServiceInfo(service, PackageManager.MATCH_DIRECT_BOOT_AUTO);
+ } catch (NameNotFoundException e) {
+ if (mService.areUsersStartedLocked(jobStatus)) {
+ // User is fully unlocked but PM still says the package doesn't exist.
+ Slog.e(TAG, "Job exists for non-existent package: " + service.getPackageName());
+ }
+ // Write null to the cache so we don't keep querying PM.
+ si = null;
+ }
+ mServiceInfoCache.add(userId, service, si);
+
return si;
}
+ @GuardedBy("mLock")
private boolean updateComponentEnabledStateLocked(JobStatus jobStatus) {
- final ServiceInfo service = getServiceInfo(jobStatus);
+ final ServiceInfo service = getServiceInfoLocked(jobStatus);
if (DEBUG && service == null) {
Slog.v(TAG, jobStatus.toShortString() + " component not present");
@@ -141,20 +182,26 @@
return !Objects.equals(ogService, service);
}
- private void updateComponentStateForPackage(final String pkg) {
- synchronized (mLock) {
- for (int u = mServiceInfoCache.numMaps() - 1; u >= 0; --u) {
- final int userId = mServiceInfoCache.keyAt(u);
-
- for (int c = mServiceInfoCache.numElementsForKey(userId) - 1; c >= 0; --c) {
- final ComponentName cn = mServiceInfoCache.keyAt(u, c);
- if (cn.getPackageName().equals(pkg)) {
- mServiceInfoCache.delete(userId, cn);
- }
- }
+ @GuardedBy("mLock")
+ private void clearComponentsForPackageLocked(final int userId, final String pkg) {
+ final int uIdx = mServiceInfoCache.indexOfKey(userId);
+ for (int c = mServiceInfoCache.numElementsForKey(userId) - 1; c >= 0; --c) {
+ final ComponentName cn = mServiceInfoCache.keyAt(uIdx, c);
+ if (cn.getPackageName().equals(pkg)) {
+ mServiceInfoCache.delete(userId, cn);
}
- updateComponentStatesLocked(
- jobStatus -> jobStatus.getServiceComponent().getPackageName().equals(pkg));
+ }
+ }
+
+ private void updateComponentStateForPackage(final int userId, final String pkg) {
+ synchronized (mLock) {
+ clearComponentsForPackageLocked(userId, pkg);
+ updateComponentStatesLocked(jobStatus -> {
+ // Using user ID instead of source user ID because the service will run under the
+ // user ID, not source user ID.
+ return jobStatus.getUserId() == userId
+ && jobStatus.getServiceComponent().getPackageName().equals(pkg);
+ });
}
}
@@ -169,6 +216,7 @@
}
}
+ @GuardedBy("mLock")
private void updateComponentStatesLocked(@NonNull Predicate<JobStatus> filter) {
mComponentStateUpdateFunctor.reset();
mService.getJobStore().forEachJob(filter, mComponentStateUpdateFunctor);
@@ -178,24 +226,40 @@
}
final class ComponentStateUpdateFunctor implements Consumer<JobStatus> {
+ @GuardedBy("mLock")
boolean mChanged;
@Override
+ @GuardedBy("mLock")
public void accept(JobStatus jobStatus) {
mChanged |= updateComponentEnabledStateLocked(jobStatus);
}
+ @GuardedBy("mLock")
private void reset() {
mChanged = false;
}
}
@Override
+ @GuardedBy("mLock")
public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
-
+ for (int u = 0; u < mServiceInfoCache.numMaps(); ++u) {
+ final int userId = mServiceInfoCache.keyAt(u);
+ for (int p = 0; p < mServiceInfoCache.numElementsForKey(userId); ++p) {
+ final ComponentName componentName = mServiceInfoCache.keyAt(u, p);
+ pw.print(userId);
+ pw.print("-");
+ pw.print(componentName);
+ pw.print(": ");
+ pw.print(mServiceInfoCache.valueAt(u, p));
+ pw.println();
+ }
+ }
}
@Override
+ @GuardedBy("mLock")
public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
Predicate<JobStatus> predicate) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 11a8b3b..2bdf656 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -77,12 +77,13 @@
|| Log.isLoggable(TAG, Log.DEBUG);
// The networking stack has a hard limit so we can't make this configurable.
- private static final int MAX_NETWORK_CALLBACKS = 50;
+ private static final int MAX_NETWORK_CALLBACKS = 125;
/**
* Minimum amount of time that should have elapsed before we'll update a {@link UidStats}
* instance.
*/
private static final long MIN_STATS_UPDATE_INTERVAL_MS = 30_000L;
+ private static final long MIN_ADJUST_CALLBACK_INTERVAL_MS = 1_000L;
private static final int UNBYPASSABLE_BG_BLOCKED_REASONS =
~ConnectivityManager.BLOCKED_REASON_NONE;
@@ -147,7 +148,8 @@
// 9. Enqueue time
// TODO: maybe consider number of jobs
// TODO: consider IMPORTANT_WHILE_FOREGROUND bit
- final int runningPriority = prioritizeExistenceOver(0, us1.numRunning, us2.numRunning);
+ final int runningPriority = prioritizeExistenceOver(0,
+ us1.runningJobs.size(), us2.runningJobs.size());
if (runningPriority != 0) {
return runningPriority;
}
@@ -210,6 +212,7 @@
* is only done in {@link #maybeAdjustRegisteredCallbacksLocked()} and may sometimes be stale.
*/
private final List<UidStats> mSortedStats = new ArrayList<>();
+ private long mLastCallbackAdjustmentTimeElapsed;
private static final int MSG_ADJUST_CALLBACKS = 0;
@@ -254,7 +257,18 @@
if (jobStatus.hasConnectivityConstraint()) {
final UidStats uidStats =
getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
- uidStats.numRunning++;
+ uidStats.runningJobs.add(jobStatus);
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Override
+ public void unprepareFromExecutionLocked(JobStatus jobStatus) {
+ if (jobStatus.hasConnectivityConstraint()) {
+ final UidStats uidStats =
+ getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
+ uidStats.runningJobs.remove(jobStatus);
+ postAdjustCallbacks();
}
}
@@ -270,12 +284,7 @@
final UidStats uidStats =
getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
uidStats.numReadyWithConnectivity--;
- if (jobStatus.madeActive != 0) {
- // numRunning would be 0 if the UidStats object didn't exist before this method
- // was called. getUidStats() handles logging, so just make sure we don't save a
- // negative value.
- uidStats.numRunning = Math.max(0, uidStats.numRunning - 1);
- }
+ uidStats.runningJobs.remove(jobStatus);
maybeRevokeStandbyExceptionLocked(jobStatus);
postAdjustCallbacks();
}
@@ -693,7 +702,11 @@
}
private void postAdjustCallbacks() {
- mHandler.obtainMessage(MSG_ADJUST_CALLBACKS).sendToTarget();
+ postAdjustCallbacks(0);
+ }
+
+ private void postAdjustCallbacks(long delayMs) {
+ mHandler.sendEmptyMessageDelayed(MSG_ADJUST_CALLBACKS, delayMs);
}
@GuardedBy("mLock")
@@ -708,6 +721,12 @@
}
final long nowElapsed = sElapsedRealtimeClock.millis();
+ if (nowElapsed - mLastCallbackAdjustmentTimeElapsed < MIN_ADJUST_CALLBACK_INTERVAL_MS) {
+ postAdjustCallbacks(MIN_ADJUST_CALLBACK_INTERVAL_MS);
+ return;
+ }
+
+ mLastCallbackAdjustmentTimeElapsed = nowElapsed;
mSortedStats.clear();
for (int u = 0; u < mUidStats.size(); ++u) {
@@ -926,7 +945,10 @@
UidDefaultNetworkCallback defaultNetworkCallback =
mCurrentDefaultNetworkCallbacks.get(jobs.valueAt(0).getSourceUid());
if (defaultNetworkCallback == null) {
- maybeRegisterDefaultNetworkCallbackLocked(jobs.valueAt(0));
+ // This method is only called via a network callback object. That means something
+ // changed about a general network characteristic (since we wouldn't be in this
+ // situation if called from a UID_specific callback). The general network callback
+ // will handle adjusting the per-UID callbacks, so nothing left to do here.
return false;
}
@@ -1106,8 +1128,13 @@
synchronized (mLock) {
if (Objects.equals(mDefaultNetwork, network)) {
mDefaultNetwork = null;
+ updateTrackedJobsLocked(mUid, network);
+ // Add a delay in case onAvailable()+onBlockedStatusChanged is called for a
+ // new network. If this onLost was called because the network is completely
+ // gone, the delay will hel make sure we don't have a short burst of adjusting
+ // callback calls.
+ postAdjustCallbacks(1000);
}
- updateTrackedJobsLocked(mUid, network);
}
}
@@ -1131,7 +1158,7 @@
private static class UidStats {
public final int uid;
public int basePriority;
- public int numRunning;
+ public final ArraySet<JobStatus> runningJobs = new ArraySet<>();
public int numReadyWithConnectivity;
public int numRequestedNetworkAvailable;
public int numEJs;
@@ -1148,7 +1175,7 @@
pw.print("UidStats{");
pw.print("uid", uid);
pw.print("pri", basePriority);
- pw.print("#run", numRunning);
+ pw.print("#run", runningJobs.size());
pw.print("#readyWithConn", numReadyWithConnectivity);
pw.print("#netAvail", numRequestedNetworkAvailable);
pw.print("#EJs", numEJs);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
index 8b0da34..e64233f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
@@ -136,6 +136,29 @@
}
@Override
+ public void unprepareFromExecutionLocked(JobStatus taskStatus) {
+ if (taskStatus.hasContentTriggerConstraint()) {
+ if (taskStatus.contentObserverJobInstance != null) {
+ if (taskStatus.contentObserverJobInstance.mChangedUris == null) {
+ taskStatus.contentObserverJobInstance.mChangedUris = taskStatus.changedUris;
+ } else {
+ taskStatus.contentObserverJobInstance.mChangedUris
+ .addAll(taskStatus.changedUris);
+ }
+ if (taskStatus.contentObserverJobInstance.mChangedAuthorities == null) {
+ taskStatus.contentObserverJobInstance.mChangedAuthorities =
+ taskStatus.changedAuthorities;
+ } else {
+ taskStatus.contentObserverJobInstance.mChangedAuthorities
+ .addAll(taskStatus.changedAuthorities);
+ }
+ taskStatus.changedUris = null;
+ taskStatus.changedAuthorities = null;
+ }
+ }
+ }
+
+ @Override
public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
boolean forUpdate) {
if (taskStatus.clearTrackingController(JobStatus.TRACKING_CONTENT)) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index d4ce437..aace645 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -34,7 +34,6 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlarmManager;
-import android.app.AppGlobals;
import android.app.IUidObserver;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
@@ -677,27 +676,30 @@
}
@Override
- public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
- boolean forUpdate) {
- if (jobStatus.clearTrackingController(JobStatus.TRACKING_QUOTA)) {
- Timer timer = mPkgTimers.get(jobStatus.getSourceUserId(),
- jobStatus.getSourcePackageName());
+ public void unprepareFromExecutionLocked(JobStatus jobStatus) {
+ Timer timer = mPkgTimers.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+ if (timer != null) {
+ timer.stopTrackingJob(jobStatus);
+ }
+ if (jobStatus.isRequestedExpeditedJob()) {
+ timer = mEJPkgTimers.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
if (timer != null) {
timer.stopTrackingJob(jobStatus);
}
- if (jobStatus.isRequestedExpeditedJob()) {
- timer = mEJPkgTimers.get(jobStatus.getSourceUserId(),
- jobStatus.getSourcePackageName());
- if (timer != null) {
- timer.stopTrackingJob(jobStatus);
- }
- }
+ }
+ mTopStartedJobs.remove(jobStatus);
+ }
+
+ @Override
+ public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
+ boolean forUpdate) {
+ if (jobStatus.clearTrackingController(JobStatus.TRACKING_QUOTA)) {
+ unprepareFromExecutionLocked(jobStatus);
ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(),
jobStatus.getSourcePackageName());
if (jobs != null) {
jobs.remove(jobStatus);
}
- mTopStartedJobs.remove(jobStatus);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
index 334876f..f0fc3b0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
@@ -76,6 +76,12 @@
}
/**
+ * Optionally implement logic here for when a job that was about to be executed failed to start.
+ */
+ public void unprepareFromExecutionLocked(JobStatus jobStatus) {
+ }
+
+ /**
* Remove task - this will happen if the task is cancelled, completed, etc.
*/
public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
diff --git a/apex/jobscheduler/service/jni/Android.bp b/apex/jobscheduler/service/jni/Android.bp
index c630217..34a1fa2 100644
--- a/apex/jobscheduler/service/jni/Android.bp
+++ b/apex/jobscheduler/service/jni/Android.bp
@@ -24,30 +24,8 @@
],
shared_libs: [
- "libnativehelper",
- "liblog",
- "libbase",
- ],
-
- product_variables: {
- arc: {
- exclude_srcs: [
- "com_android_server_alarm_AlarmManagerService.cpp",
- ],
- srcs: [
- ":arctimersrcs",
- ],
- }
- },
-}
-
-filegroup {
- name: "lib_alarmManagerService_native",
- srcs: [
- "com_android_server_alarm_AlarmManagerService.cpp",
- ],
- visibility: [
- // TODO: remove this
- "//vendor:__subpackages__",
+ "libnativehelper",
+ "liblog",
+ "libbase",
],
}
diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt
index bebf019..b7d7ed8 100644
--- a/apex/media/framework/api/current.txt
+++ b/apex/media/framework/api/current.txt
@@ -10,7 +10,6 @@
method @NonNull public java.util.List<java.lang.String> getUnsupportedVideoMimeTypes();
method public boolean isFormatSpecified(@NonNull String);
method public boolean isHdrTypeSupported(@NonNull String);
- method public boolean isSlowMotionSupported();
method public boolean isVideoMimeTypeSupported(@NonNull String);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.ApplicationMediaCapabilities> CREATOR;
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
index 3f30d3e..97fa0ec 100644
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
@@ -242,7 +242,7 @@
}
};
- /*
+ /**
* Query the video codec mime types supported by the application.
* @return List of supported video codec mime types. The list will be empty if there are none.
*/
@@ -251,7 +251,7 @@
return new ArrayList<>(mSupportedVideoMimeTypes);
}
- /*
+ /**
* Query the video codec mime types that are not supported by the application.
* @return List of unsupported video codec mime types. The list will be empty if there are none.
*/
@@ -260,7 +260,7 @@
return new ArrayList<>(mUnsupportedVideoMimeTypes);
}
- /*
+ /**
* Query all hdr types that are supported by the application.
* @return List of supported hdr types. The list will be empty if there are none.
*/
@@ -269,7 +269,7 @@
return new ArrayList<>(mSupportedHdrTypes);
}
- /*
+ /**
* Query all hdr types that are not supported by the application.
* @return List of unsupported hdr types. The list will be empty if there are none.
*/
@@ -278,7 +278,7 @@
return new ArrayList<>(mUnsupportedHdrTypes);
}
- /*
+ /**
* Whether handling of slow-motion video is supported
* @hide
*/
diff --git a/core/api/current.txt b/core/api/current.txt
index 4b12d54..6889855 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -592,6 +592,7 @@
field public static final int editTextStyle = 16842862; // 0x101006e
field @Deprecated public static final int editable = 16843115; // 0x101016b
field public static final int editorExtras = 16843300; // 0x1010224
+ field public static final int effectColor;
field public static final int elegantTextHeight = 16843869; // 0x101045d
field public static final int elevation = 16843840; // 0x1010440
field public static final int ellipsize = 16842923; // 0x10100ab
@@ -8604,37 +8605,37 @@
public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile {
method public void finalize();
- method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method public int getConnectionState(android.bluetooth.BluetoothDevice);
- method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
- method public boolean isA2dpPlaying(android.bluetooth.BluetoothDevice);
- field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
- field public static final String ACTION_PLAYING_STATE_CHANGED = "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isA2dpPlaying(android.bluetooth.BluetoothDevice);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_PLAYING_STATE_CHANGED = "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
field public static final int STATE_NOT_PLAYING = 11; // 0xb
field public static final int STATE_PLAYING = 10; // 0xa
}
public final class BluetoothAdapter {
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelDiscovery();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean cancelDiscovery();
method public static boolean checkBluetoothAddress(String);
method public void closeProfileProxy(int, android.bluetooth.BluetoothProfile);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disable();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enable();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public String getAddress();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disable();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enable();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, "android.permission.LOCAL_MAC_ADDRESS"}) public String getAddress();
method public android.bluetooth.le.BluetoothLeAdvertiser getBluetoothLeAdvertiser();
method public android.bluetooth.le.BluetoothLeScanner getBluetoothLeScanner();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
method public static android.bluetooth.BluetoothAdapter getDefaultAdapter();
method public int getLeMaximumAdvertisingDataLength();
- method public String getName();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getProfileConnectionState(int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getProfileConnectionState(int);
method public boolean getProfileProxy(android.content.Context, android.bluetooth.BluetoothProfile.ServiceListener, int);
method public android.bluetooth.BluetoothDevice getRemoteDevice(String);
method public android.bluetooth.BluetoothDevice getRemoteDevice(byte[]);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getScanMode();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getState();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isDiscovering();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEnabled();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public int getScanMode();
+ method public int getState();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean isDiscovering();
+ method public boolean isEnabled();
method public boolean isLe2MPhySupported();
method public boolean isLeCodedPhySupported();
method public boolean isLeExtendedAdvertisingSupported();
@@ -8642,22 +8643,22 @@
method public boolean isMultipleAdvertisementSupported();
method public boolean isOffloadedFilteringSupported();
method public boolean isOffloadedScanBatchingSupported();
- method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothServerSocket listenUsingInsecureL2capChannel() throws java.io.IOException;
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String, java.util.UUID) throws java.io.IOException;
- method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothServerSocket listenUsingL2capChannel() throws java.io.IOException;
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(String, java.util.UUID) throws java.io.IOException;
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setName(String);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean startDiscovery();
- method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean startLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback);
- method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean startLeScan(java.util.UUID[], android.bluetooth.BluetoothAdapter.LeScanCallback);
- method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public void stopLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback);
- field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
- field public static final String ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
- field public static final String ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
- field public static final String ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
- field public static final String ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
- field public static final String ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE";
- field public static final String ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingInsecureL2capChannel() throws java.io.IOException;
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String, java.util.UUID) throws java.io.IOException;
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingL2capChannel() throws java.io.IOException;
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(String, java.util.UUID) throws java.io.IOException;
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setName(String);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startDiscovery();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startLeScan(java.util.UUID[], android.bluetooth.BluetoothAdapter.LeScanCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void stopLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public static final String ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public static final String ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public static final String ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public static final String ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
field public static final String ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED";
field public static final int ERROR = -2147483648; // 0x80000000
field public static final String EXTRA_CONNECTION_STATE = "android.bluetooth.adapter.extra.CONNECTION_STATE";
@@ -9007,38 +9008,38 @@
}
public final class BluetoothDevice implements android.os.Parcelable {
- method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
- method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int);
- method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int);
- method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int, android.os.Handler);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean createBond();
- method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothSocket createInsecureL2capChannel(int) throws java.io.IOException;
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
- method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothSocket createL2capChannel(int) throws java.io.IOException;
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int, int, android.os.Handler);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean createBond();
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothSocket createInsecureL2capChannel(int) throws java.io.IOException;
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothSocket createL2capChannel(int) throws java.io.IOException;
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
method public int describeContents();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean fetchUuidsWithSdp();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean fetchUuidsWithSdp();
method public String getAddress();
- method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public String getAlias();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothClass getBluetoothClass();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getBondState();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public String getName();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getType();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.os.ParcelUuid[] getUuids();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String);
+ method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getAlias();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothClass getBluetoothClass();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getBondState();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getType();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.os.ParcelUuid[] getUuids();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setAlias(@NonNull String);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPairingConfirmation(boolean);
- method public boolean setPin(byte[]);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setPin(byte[]);
method public void writeToParcel(android.os.Parcel, int);
- field public static final String ACTION_ACL_CONNECTED = "android.bluetooth.device.action.ACL_CONNECTED";
- field public static final String ACTION_ACL_DISCONNECTED = "android.bluetooth.device.action.ACL_DISCONNECTED";
- field public static final String ACTION_ACL_DISCONNECT_REQUESTED = "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED";
- field public static final String ACTION_ALIAS_CHANGED = "android.bluetooth.device.action.ALIAS_CHANGED";
- field public static final String ACTION_BOND_STATE_CHANGED = "android.bluetooth.device.action.BOND_STATE_CHANGED";
- field public static final String ACTION_CLASS_CHANGED = "android.bluetooth.device.action.CLASS_CHANGED";
- field public static final String ACTION_FOUND = "android.bluetooth.device.action.FOUND";
- field public static final String ACTION_NAME_CHANGED = "android.bluetooth.device.action.NAME_CHANGED";
- field public static final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST";
- field public static final String ACTION_UUID = "android.bluetooth.device.action.UUID";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_ACL_CONNECTED = "android.bluetooth.device.action.ACL_CONNECTED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_ACL_DISCONNECTED = "android.bluetooth.device.action.ACL_DISCONNECTED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_ACL_DISCONNECT_REQUESTED = "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_ALIAS_CHANGED = "android.bluetooth.device.action.ALIAS_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_BOND_STATE_CHANGED = "android.bluetooth.device.action.BOND_STATE_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CLASS_CHANGED = "android.bluetooth.device.action.CLASS_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public static final String ACTION_FOUND = "android.bluetooth.device.action.FOUND";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_NAME_CHANGED = "android.bluetooth.device.action.NAME_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public static final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public static final String ACTION_UUID = "android.bluetooth.device.action.UUID";
field public static final int ADDRESS_TYPE_PUBLIC = 0; // 0x0
field public static final int ADDRESS_TYPE_RANDOM = 1; // 0x1
field public static final int BOND_BONDED = 12; // 0xc
@@ -9076,30 +9077,30 @@
}
public final class BluetoothGatt implements android.bluetooth.BluetoothProfile {
- method public void abortReliableWrite();
- method @Deprecated public void abortReliableWrite(android.bluetooth.BluetoothDevice);
- method public boolean beginReliableWrite();
- method public void close();
- method public boolean connect();
- method public void disconnect();
- method public boolean discoverServices();
- method public boolean executeReliableWrite();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void abortReliableWrite();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void abortReliableWrite(android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean beginReliableWrite();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void close();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void disconnect();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean discoverServices();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean executeReliableWrite();
method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method public int getConnectionState(android.bluetooth.BluetoothDevice);
method public android.bluetooth.BluetoothDevice getDevice();
method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
- method public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
- method public boolean readDescriptor(android.bluetooth.BluetoothGattDescriptor);
- method public void readPhy();
- method public boolean readRemoteRssi();
- method public boolean requestConnectionPriority(int);
- method public boolean requestMtu(int);
- method public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
- method public void setPreferredPhy(int, int, int);
- method public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
- method public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean readDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void readPhy();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean readRemoteRssi();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean requestConnectionPriority(int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean requestMtu(int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setPreferredPhy(int, int, int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
field public static final int CONNECTION_PRIORITY_BALANCED = 0; // 0x0
field public static final int CONNECTION_PRIORITY_HIGH = 1; // 0x1
field public static final int CONNECTION_PRIORITY_LOW_POWER = 2; // 0x2
@@ -9209,21 +9210,21 @@
}
public final class BluetoothGattServer implements android.bluetooth.BluetoothProfile {
- method public boolean addService(android.bluetooth.BluetoothGattService);
- method public void cancelConnection(android.bluetooth.BluetoothDevice);
- method public void clearServices();
- method public void close();
- method public boolean connect(android.bluetooth.BluetoothDevice, boolean);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean addService(android.bluetooth.BluetoothGattService);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void cancelConnection(android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void clearServices();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void close();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect(android.bluetooth.BluetoothDevice, boolean);
method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method public int getConnectionState(android.bluetooth.BluetoothDevice);
method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
- method public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean);
- method public void readPhy(android.bluetooth.BluetoothDevice);
- method public boolean removeService(android.bluetooth.BluetoothGattService);
- method public boolean sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]);
- method public void setPreferredPhy(android.bluetooth.BluetoothDevice, int, int, int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void readPhy(android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeService(android.bluetooth.BluetoothGattService);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setPreferredPhy(android.bluetooth.BluetoothDevice, int, int, int);
}
public abstract class BluetoothGattServerCallback {
@@ -9261,18 +9262,18 @@
}
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
- method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method public int getConnectionState(android.bluetooth.BluetoothDevice);
- method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
- method public boolean isAudioConnected(android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isNoiseReductionSupported(@NonNull android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isVoiceRecognitionSupported(@NonNull android.bluetooth.BluetoothDevice);
- method public boolean sendVendorSpecificResultCode(android.bluetooth.BluetoothDevice, String, String);
- method public boolean startVoiceRecognition(android.bluetooth.BluetoothDevice);
- method public boolean stopVoiceRecognition(android.bluetooth.BluetoothDevice);
- field public static final String ACTION_AUDIO_STATE_CHANGED = "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
- field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
- field public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT = "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isAudioConnected(android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isNoiseReductionSupported(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isVoiceRecognitionSupported(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendVendorSpecificResultCode(android.bluetooth.BluetoothDevice, String, String);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean startVoiceRecognition(android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean stopVoiceRecognition(android.bluetooth.BluetoothDevice);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_AUDIO_STATE_CHANGED = "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT = "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
field public static final int AT_CMD_TYPE_ACTION = 4; // 0x4
field public static final int AT_CMD_TYPE_BASIC = 3; // 0x3
field public static final int AT_CMD_TYPE_READ = 0; // 0x0
@@ -9289,14 +9290,14 @@
}
@Deprecated public final class BluetoothHealth implements android.bluetooth.BluetoothProfile {
- method @Deprecated public boolean connectChannelToSource(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
- method @Deprecated public boolean disconnectChannel(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration, int);
- method @Deprecated public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method @Deprecated public int getConnectionState(android.bluetooth.BluetoothDevice);
- method @Deprecated public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
- method @Deprecated public android.os.ParcelFileDescriptor getMainChannelFd(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
- method @Deprecated public boolean registerSinkAppConfiguration(String, int, android.bluetooth.BluetoothHealthCallback);
- method @Deprecated public boolean unregisterAppConfiguration(android.bluetooth.BluetoothHealthAppConfiguration);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connectChannelToSource(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnectChannel(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration, int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(android.bluetooth.BluetoothDevice);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.os.ParcelFileDescriptor getMainChannelFd(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean registerSinkAppConfiguration(String, int, android.bluetooth.BluetoothHealthCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean unregisterAppConfiguration(android.bluetooth.BluetoothHealthAppConfiguration);
field @Deprecated public static final int APP_CONFIG_REGISTRATION_FAILURE = 1; // 0x1
field @Deprecated public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0; // 0x0
field @Deprecated public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; // 0x3
@@ -9327,24 +9328,24 @@
}
public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile {
- method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
- method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
- field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
}
public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile {
- method public boolean connect(android.bluetooth.BluetoothDevice);
- method public boolean disconnect(android.bluetooth.BluetoothDevice);
- method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method public int getConnectionState(android.bluetooth.BluetoothDevice);
- method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
- method public boolean registerApp(android.bluetooth.BluetoothHidDeviceAppSdpSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, java.util.concurrent.Executor, android.bluetooth.BluetoothHidDevice.Callback);
- method public boolean replyReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]);
- method public boolean reportError(android.bluetooth.BluetoothDevice, byte);
- method public boolean sendReport(android.bluetooth.BluetoothDevice, int, byte[]);
- method public boolean unregisterApp();
- field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED";
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect(android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean registerApp(android.bluetooth.BluetoothHidDeviceAppSdpSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, java.util.concurrent.Executor, android.bluetooth.BluetoothHidDevice.Callback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean replyReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean reportError(android.bluetooth.BluetoothDevice, byte);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendReport(android.bluetooth.BluetoothDevice, int, byte[]);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean unregisterApp();
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED";
field public static final byte ERROR_RSP_INVALID_PARAM = 4; // 0x4
field public static final byte ERROR_RSP_INVALID_RPT_ID = 2; // 0x2
field public static final byte ERROR_RSP_NOT_READY = 1; // 0x1
@@ -9410,26 +9411,26 @@
}
public final class BluetoothLeAudio implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void close();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) protected void finalize();
- method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
- method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
- field public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED";
+ method public void close();
+ method protected void finalize();
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED";
}
public final class BluetoothManager {
method public android.bluetooth.BluetoothAdapter getAdapter();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionState(android.bluetooth.BluetoothDevice, int);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int, int[]);
- method public android.bluetooth.BluetoothGattServer openGattServer(android.content.Context, android.bluetooth.BluetoothGattServerCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int, int[]);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothGattServer openGattServer(android.content.Context, android.bluetooth.BluetoothGattServerCallback);
}
public interface BluetoothProfile {
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionState(android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method public int getConnectionState(android.bluetooth.BluetoothDevice);
+ method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
field public static final int A2DP = 2; // 0x2
field public static final String EXTRA_PREVIOUS_STATE = "android.bluetooth.profile.extra.PREVIOUS_STATE";
field public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
@@ -9460,7 +9461,7 @@
public final class BluetoothSocket implements java.io.Closeable {
method public void close() throws java.io.IOException;
- method public void connect() throws java.io.IOException;
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void connect() throws java.io.IOException;
method public int getConnectionType();
method public java.io.InputStream getInputStream() throws java.io.IOException;
method public int getMaxReceivePacketSize();
@@ -9538,13 +9539,13 @@
}
public final class AdvertisingSet {
- method public void enableAdvertising(boolean, int, int);
- method public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
- method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
- method public void setPeriodicAdvertisingData(android.bluetooth.le.AdvertiseData);
- method public void setPeriodicAdvertisingEnabled(boolean);
- method public void setPeriodicAdvertisingParameters(android.bluetooth.le.PeriodicAdvertisingParameters);
- method public void setScanResponseData(android.bluetooth.le.AdvertiseData);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void enableAdvertising(boolean, int, int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setPeriodicAdvertisingParameters(android.bluetooth.le.PeriodicAdvertisingParameters);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void setScanResponseData(android.bluetooth.le.AdvertiseData);
}
public abstract class AdvertisingSetCallback {
@@ -9607,23 +9608,23 @@
}
public final class BluetoothLeAdvertiser {
- method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
- method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
- method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
- method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
- method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, int, android.bluetooth.le.AdvertisingSetCallback);
- method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, int, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
- method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
- method public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, int, android.bluetooth.le.AdvertisingSetCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, int, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE) public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
}
public final class BluetoothLeScanner {
- method public void flushPendingScanResults(android.bluetooth.le.ScanCallback);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public void startScan(android.bluetooth.le.ScanCallback);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public void startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public int startScan(@Nullable java.util.List<android.bluetooth.le.ScanFilter>, @Nullable android.bluetooth.le.ScanSettings, @NonNull android.app.PendingIntent);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public void stopScan(android.bluetooth.le.ScanCallback);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public void stopScan(android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void flushPendingScanResults(android.bluetooth.le.ScanCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startScan(android.bluetooth.le.ScanCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public int startScan(@Nullable java.util.List<android.bluetooth.le.ScanFilter>, @Nullable android.bluetooth.le.ScanSettings, @NonNull android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void stopScan(android.bluetooth.le.ScanCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void stopScan(android.app.PendingIntent);
field public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
field public static final String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE";
field public static final String EXTRA_LIST_SCAN_RESULT = "android.bluetooth.le.extra.LIST_SCAN_RESULT";
@@ -12086,13 +12087,6 @@
field public static final String ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED = "android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED";
}
- public class DataLoaderParams {
- method @NonNull public static final android.content.pm.DataLoaderParams forStreaming(@NonNull android.content.ComponentName, @NonNull String);
- method @NonNull public final String getArguments();
- method @NonNull public final android.content.ComponentName getComponentName();
- method @NonNull public final int getType();
- }
-
public final class FeatureGroupInfo implements android.os.Parcelable {
ctor public FeatureGroupInfo();
ctor public FeatureGroupInfo(android.content.pm.FeatureGroupInfo);
@@ -12127,14 +12121,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.InstallSourceInfo> CREATOR;
}
- public final class InstallationFile {
- method public long getLengthBytes();
- method public int getLocation();
- method @Nullable public byte[] getMetadata();
- method @NonNull public String getName();
- method @Nullable public byte[] getSignature();
- }
-
public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
ctor public InstrumentationInfo();
ctor public InstrumentationInfo(android.content.pm.InstrumentationInfo);
@@ -12331,9 +12317,6 @@
field public static final String ACTION_SESSION_COMMITTED = "android.content.pm.action.SESSION_COMMITTED";
field public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
field public static final String ACTION_SESSION_UPDATED = "android.content.pm.action.SESSION_UPDATED";
- field public static final int DATA_LOADER_TYPE_NONE = 0; // 0x0
- field public static final int DATA_LOADER_TYPE_STREAMING = 1; // 0x1
- field public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
field public static final String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME";
field public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
field public static final String EXTRA_SESSION = "android.content.pm.extra.SESSION";
@@ -12341,9 +12324,6 @@
field public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
field public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
field public static final String EXTRA_STORAGE_PATH = "android.content.pm.extra.STORAGE_PATH";
- field public static final int LOCATION_DATA_APP = 0; // 0x0
- field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
- field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
field public static final int STATUS_FAILURE = 1; // 0x1
field public static final int STATUS_FAILURE_ABORTED = 3; // 0x3
field public static final int STATUS_FAILURE_BLOCKED = 2; // 0x2
@@ -12351,7 +12331,6 @@
field public static final int STATUS_FAILURE_INCOMPATIBLE = 7; // 0x7
field public static final int STATUS_FAILURE_INVALID = 4; // 0x4
field public static final int STATUS_FAILURE_STORAGE = 6; // 0x6
- field public static final int STATUS_PENDING_STREAMING = -2; // 0xfffffffe
field public static final int STATUS_PENDING_USER_ACTION = -1; // 0xffffffff
field public static final int STATUS_SUCCESS = 0; // 0x0
}
@@ -12359,12 +12338,10 @@
public static class PackageInstaller.Session implements java.io.Closeable {
method public void abandon();
method public void addChildSessionId(int);
- method public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]);
method public void close();
method public void commit(@NonNull android.content.IntentSender);
method public void fsync(@NonNull java.io.OutputStream) throws java.io.IOException;
method @NonNull public int[] getChildSessionIds();
- method @Nullable public android.content.pm.DataLoaderParams getDataLoaderParams();
method @NonNull public String[] getNames() throws java.io.IOException;
method public int getParentSessionId();
method public boolean isMultiPackage();
@@ -12372,7 +12349,6 @@
method @NonNull public java.io.InputStream openRead(@NonNull String) throws java.io.IOException;
method @NonNull public java.io.OutputStream openWrite(@NonNull String, long, long) throws java.io.IOException;
method public void removeChildSessionId(int);
- method public void removeFile(int, @NonNull String);
method public void removeSplit(@NonNull String) throws java.io.IOException;
method @Deprecated public void setChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>, @Nullable byte[]) throws java.io.IOException;
method public void setStagingProgress(float);
@@ -12443,7 +12419,6 @@
method public void setAppLabel(@Nullable CharSequence);
method public void setAppPackageName(@Nullable String);
method @Deprecated public void setAutoRevokePermissionsMode(boolean);
- method public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams);
method public void setInstallLocation(int);
method public void setInstallReason(int);
method public void setInstallScenario(int);
@@ -16817,8 +16792,10 @@
public class RippleDrawable extends android.graphics.drawable.LayerDrawable {
ctor public RippleDrawable(@NonNull android.content.res.ColorStateList, @Nullable android.graphics.drawable.Drawable, @Nullable android.graphics.drawable.Drawable);
+ method @NonNull public android.content.res.ColorStateList getEffectColor();
method public int getRadius();
- method public void setColor(android.content.res.ColorStateList);
+ method public void setColor(@NonNull android.content.res.ColorStateList);
+ method public void setEffectColor(@NonNull android.content.res.ColorStateList);
method public void setRadius(int);
field public static final int RADIUS_AUTO = -1; // 0xffffffff
}
@@ -18824,6 +18801,7 @@
package android.hardware.display {
public final class DeviceProductInfo implements android.os.Parcelable {
+ ctor public DeviceProductInfo(@Nullable String, @NonNull String, @NonNull String, @IntRange(from=1990) int, int);
method public int describeContents();
method public int getConnectionToSinkType();
method @IntRange(from=0xffffffff, to=53) public int getManufactureWeek();
@@ -20379,7 +20357,7 @@
method @NonNull public java.util.List<android.media.AudioDeviceInfo> getAvailableCommunicationDevices();
method @Nullable public android.media.AudioDeviceInfo getCommunicationDevice();
method public android.media.AudioDeviceInfo[] getDevices(int);
- method @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public int getEncodedSurroundMode();
+ method public int getEncodedSurroundMode();
method public java.util.List<android.media.MicrophoneInfo> getMicrophones() throws java.io.IOException;
method public int getMode();
method public String getParameters(String);
@@ -20402,7 +20380,7 @@
method public static boolean isOffloadedPlaybackSupported(@NonNull android.media.AudioFormat, @NonNull android.media.AudioAttributes);
method public boolean isSpeakerphoneOn();
method public boolean isStreamMute(int);
- method @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public boolean isSurroundFormatEnabled(int);
+ method public boolean isSurroundFormatEnabled(int);
method public boolean isVolumeFixed();
method @Deprecated public boolean isWiredHeadsetOn();
method public void loadSoundEffects();
@@ -24636,7 +24614,6 @@
package android.media.metrics {
public abstract class Event {
- ctor protected Event(long);
method @NonNull public android.os.Bundle getMetricsBundle();
method @IntRange(from=0xffffffff) public long getTimeSinceCreatedMillis();
}
@@ -24646,7 +24623,7 @@
field @NonNull public static final android.media.metrics.LogSessionId LOG_SESSION_ID_NONE;
}
- public class MediaMetricsManager {
+ public final class MediaMetricsManager {
method @NonNull public android.media.metrics.PlaybackSession createPlaybackSession();
method @NonNull public android.media.metrics.RecordingSession createRecordingSession();
field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL
@@ -25217,7 +25194,6 @@
method public float getPlaybackSpeed();
method public long getPosition();
method public int getState();
- method public boolean isActive();
method public void writeToParcel(android.os.Parcel, int);
field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
field public static final long ACTION_PAUSE = 2L; // 0x2L
@@ -38369,25 +38345,6 @@
}
-package android.service.dataloader {
-
- public abstract class DataLoaderService extends android.app.Service {
- ctor public DataLoaderService();
- method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method @Nullable public android.service.dataloader.DataLoaderService.DataLoader onCreateDataLoader(@NonNull android.content.pm.DataLoaderParams);
- }
-
- public static interface DataLoaderService.DataLoader {
- method public boolean onCreate(@NonNull android.content.pm.DataLoaderParams, @NonNull android.service.dataloader.DataLoaderService.FileSystemConnector);
- method public boolean onPrepareImage(@NonNull java.util.Collection<android.content.pm.InstallationFile>, @NonNull java.util.Collection<java.lang.String>);
- }
-
- public static final class DataLoaderService.FileSystemConnector {
- method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void writeData(@NonNull String, long, long, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
- }
-
-}
-
package android.service.dreams {
public class DreamService extends android.app.Service implements android.view.Window.Callback {
@@ -40334,7 +40291,6 @@
field public static final int DURATION_MEDIUM = 2; // 0x2
field public static final int DURATION_SHORT = 1; // 0x1
field public static final int DURATION_VERY_SHORT = 0; // 0x0
- field public static final long ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION = 157233955L; // 0x95f3323L
field public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
field public static final String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
@@ -42609,7 +42565,7 @@
method public String getNetworkOperator();
method public String getNetworkOperatorName();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode();
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getNetworkSlicingConfiguration(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.data.SlicingConfig,android.telephony.TelephonyManager.SlicingException>);
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getNetworkSlicingConfiguration(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.data.NetworkSlicingConfig,android.telephony.TelephonyManager.NetworkSlicingException>);
method public String getNetworkSpecifier();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType();
method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
@@ -42660,6 +42616,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isModemEnabledForSlot(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int isMultiSimSupported();
method public boolean isNetworkRoaming();
+ method public boolean isRadioInterfaceCapabilitySupported(@NonNull String);
method public boolean isRttSupported();
method public boolean isSmsCapable();
method @Deprecated public boolean isTtyModeSupported();
@@ -42859,11 +42816,13 @@
field public static final int ERROR_TIMEOUT = 1; // 0x1
}
- public static class TelephonyManager.SlicingException extends java.lang.Exception {
- ctor public TelephonyManager.SlicingException(int);
- method public int getErrorCode();
- field public static final int ERROR_MODEM_ERROR = 2; // 0x2
- field public static final int ERROR_TIMEOUT = 1; // 0x1
+ public class TelephonyManager.ModemErrorException extends android.telephony.TelephonyManager.NetworkSlicingException {
+ }
+
+ public static class TelephonyManager.NetworkSlicingException extends java.lang.Exception {
+ }
+
+ public class TelephonyManager.TimeoutException extends android.telephony.TelephonyManager.NetworkSlicingException {
}
public abstract static class TelephonyManager.UssdResponseCallback {
@@ -43073,6 +43032,15 @@
method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setStatus(int);
}
+ public final class NetworkSlicingConfig implements android.os.Parcelable {
+ ctor public NetworkSlicingConfig();
+ method public int describeContents();
+ method @NonNull public java.util.List<android.telephony.data.NetworkSliceInfo> getSliceInfo();
+ method @NonNull public java.util.List<android.telephony.data.UrspRule> getUrspRules();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.NetworkSlicingConfig> CREATOR;
+ }
+
public final class RouteSelectionDescriptor implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<java.lang.String> getDataNetworkName();
@@ -43090,30 +43058,14 @@
field public static final int SESSION_TYPE_IPV6 = 1; // 0x1
}
- public final class SlicingConfig implements android.os.Parcelable {
- ctor public SlicingConfig();
- method public int describeContents();
- method @NonNull public java.util.List<android.telephony.data.NetworkSliceInfo> getSliceInfo();
- method @NonNull public java.util.List<android.telephony.data.UrspRule> getUrspRules();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.SlicingConfig> CREATOR;
- }
-
public final class TrafficDescriptor implements android.os.Parcelable {
method public int describeContents();
method @Nullable public String getDataNetworkName();
- method @Nullable public String getOsAppId();
+ method @Nullable public byte[] getOsAppId();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.TrafficDescriptor> CREATOR;
}
- public static final class TrafficDescriptor.Builder {
- ctor public TrafficDescriptor.Builder();
- method @NonNull public android.telephony.data.TrafficDescriptor build();
- method @NonNull public android.telephony.data.TrafficDescriptor.Builder setDataNetworkName(@NonNull String);
- method @NonNull public android.telephony.data.TrafficDescriptor.Builder setOsAppId(@NonNull String);
- }
-
public final class UrspRule implements android.os.Parcelable {
method public int describeContents();
method @IntRange(from=0x0, to=0xff) public int getPrecedence();
@@ -48567,7 +48519,7 @@
method public android.view.View focusSearch(int);
method public void forceHasOverlappingRendering(boolean);
method public void forceLayout();
- method @Nullable public void generateDisplayHash(@NonNull String, @Nullable android.graphics.Rect, @NonNull java.util.concurrent.Executor, @NonNull android.view.displayhash.DisplayHashResultCallback);
+ method public void generateDisplayHash(@NonNull String, @Nullable android.graphics.Rect, @NonNull java.util.concurrent.Executor, @NonNull android.view.displayhash.DisplayHashResultCallback);
method public static int generateViewId();
method public CharSequence getAccessibilityClassName();
method public android.view.View.AccessibilityDelegate getAccessibilityDelegate();
@@ -52912,7 +52864,7 @@
public class Translator {
method public void destroy();
method public boolean isDestroyed();
- method @Nullable public void translate(@NonNull android.view.translation.TranslationRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.translation.TranslationResponse>);
+ method @Nullable public void translate(@NonNull android.view.translation.TranslationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.translation.TranslationResponse>);
}
public final class UiTranslationManager {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 2497827..b653410 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -161,6 +161,10 @@
method public void onVolumeChanged(@NonNull android.media.session.MediaSession.Token, int);
}
+ public final class PlaybackState implements android.os.Parcelable {
+ method public boolean isActiveState();
+ }
+
}
package android.net {
@@ -232,7 +236,7 @@
}
public class VpnManager {
- field @Deprecated public static final int TYPE_VPN_LEGACY = 3; // 0x3
+ field public static final int TYPE_VPN_LEGACY = 3; // 0x3
field public static final int TYPE_VPN_NONE = -1; // 0xffffffff
field public static final int TYPE_VPN_OEM = 4; // 0x4
field public static final int TYPE_VPN_PLATFORM = 2; // 0x2
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 37bc44f..adbf18f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -40,6 +40,7 @@
field public static final String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE";
field public static final String BIND_CONTENT_SUGGESTIONS_SERVICE = "android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE";
field public static final String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
+ field public static final String BIND_DISPLAY_HASHING_SERVICE = "android.permission.BIND_DISPLAY_HASHING_SERVICE";
field public static final String BIND_DOMAIN_VERIFICATION_AGENT = "android.permission.BIND_DOMAIN_VERIFICATION_AGENT";
field public static final String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
@@ -437,7 +438,8 @@
public class AlarmManager {
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, android.app.PendingIntent, android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void set(int, long, long, long, android.app.AlarmManager.OnAlarmListener, android.os.Handler, android.os.WorkSource);
- method @RequiresPermission(android.Manifest.permission.SCHEDULE_PRIORITIZED_ALARM) public void setPrioritized(int, long, long, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.app.AlarmManager.OnAlarmListener);
+ method @RequiresPermission(allOf={android.Manifest.permission.UPDATE_DEVICE_STATS, android.Manifest.permission.SCHEDULE_EXACT_ALARM}, conditional=true) public void setExact(int, long, @Nullable String, @NonNull java.util.concurrent.Executor, @NonNull android.os.WorkSource, @NonNull android.app.AlarmManager.OnAlarmListener);
+ method @RequiresPermission(android.Manifest.permission.SCHEDULE_PRIORITIZED_ALARM) public void setPrioritized(int, long, long, @Nullable String, @NonNull java.util.concurrent.Executor, @NonNull android.app.AlarmManager.OnAlarmListener);
}
public class AppOpsManager {
@@ -1901,7 +1903,7 @@
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getDynamicBufferSupport();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBufferLengthMillis(int, int);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1; // 0x1
field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; // 0x2
field public static final int DYNAMIC_BUFFER_SUPPORT_NONE = 0; // 0x0
@@ -1917,22 +1919,22 @@
method public void finalize();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isAudioPlaying(@NonNull android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
}
public final class BluetoothAdapter {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean addOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
- method public boolean disableBLE();
- method public boolean enableBLE();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disableBLE();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enableBLE();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enableNoAutoConnect();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void generateLocalOobData(int, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OobDataCallback);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public long getDiscoveryEndMillis();
method public boolean isBleScanAlwaysAvailable();
method public boolean isLeEnabled();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeActiveDevice(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean removeActiveDevice(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setActiveDevice(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setActiveDevice(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
field public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
field public static final int ACTIVE_DEVICE_ALL = 2; // 0x2
@@ -1948,18 +1950,20 @@
}
public static interface BluetoothAdapter.OobDataCallback {
+ method public void onError(int);
+ method public void onOobData(int, @Nullable android.bluetooth.OobData);
}
public final class BluetoothDevice implements android.os.Parcelable {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean canBondWithoutDialog();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelBondProcess();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean createBondOutOfBand(int, @Nullable android.bluetooth.OobData, @Nullable android.bluetooth.OobData);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean cancelBondProcess();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean createBondOutOfBand(int, @Nullable android.bluetooth.OobData, @Nullable android.bluetooth.OobData);
method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getSimAccessPermission();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getSimAccessPermission();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isConnected();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isEncrypted();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInSilenceMode();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeBond();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeBond();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMessageAccessPermission(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, @NonNull byte[]);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
@@ -2000,51 +2004,51 @@
}
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connect(android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean connect(android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
}
public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public long getHiSyncId(@NonNull android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
}
public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile {
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
}
public final class BluetoothHidHost implements android.bluetooth.BluetoothProfile {
- method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
- field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
}
public final class BluetoothMap implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void close();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) protected void finalize();
- method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method public void close();
+ method protected void finalize();
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
}
public final class BluetoothMapClient implements android.bluetooth.BluetoothProfile {
- method @RequiresPermission(android.Manifest.permission.SEND_SMS) public boolean sendMessage(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.Collection<android.net.Uri>, @NonNull String, @Nullable android.app.PendingIntent, @Nullable android.app.PendingIntent);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.SEND_SMS}) public boolean sendMessage(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.Collection<android.net.Uri>, @NonNull String, @Nullable android.app.PendingIntent, @Nullable android.app.PendingIntent);
}
public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
- method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isTetheringOn();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setBluetoothTethering(boolean);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
- field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
- field public static final String ACTION_TETHERING_STATE_CHANGED = "android.bluetooth.action.TETHERING_STATE_CHANGED";
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isTetheringOn();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.TETHER_PRIVILEGED}) public void setBluetoothTethering(boolean);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_TETHERING_STATE_CHANGED = "android.bluetooth.action.TETHERING_STATE_CHANGED";
field public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
field public static final String EXTRA_TETHERING_STATE = "android.bluetooth.extra.TETHERING_STATE";
field public static final int LOCAL_NAP_ROLE = 1; // 0x1
@@ -2058,7 +2062,7 @@
public class BluetoothPbap implements android.bluetooth.BluetoothProfile {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
}
@@ -2183,9 +2187,9 @@
package android.bluetooth.le {
public final class BluetoothLeScanner {
- method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADMIN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(android.os.WorkSource, android.bluetooth.le.ScanCallback);
- method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_ADMIN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.os.WorkSource, android.bluetooth.le.ScanCallback);
- method public void startTruncatedScan(java.util.List<android.bluetooth.le.TruncatedFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(android.os.WorkSource, android.bluetooth.le.ScanCallback);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.os.WorkSource, android.bluetooth.le.ScanCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startTruncatedScan(java.util.List<android.bluetooth.le.TruncatedFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
}
public final class ResultStorageDescriptor implements android.os.Parcelable {
@@ -2319,7 +2323,7 @@
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
field public static final String TETHERING_SERVICE = "tethering";
- field public static final String TRANSLATION_MANAGER_SERVICE = "transformer";
+ field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
field public static final String VR_SERVICE = "vrmanager";
field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
@@ -2525,6 +2529,18 @@
public class DataLoaderParams {
method @NonNull public static final android.content.pm.DataLoaderParams forIncremental(@NonNull android.content.ComponentName, @NonNull String);
+ method @NonNull public static final android.content.pm.DataLoaderParams forStreaming(@NonNull android.content.ComponentName, @NonNull String);
+ method @NonNull public final String getArguments();
+ method @NonNull public final android.content.ComponentName getComponentName();
+ method @NonNull public final int getType();
+ }
+
+ public final class InstallationFile {
+ method public long getLengthBytes();
+ method public int getLocation();
+ method @Nullable public byte[] getMetadata();
+ method @NonNull public String getName();
+ method @Nullable public byte[] getSignature();
}
public final class InstantAppInfo implements android.os.Parcelable {
@@ -2618,10 +2634,19 @@
public class PackageInstaller {
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2
+ field public static final int DATA_LOADER_TYPE_NONE = 0; // 0x0
+ field public static final int DATA_LOADER_TYPE_STREAMING = 1; // 0x1
+ field public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
+ field public static final int LOCATION_DATA_APP = 0; // 0x0
+ field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
+ field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
}
public static class PackageInstaller.Session implements java.io.Closeable {
+ method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void commitTransferred(@NonNull android.content.IntentSender);
+ method @Nullable @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public android.content.pm.DataLoaderParams getDataLoaderParams();
+ method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void removeFile(int, @NonNull String);
}
public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
@@ -2642,6 +2667,7 @@
public static class PackageInstaller.SessionParams implements android.os.Parcelable {
method @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public void setAllocateAggressive(boolean);
method @Deprecated public void setAllowDowngrade(boolean);
+ method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.USE_INSTALLER_V2"}) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams);
method public void setDontKillApp(boolean);
method public void setEnableRollback(boolean);
method public void setEnableRollback(boolean, int);
@@ -7296,10 +7322,6 @@
package android.net {
- public class DnsResolverServiceManager {
- method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public static android.os.IBinder getService(@NonNull android.content.Context);
- }
-
public class EthernetManager {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
}
@@ -7859,7 +7881,7 @@
method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
method @Nullable public static android.net.wifi.nl80211.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
method @Deprecated public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
- method public boolean registerCountryCodeChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangeListener);
+ method public boolean registerCountryCodeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangedListener);
method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SendMgmtFrameCallback);
method public void setOnServiceDeadCallback(@NonNull Runnable);
method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback);
@@ -7872,7 +7894,7 @@
method public boolean tearDownClientInterface(@NonNull String);
method public boolean tearDownInterfaces();
method public boolean tearDownSoftApInterface(@NonNull String);
- method public void unregisterCountryCodeChangeListener(@NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangeListener);
+ method public void unregisterCountryCodeChangedListener(@NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangedListener);
field public static final String SCANNING_PARAM_ENABLE_6GHZ_RNR = "android.net.wifi.nl80211.SCANNING_PARAM_ENABLE_6GHZ_RNR";
field public static final int SCAN_TYPE_PNO_SCAN = 1; // 0x1
field public static final int SCAN_TYPE_SINGLE_SCAN = 0; // 0x0
@@ -7883,8 +7905,8 @@
field public static final int SEND_MGMT_FRAME_ERROR_UNKNOWN = 1; // 0x1
}
- public static interface WifiNl80211Manager.CountryCodeChangeListener {
- method public void onChanged(@NonNull String);
+ public static interface WifiNl80211Manager.CountryCodeChangedListener {
+ method public void onCountryCodeChanged(@NonNull String);
}
public static class WifiNl80211Manager.OemSecurityType {
@@ -7942,16 +7964,16 @@
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
- method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnStateCallback);
+ method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
- method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnStateCallback(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnStateCallback);
+ method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
}
- public static interface NfcAdapter.ControllerAlwaysOnStateCallback {
- method public void onStateChanged(boolean);
+ public static interface NfcAdapter.ControllerAlwaysOnListener {
+ method public void onControllerAlwaysOnChanged(boolean);
}
public static interface NfcAdapter.NfcUnlockHandler {
@@ -8370,6 +8392,7 @@
field public static final int REASON_ACCOUNT_TRANSFER = 104; // 0x68
field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
field public static final int REASON_GEOFENCING = 100; // 0x64
+ field public static final int REASON_LOCATION_PROVIDER = 312; // 0x138
field public static final int REASON_OTHER = 1; // 0x1
field public static final int REASON_PUSH_MESSAGING = 101; // 0x65
field public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
@@ -8422,6 +8445,7 @@
field @Deprecated public static final int EVENT_UNSPECIFIED = 0; // 0x0
field @Deprecated public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
field @Deprecated public static final int REASON_GEOFENCING = 100; // 0x64
+ field @Deprecated public static final int REASON_LOCATION_PROVIDER = 312; // 0x138
field @Deprecated public static final int REASON_OTHER = 1; // 0x1
field @Deprecated public static final int REASON_PUSH_MESSAGING = 101; // 0x65
field @Deprecated public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
@@ -8966,7 +8990,7 @@
package android.provider {
public class CallLog {
- method @RequiresPermission(allOf={android.Manifest.permission.WRITE_CALL_LOG, android.Manifest.permission.INTERACT_ACROSS_USERS}) public static void storeCallComposerPictureAsUser(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull java.io.InputStream, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.net.Uri,android.provider.CallLog.CallComposerLoggingException>);
+ method @RequiresPermission(allOf={android.Manifest.permission.WRITE_CALL_LOG, android.Manifest.permission.INTERACT_ACROSS_USERS}) public static void storeCallComposerPicture(@NonNull android.content.Context, @NonNull java.io.InputStream, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.net.Uri,android.provider.CallLog.CallComposerLoggingException>);
}
public static class CallLog.CallComposerLoggingException extends java.lang.Throwable {
@@ -9817,12 +9841,30 @@
}
+package android.service.dataloader {
+
+ public abstract class DataLoaderService extends android.app.Service {
+ ctor public DataLoaderService();
+ method @Nullable public android.service.dataloader.DataLoaderService.DataLoader onCreateDataLoader(@NonNull android.content.pm.DataLoaderParams);
+ }
+
+ public static interface DataLoaderService.DataLoader {
+ method public boolean onCreate(@NonNull android.content.pm.DataLoaderParams, @NonNull android.service.dataloader.DataLoaderService.FileSystemConnector);
+ method public boolean onPrepareImage(@NonNull java.util.Collection<android.content.pm.InstallationFile>, @NonNull java.util.Collection<java.lang.String>);
+ }
+
+ public static final class DataLoaderService.FileSystemConnector {
+ method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void writeData(@NonNull String, long, long, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException;
+ }
+
+}
+
package android.service.displayhash {
public final class DisplayHashParams implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.util.Size getBufferSize();
- method public boolean isGrayscaleBuffer();
+ method public boolean isUseGrayscale();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.displayhash.DisplayHashParams> CREATOR;
}
@@ -9831,17 +9873,17 @@
ctor public DisplayHashParams.Builder();
method @NonNull public android.service.displayhash.DisplayHashParams build();
method @NonNull public android.service.displayhash.DisplayHashParams.Builder setBufferSize(int, int);
- method @NonNull public android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean);
+ method @NonNull public android.service.displayhash.DisplayHashParams.Builder setUseGrayscale(boolean);
}
- public abstract class DisplayHasherService extends android.app.Service {
- ctor public DisplayHasherService();
+ public abstract class DisplayHashingService extends android.app.Service {
+ ctor public DisplayHashingService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method @Nullable public abstract void onGenerateDisplayHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String, @NonNull android.view.displayhash.DisplayHashResultCallback);
+ method public abstract void onGenerateDisplayHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String, @NonNull android.view.displayhash.DisplayHashResultCallback);
method @NonNull public abstract java.util.Map<java.lang.String,android.service.displayhash.DisplayHashParams> onGetDisplayHashAlgorithms();
method @Nullable public abstract android.view.displayhash.VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[], @NonNull android.view.displayhash.DisplayHash);
- field public static final String SERVICE_INTERFACE = "android.service.displayhash.DisplayHasherService";
- field public static final String SERVICE_META_DATA = "android.displayhash.display_hasher_service";
+ field public static final String SERVICE_INTERFACE = "android.service.displayhash.DisplayHashingService";
+ field public static final String SERVICE_META_DATA = "android.displayhash.display_hashing_service";
}
}
@@ -10183,7 +10225,7 @@
ctor public RotationResolutionRequest(@NonNull String, int, int, boolean, long);
method public int describeContents();
method public int getCurrentRotation();
- method @NonNull public String getPackageName();
+ method @NonNull public String getForegroundPackageName();
method public int getProposedRotation();
method public long getTimeoutMillis();
method public boolean shouldUseCamera();
@@ -10359,7 +10401,7 @@
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, @NonNull android.os.CancellationSignal, @NonNull android.service.translation.TranslationService.OnTranslationResultCallback);
+ method public abstract void onTranslationRequest(@NonNull android.view.translation.TranslationRequest, int, @Nullable android.os.CancellationSignal, @NonNull android.service.translation.TranslationService.OnTranslationResultCallback);
field public static final String SERVICE_INTERFACE = "android.service.translation.TranslationService";
field public static final String SERVICE_META_DATA = "android.translation_service";
}
@@ -11067,10 +11109,6 @@
field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool";
}
- public static final class CarrierConfigManager.Ims {
- field public static final String KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY = "ims.publish_service_desc_feature_tag_map_override_string_array";
- }
-
public static final class CarrierConfigManager.Wifi {
field public static final String KEY_AVOID_5GHZ_SOFTAP_FOR_LAA_BOOL = "wifi.avoid_5ghz_softap_for_laa_bool";
field public static final String KEY_AVOID_5GHZ_WIFI_DIRECT_FOR_LAA_BOOL = "wifi.avoid_5ghz_wifi_direct_for_laa_bool";
@@ -11936,7 +11974,6 @@
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
- method public boolean isRadioInterfaceCapabilitySupported(@NonNull String);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isTetheringApnRequired();
@@ -14120,6 +14157,7 @@
method @NonNull public java.io.File getFile();
method @Nullable public String getFontFamilyName();
method @NonNull public String getFontVariationSettings();
+ method @NonNull public String getPostScriptName();
method @NonNull public android.graphics.fonts.FontStyle getStyle();
method public int getTtcIndex();
method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index b50b8dd..bf9f4f1 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -51,7 +51,7 @@
package android.bluetooth {
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
- method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
+ method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
}
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 20e9187..3ce2df8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -161,6 +161,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void moveTaskToRootTask(int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void removeRootTasksInWindowingModes(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void removeRootTasksWithActivityTypes(@NonNull int[]);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean removeTask(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void resizeTask(int, android.graphics.Rect);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void startSystemLockTaskMode(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void stopSystemLockTaskMode();
@@ -656,6 +657,16 @@
ctor public UsageStats();
}
+ public static final class UsageStats.Builder {
+ ctor public UsageStats.Builder();
+ method @NonNull public android.app.usage.UsageStats build();
+ method @NonNull public android.app.usage.UsageStats.Builder setFirstTimeStamp(long);
+ method @NonNull public android.app.usage.UsageStats.Builder setLastTimeStamp(long);
+ method @NonNull public android.app.usage.UsageStats.Builder setLastTimeUsed(long);
+ method @NonNull public android.app.usage.UsageStats.Builder setPackageName(@Nullable String);
+ method @NonNull public android.app.usage.UsageStats.Builder setTotalTimeInForeground(long);
+ }
+
public final class UsageStatsManager {
method public void forceUsageSourceSettingRead();
}
@@ -998,7 +1009,7 @@
}
public class Typeface {
- method @NonNull public static java.util.Map<java.lang.String,android.graphics.Typeface> deserializeFontMap(@NonNull java.nio.ByteBuffer) throws java.io.IOException;
+ method @NonNull public static long[] deserializeFontMap(@NonNull java.nio.ByteBuffer, @NonNull java.util.Map<java.lang.String,android.graphics.Typeface>) throws java.io.IOException;
method @Nullable public static android.os.SharedMemory getSystemFontMapSharedMemory();
method @NonNull public static android.os.SharedMemory serializeFontMap(@NonNull java.util.Map<java.lang.String,android.graphics.Typeface>) throws android.system.ErrnoException, java.io.IOException;
}
@@ -1574,6 +1585,10 @@
method public static void setServiceForTest(@Nullable android.os.IBinder);
}
+ public class NetworkWatchlistManager {
+ method @Nullable public byte[] getWatchlistConfigHash();
+ }
+
public class TrafficStats {
method public static long getLoopbackRxBytes();
method public static long getLoopbackRxPackets();
@@ -1897,6 +1912,16 @@
method public String getPath();
}
+ public static final class StorageVolume.Builder {
+ ctor public StorageVolume.Builder(@NonNull String, @NonNull java.io.File, @NonNull String, @NonNull android.os.UserHandle, @NonNull String);
+ method @NonNull public android.os.storage.StorageVolume build();
+ method @NonNull public android.os.storage.StorageVolume.Builder setEmulated(boolean);
+ method @NonNull public android.os.storage.StorageVolume.Builder setPrimary(boolean);
+ method @NonNull public android.os.storage.StorageVolume.Builder setRemovable(boolean);
+ method @NonNull public android.os.storage.StorageVolume.Builder setStorageUuid(@Nullable java.util.UUID);
+ method @NonNull public android.os.storage.StorageVolume.Builder setUuid(@Nullable String);
+ }
+
}
package android.os.strictmode {
@@ -2009,6 +2034,7 @@
public final class PermissionManager {
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData();
+ method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData(boolean);
method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource);
}
@@ -2487,6 +2513,7 @@
method @NonNull public java.io.File getFile();
method @Nullable public String getFontFamilyName();
method @NonNull public String getFontVariationSettings();
+ method @NonNull public String getPostScriptName();
method @NonNull public android.graphics.fonts.FontStyle getStyle();
method public int getTtcIndex();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -2739,6 +2766,7 @@
public interface WindowManager extends android.view.ViewManager {
method public default int getDisplayImePolicy(int);
method public default void holdLock(android.os.IBinder, int);
+ method public default boolean isTaskSnapshotSupported();
method public default void setDisplayImePolicy(int, int);
method public default void setForceCrossWindowBlurDisabled(boolean);
method public default void setShouldShowSystemDecors(int, boolean);
@@ -3172,5 +3200,12 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void applyTransaction(@NonNull android.window.WindowContainerTransaction);
}
+ @UiContext public abstract class WindowProviderService extends android.app.Service {
+ ctor public WindowProviderService();
+ method public final void attachToWindowToken(@NonNull android.os.IBinder);
+ method @Nullable public android.os.Bundle getWindowContextOptions();
+ method public abstract int getWindowType();
+ }
+
}
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 1d290de..c8a8d36 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -70,6 +70,7 @@
"android/annotation/Nullable.java",
"android/annotation/PluralsRes.java",
"android/annotation/RawRes.java",
+ "android/annotation/RequiresNoPermission.java",
"android/annotation/RequiresPermission.java",
"android/annotation/SdkConstant.java",
"android/annotation/Size.java",
diff --git a/core/java/android/annotation/RequiresNoPermission.java b/core/java/android/annotation/RequiresNoPermission.java
new file mode 100644
index 0000000..6ff4d6e3
--- /dev/null
+++ b/core/java/android/annotation/RequiresNoPermission.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.annotation;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the annotated element requires no permissions.
+ *
+ * @hide
+ */
+@Retention(CLASS)
+@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD,PARAMETER})
+public @interface RequiresNoPermission {
+}
diff --git a/core/java/android/annotation/RequiresPermission.java b/core/java/android/annotation/RequiresPermission.java
index 1d89e31..303ab41 100644
--- a/core/java/android/annotation/RequiresPermission.java
+++ b/core/java/android/annotation/RequiresPermission.java
@@ -20,7 +20,7 @@
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@@ -76,7 +76,7 @@
*
* @hide
*/
-@Retention(SOURCE)
+@Retention(CLASS)
@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD,PARAMETER})
public @interface RequiresPermission {
/**
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 45120b6..77f0cf8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -8792,11 +8792,11 @@
* @hide
*/
public void updateUiTranslationState(int state, TranslationSpec sourceSpec,
- TranslationSpec destSpec, List<AutofillId> viewIds) {
+ TranslationSpec targetSpec, List<AutofillId> viewIds) {
if (mUiTranslationController == null) {
mUiTranslationController = new UiTranslationController(this, getApplicationContext());
}
- mUiTranslationController.updateUiTranslationState(state, sourceSpec, destSpec, viewIds);
+ mUiTranslationController.updateUiTranslationState(state, sourceSpec, targetSpec, viewIds);
}
class HostCallbacks extends FragmentHostCallback<Activity> {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index db42803..a24555f 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4272,7 +4272,8 @@
try {
getService().broadcastIntentWithFeature(
null, null, intent, null, null, Activity.RESULT_OK, null, null,
- null /*permission*/, appOp, null, false, true, userId);
+ null /*requiredPermissions*/, null /*excludedPermissions*/, appOp, null, false,
+ true, userId);
} catch (RemoteException ex) {
}
}
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 627017c..28d6fbb 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -469,7 +469,8 @@
}
}
- /** @hide */
+ /** Removes task by a given taskId */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
public boolean removeTask(int taskId) {
try {
return getService().removeTask(taskId);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8ff14b0..0acc4b3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1835,12 +1835,12 @@
@Override
public void updateUiTranslationState(IBinder activityToken, int state,
- TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds) {
+ TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = activityToken;
args.arg2 = state;
args.arg3 = sourceSpec;
- args.arg4 = destSpec;
+ args.arg4 = targetSpec;
args.arg5 = viewIds;
sendMessage(H.UPDATE_UI_TRANSLATION_STATE, args);
}
@@ -4169,13 +4169,13 @@
}
private void updateUiTranslationState(IBinder activityToken, int state,
- TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds) {
+ TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds) {
final ActivityClientRecord r = mActivities.get(activityToken);
if (r == null) {
Log.w(TAG, "updateUiTranslationState(): no activity for " + activityToken);
return;
}
- r.activity.updateUiTranslationState(state, sourceSpec, destSpec, viewIds);
+ r.activity.updateUiTranslationState(state, sourceSpec, targetSpec, viewIds);
}
private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
@@ -4390,11 +4390,12 @@
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
- ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
Application app = packageInfo.makeApplication(false, mInstrumentation);
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
+ final ContextImpl context = ContextImpl.getImpl(service
+ .createServiceBaseContext(this, packageInfo));
// Service resources must be initialized with the same loaders as the application
// context.
context.getResources().addLoaders(
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index df9530f..1cb46b1 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -6475,7 +6475,7 @@
historicalDiscreteAccesses.add(other.mDiscreteAccesses.get(i++));
}
}
- mDiscreteAccesses = historicalDiscreteAccesses;
+ mDiscreteAccesses = deduplicateDiscreteEvents(historicalDiscreteAccesses);
}
private void increaseAccessCount(@UidState int uidState, @OpFlags int flags,
@@ -6996,7 +6996,7 @@
}
result.add(entry);
}
- return result;
+ return deduplicateDiscreteEvents(result);
}
/**
@@ -9819,4 +9819,52 @@
}
}
}
+
+ private static List<AttributedOpEntry> deduplicateDiscreteEvents(List<AttributedOpEntry> list) {
+ int n = list.size();
+ int i = 0;
+ for (int j = 0, k = 0; j < n; i++, j = k) {
+ long currentAccessTime = list.get(j).getLastAccessTime(OP_FLAGS_ALL);
+ k = j + 1;
+ while(k < n && list.get(k).getLastAccessTime(OP_FLAGS_ALL) == currentAccessTime) {
+ k++;
+ }
+ list.set(i, mergeAttributedOpEntries(list.subList(j, k)));
+ }
+ for (; i < n; i++) {
+ list.remove(list.size() - 1);
+ }
+ return list;
+ }
+
+ private static AttributedOpEntry mergeAttributedOpEntries(List<AttributedOpEntry> opEntries) {
+ if (opEntries.size() == 1) {
+ return opEntries.get(0);
+ }
+ LongSparseArray<AppOpsManager.NoteOpEvent> accessEvents = new LongSparseArray<>();
+ LongSparseArray<AppOpsManager.NoteOpEvent> rejectEvents = new LongSparseArray<>();
+ int opCount = opEntries.size();
+ for (int i = 0; i < opCount; i++) {
+ AttributedOpEntry a = opEntries.get(i);
+ ArraySet<Long> keys = a.collectKeys();
+ final int keyCount = keys.size();
+ for (int k = 0; k < keyCount; k++) {
+ final long key = keys.valueAt(k);
+
+ final int uidState = extractUidStateFromKey(key);
+ final int flags = extractFlagsFromKey(key);
+
+ NoteOpEvent access = a.getLastAccessEvent(uidState, uidState, flags);
+ NoteOpEvent reject = a.getLastRejectEvent(uidState, uidState, flags);
+
+ if (access != null) {
+ accessEvents.append(key, access);
+ }
+ if (reject != null) {
+ rejectEvents.append(key, reject);
+ }
+ }
+ }
+ return new AttributedOpEntry(opEntries.get(0).mOp, false, accessEvents, rejectEvents);
+ }
}
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index dfc105a..8574678 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -614,7 +614,7 @@
* tombstone traces will be returned for
* {@link #REASON_CRASH_NATIVE}, with an InputStream containing a protobuf with
* <a href="https://android.googlesource.com/platform/system/core/+/refs/heads/master/debuggerd/proto/tombstone.proto">this schema</a>.
- * Note thatbecause these traces are kept in a separate global circular buffer, crashes may be
+ * Note that because these traces are kept in a separate global circular buffer, crashes may be
* overwritten by newer crashes (including from other applications), so this may still return
* null.
*
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f7ea381..9753b67 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1176,8 +1176,8 @@
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
- null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
- false, getUserId());
+ null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/,
+ AppOpsManager.OP_NONE, null, false, false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1194,7 +1194,8 @@
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
null, Activity.RESULT_OK, null, null, receiverPermissions,
- AppOpsManager.OP_NONE, null, false, false, getUserId());
+ null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, false, false,
+ getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1209,7 +1210,8 @@
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
null, Activity.RESULT_OK, null, null, receiverPermissions,
- AppOpsManager.OP_NONE, null, false, false, getUserId());
+ null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, false, false,
+ getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1224,7 +1226,24 @@
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
null, Activity.RESULT_OK, null, null, receiverPermissions,
- AppOpsManager.OP_NONE, null, false, false, user.getIdentifier());
+ null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, false, false,
+ user.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions,
+ String[] excludedPermissions) {
+ warnIfCallingFromSystemProcess();
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ intent.prepareToLeaveProcess(this);
+ ActivityManager.getService().broadcastIntentWithFeature(
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, receiverPermissions, excludedPermissions,
+ AppOpsManager.OP_NONE, null, false, false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1241,7 +1260,8 @@
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
null, Activity.RESULT_OK, null, null, receiverPermissions,
- AppOpsManager.OP_NONE, options, false, false, getUserId());
+ null /*excludedPermissions=*/, AppOpsManager.OP_NONE, options, false, false,
+ getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1257,8 +1277,8 @@
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
- null, Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false,
- false, getUserId());
+ null, Activity.RESULT_OK, null, null, receiverPermissions,
+ null /*excludedPermissions=*/, appOp, null, false, false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1275,7 +1295,8 @@
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
null, Activity.RESULT_OK, null, null, receiverPermissions,
- AppOpsManager.OP_NONE, null, true, false, getUserId());
+ null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, true, false,
+ getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1337,8 +1358,8 @@
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
- rd, initialCode, initialData, initialExtras, receiverPermissions, appOp,
- options, true, false, getUserId());
+ rd, initialCode, initialData, initialExtras, receiverPermissions,
+ null /*excludedPermissions=*/, appOp, options, true, false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1351,8 +1372,8 @@
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
- null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
- false, user.getIdentifier());
+ null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/,
+ AppOpsManager.OP_NONE, null, false, false, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1375,7 +1396,8 @@
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
null, Activity.RESULT_OK, null, null, receiverPermissions,
- AppOpsManager.OP_NONE, options, false, false, user.getIdentifier());
+ null /*excludedPermissions=*/, AppOpsManager.OP_NONE, options, false, false,
+ user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1391,8 +1413,8 @@
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
- null, Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false,
- false, user.getIdentifier());
+ null, Activity.RESULT_OK, null, null, receiverPermissions,
+ null /*excludedPermissions=*/, appOp, null, false, false, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1442,8 +1464,9 @@
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
- rd, initialCode, initialData, initialExtras, receiverPermissions, appOp,
- options, true, false, user.getIdentifier());
+ rd, initialCode, initialData, initialExtras, receiverPermissions,
+ null /*excludedPermissions=*/, appOp, options, true, false,
+ user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1483,8 +1506,8 @@
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
- null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
- true, getUserId());
+ null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/,
+ AppOpsManager.OP_NONE, null, false, true, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1522,8 +1545,8 @@
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
- null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options,
- false, true, getUserId());
+ null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/,
+ AppOpsManager.OP_NONE, options, false, true, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1558,8 +1581,9 @@
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
- rd, initialCode, initialData, initialExtras, null, AppOpsManager.OP_NONE, null,
- true, true, getUserId());
+ rd, initialCode, initialData, initialExtras, null,
+ null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, true, true,
+ getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1590,8 +1614,8 @@
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
- null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
- true, user.getIdentifier());
+ null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/,
+ AppOpsManager.OP_NONE, null, false, true, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1605,8 +1629,8 @@
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
- null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options,
- false, true, user.getIdentifier());
+ null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/,
+ AppOpsManager.OP_NONE, options, false, true, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1640,8 +1664,9 @@
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
- rd, initialCode, initialData, initialExtras, null, AppOpsManager.OP_NONE, null,
- true, true, user.getIdentifier());
+ rd, initialCode, initialData, initialExtras, null,
+ null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, true, true,
+ user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f9279da..89d90a3 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -137,7 +137,7 @@
int appOp, in Bundle options, boolean serialized, boolean sticky, int userId);
int broadcastIntentWithFeature(in IApplicationThread caller, in String callingFeatureId,
in Intent intent, in String resolvedType, in IIntentReceiver resultTo, int resultCode,
- in String resultData, in Bundle map, in String[] requiredPermissions,
+ in String resultData, in Bundle map, in String[] requiredPermissions, in String[] excludePermissions,
int appOp, in Bundle options, boolean serialized, boolean sticky, int userId);
void unbroadcastIntent(in IApplicationThread caller, in Intent intent, int userId);
@UnsupportedAppUsage
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 78e7ce8..918309e 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -160,5 +160,5 @@
IUiAutomationConnection instrumentationUiConnection,
in ApplicationInfo targetInfo);
void updateUiTranslationState(IBinder activityToken, int state, in TranslationSpec sourceSpec,
- in TranslationSpec destSpec, in List<AutofillId> viewIds);
+ in TranslationSpec targetSpec, in List<AutofillId> viewIds);
}
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index e83557c..4f7c684 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -190,4 +190,18 @@
* Called from SystemUI when it shows the AoD UI.
*/
oneway void setInAmbientMode(boolean inAmbientMode, long animationDuration);
+
+ /**
+ * Called from SystemUI when the device is waking up.
+ *
+ * @hide
+ */
+ oneway void notifyWakingUp(int x, int y, in Bundle extras);
+
+ /**
+ * Called from SystemUI when the device is going to sleep.
+ *
+ * @hide
+ */
+ void notifyGoingToSleep(int x, int y, in Bundle extras);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d15d1b7..7ce0c70 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6315,7 +6315,7 @@
* Gets the theme's background color
*/
private @ColorInt int getDefaultBackgroundColor() {
- return obtainThemeColor(R.attr.colorBackground,
+ return obtainThemeColor(R.attr.colorSurface,
mInNightMode ? Color.BLACK : Color.WHITE);
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 4cf3a80..ca08683 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -53,7 +53,6 @@
import android.os.UserHandle;
import android.util.AndroidException;
import android.util.ArraySet;
-import android.util.Log;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.IResultReceiver;
@@ -371,19 +370,9 @@
"Cannot set both FLAG_IMMUTABLE and FLAG_MUTABLE for PendingIntent");
}
- // TODO(b/178092897) Remove the below instrumentation check and enforce
- // the explicit mutability requirement for apps under instrumentation.
- ActivityThread thread = ActivityThread.currentActivityThread();
- Instrumentation mInstrumentation = thread.getInstrumentation();
-
if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED)
&& !flagImmutableSet && !flagMutableSet) {
-
- if (mInstrumentation.isInstrumenting()) {
- Log.e(TAG, msg);
- } else {
throw new IllegalArgumentException(msg);
- }
}
}
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 2ceea7f..0ab3f2f 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -861,6 +861,19 @@
}
/**
+ * Creates the base {@link Context} of this {@link Service}.
+ * Users may override this API to create customized base context.
+ *
+ * @see android.window.WindowProviderService WindowProviderService class for example
+ * @see ContextWrapper#attachBaseContext(Context)
+ *
+ * @hide
+ */
+ public Context createServiceBaseContext(ActivityThread mainThread, LoadedApk packageInfo) {
+ return ContextImpl.createAppContext(mainThread, packageInfo);
+ }
+
+ /**
* @hide
* Clean up any references to avoid leaks.
*/
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 6a71c92..8d332ab 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -190,6 +190,30 @@
public static final String COMMAND_DROP = "android.home.drop";
/**
+ * Command for {@link #sendWallpaperCommand}: reported by System UI when the device is waking
+ * up. The x and y arguments are a location (possibly very roughly) corresponding to the action
+ * that caused the device to wake up. For example, if the power button was pressed, this will be
+ * the location on the screen nearest the power button.
+ *
+ * If the location is unknown or not applicable, x and y will be -1.
+ *
+ * @hide
+ */
+ public static final String COMMAND_WAKING_UP = "android.wallpaper.wakingup";
+
+ /**
+ * Command for {@link #sendWallpaperCommand}: reported by System UI when the device is going to
+ * sleep. The x and y arguments are a location (possibly very roughly) corresponding to the
+ * action that caused the device to go to sleep. For example, if the power button was pressed,
+ * this will be the location on the screen nearest the power button.
+ *
+ * If the location is unknown or not applicable, x and y will be -1.
+ *
+ * @hide
+ */
+ public static final String COMMAND_GOING_TO_SLEEP = "android.wallpaper.goingtosleep";
+
+ /**
* Command for {@link #sendWallpaperCommand}: reported when the wallpaper that was already
* set is re-applied by the user.
* @hide
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index e93138b..759597c 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -28,6 +28,7 @@
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.MIN_LOCK_PASSWORD_SIZE;
import static com.android.internal.widget.PasswordValidationError.CONTAINS_INVALID_CHARACTERS;
import static com.android.internal.widget.PasswordValidationError.CONTAINS_SEQUENCE;
@@ -74,11 +75,8 @@
// consider it a complex PIN/password.
public static final int MAX_ALLOWED_SEQUENCE = 3;
- // One of CREDENTIAL_TYPE_NONE, CREDENTIAL_TYPE_PATTERN or CREDENTIAL_TYPE_PASSWORD.
- // Note that this class still uses CREDENTIAL_TYPE_PASSWORD to represent both numeric PIN
- // and alphabetic password. This is OK as long as this definition is only used internally,
- // and the value never gets mixed up with credential types from other parts of the framework.
- // TODO: fix this (ideally after we move logic to PasswordPolicy)
+ // One of CREDENTIAL_TYPE_NONE, CREDENTIAL_TYPE_PATTERN, CREDENTIAL_TYPE_PIN or
+ // CREDENTIAL_TYPE_PASSWORD.
public @CredentialType int credType;
// Fields below only make sense when credType is PASSWORD.
public int length = 0;
@@ -192,13 +190,15 @@
/**
* Returns the {@code PasswordMetrics} for a given credential.
*
- * If the credential is a pin or a password, equivalent to {@link #computeForPassword(byte[])}.
- * {@code credential} cannot be null when {@code type} is
+ * If the credential is a pin or a password, equivalent to
+ * {@link #computeForPasswordOrPin(byte[], boolean)}. {@code credential} cannot be null
+ * when {@code type} is
* {@link com.android.internal.widget.LockPatternUtils#CREDENTIAL_TYPE_PASSWORD}.
*/
public static PasswordMetrics computeForCredential(LockscreenCredential credential) {
if (credential.isPassword() || credential.isPin()) {
- return PasswordMetrics.computeForPassword(credential.getCredential());
+ return PasswordMetrics.computeForPasswordOrPin(credential.getCredential(),
+ credential.isPin());
} else if (credential.isPattern()) {
return new PasswordMetrics(CREDENTIAL_TYPE_PATTERN);
} else if (credential.isNone()) {
@@ -209,9 +209,9 @@
}
/**
- * Returns the {@code PasswordMetrics} for a given password
+ * Returns the {@code PasswordMetrics} for a given password or pin
*/
- public static PasswordMetrics computeForPassword(@NonNull byte[] password) {
+ public static PasswordMetrics computeForPasswordOrPin(byte[] password, boolean isPin) {
// Analyse the characters used
int letters = 0;
int upperCase = 0;
@@ -245,8 +245,9 @@
}
}
+ final int credType = isPin ? CREDENTIAL_TYPE_PIN : CREDENTIAL_TYPE_PASSWORD;
final int seqLength = maxLengthSequence(password);
- return new PasswordMetrics(CREDENTIAL_TYPE_PASSWORD, length, letters, upperCase, lowerCase,
+ return new PasswordMetrics(credType, length, letters, upperCase, lowerCase,
numeric, symbols, nonLetter, nonNumeric, seqLength);
}
@@ -353,7 +354,7 @@
*/
public void maxWith(PasswordMetrics other) {
credType = Math.max(credType, other.credType);
- if (credType != CREDENTIAL_TYPE_PASSWORD) {
+ if (credType != CREDENTIAL_TYPE_PASSWORD && credType != CREDENTIAL_TYPE_PIN) {
return;
}
length = Math.max(length, other.length);
@@ -408,7 +409,7 @@
@Override
boolean allowsCredType(int credType) {
- return credType == CREDENTIAL_TYPE_PASSWORD;
+ return credType == CREDENTIAL_TYPE_PASSWORD || credType == CREDENTIAL_TYPE_PIN;
}
},
BUCKET_MEDIUM(PASSWORD_COMPLEXITY_MEDIUM) {
@@ -424,7 +425,7 @@
@Override
boolean allowsCredType(int credType) {
- return credType == CREDENTIAL_TYPE_PASSWORD;
+ return credType == CREDENTIAL_TYPE_PASSWORD || credType == CREDENTIAL_TYPE_PIN;
}
},
BUCKET_LOW(PASSWORD_COMPLEXITY_LOW) {
@@ -489,7 +490,7 @@
if (!bucket.allowsCredType(credType)) {
return false;
}
- if (credType != CREDENTIAL_TYPE_PASSWORD) {
+ if (credType != CREDENTIAL_TYPE_PASSWORD && credType != CREDENTIAL_TYPE_PIN) {
return true;
}
return (bucket.canHaveSequence() || seqLength <= MAX_ALLOWED_SEQUENCE)
@@ -529,7 +530,7 @@
new PasswordValidationError(CONTAINS_INVALID_CHARACTERS, 0));
}
- final PasswordMetrics enteredMetrics = computeForPassword(password);
+ final PasswordMetrics enteredMetrics = computeForPasswordOrPin(password, isPin);
return validatePasswordMetrics(adminMetrics, minComplexity, isPin, enteredMetrics);
}
@@ -555,8 +556,8 @@
|| !bucket.allowsCredType(actualMetrics.credType)) {
return Collections.singletonList(new PasswordValidationError(WEAK_CREDENTIAL_TYPE, 0));
}
- // TODO: this needs to be modified if CREDENTIAL_TYPE_PIN is added.
- if (actualMetrics.credType != CREDENTIAL_TYPE_PASSWORD) {
+ if (actualMetrics.credType != CREDENTIAL_TYPE_PASSWORD
+ && actualMetrics.credType != CREDENTIAL_TYPE_PIN) {
return Collections.emptyList(); // Nothing to check for pattern or none.
}
diff --git a/core/java/android/app/admin/PasswordPolicy.java b/core/java/android/app/admin/PasswordPolicy.java
index 13f11ad..0544a36 100644
--- a/core/java/android/app/admin/PasswordPolicy.java
+++ b/core/java/android/app/admin/PasswordPolicy.java
@@ -20,6 +20,7 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
@@ -27,6 +28,7 @@
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
/**
* {@hide}
@@ -58,14 +60,20 @@
} else if (quality == PASSWORD_QUALITY_BIOMETRIC_WEAK
|| quality == PASSWORD_QUALITY_SOMETHING) {
return new PasswordMetrics(CREDENTIAL_TYPE_PATTERN);
- } // quality is NUMERIC or stronger.
+ } else if (quality == PASSWORD_QUALITY_NUMERIC
+ || quality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
+ PasswordMetrics result = new PasswordMetrics(CREDENTIAL_TYPE_PIN);
+ result.length = length;
+ if (quality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
+ result.seqLength = PasswordMetrics.MAX_ALLOWED_SEQUENCE;
+ }
+ return result;
+ } // quality is ALPHABETIC or stronger.
PasswordMetrics result = new PasswordMetrics(CREDENTIAL_TYPE_PASSWORD);
result.length = length;
- if (quality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
- result.seqLength = PasswordMetrics.MAX_ALLOWED_SEQUENCE;
- } else if (quality == PASSWORD_QUALITY_ALPHABETIC) {
+ if (quality == PASSWORD_QUALITY_ALPHABETIC) {
result.nonNumeric = 1;
} else if (quality == PASSWORD_QUALITY_ALPHANUMERIC) {
result.numeric = 1;
diff --git a/core/java/android/app/time/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java
index 433b420..895a8e4 100644
--- a/core/java/android/app/time/TimeZoneCapabilities.java
+++ b/core/java/android/app/time/TimeZoneCapabilities.java
@@ -207,6 +207,17 @@
mUserHandle = Objects.requireNonNull(userHandle);
}
+ public Builder(@NonNull TimeZoneCapabilities capabilitiesToCopy) {
+ Objects.requireNonNull(capabilitiesToCopy);
+ mUserHandle = capabilitiesToCopy.mUserHandle;
+ mConfigureAutoDetectionEnabledCapability =
+ capabilitiesToCopy.mConfigureAutoDetectionEnabledCapability;
+ mConfigureGeoDetectionEnabledCapability =
+ capabilitiesToCopy.mConfigureGeoDetectionEnabledCapability;
+ mSuggestManualTimeZoneCapability =
+ capabilitiesToCopy.mSuggestManualTimeZoneCapability;
+ }
+
/** Sets the state for the automatic time zone detection enabled config. */
public Builder setConfigureAutoDetectionEnabledCapability(@CapabilityState int value) {
this.mConfigureAutoDetectionEnabledCapability = value;
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 52016b6..a356230 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -32,6 +32,18 @@
public interface TimeDetector {
/**
+ * The name of the service for shell commands.
+ * @hide
+ */
+ String SHELL_COMMAND_SERVICE_NAME = "time_detector";
+
+ /**
+ * A shell command that prints the current "auto time detection" global setting value.
+ * @hide
+ */
+ String SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED = "is_auto_detection_enabled";
+
+ /**
* A shared utility method to create a {@link ManualTimeSuggestion}.
*
* @hide
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index cda4634..b723140 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -31,6 +31,8 @@
import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -760,4 +762,48 @@
return new UsageStats[size];
}
};
+
+ /** @hide */
+ // This class is used by the mainline test suite, so we have to keep these APIs around across
+ // releases. Consider making this class public to help external developers to write tests as
+ // well.
+ @TestApi
+ public static final class Builder {
+ private final UsageStats mUsageStats = new UsageStats();
+
+ @NonNull
+ public UsageStats build() {
+ return mUsageStats;
+ }
+
+ @NonNull
+ public Builder setPackageName(@Nullable String packageName) {
+ mUsageStats.mPackageName = packageName;
+ return this;
+ }
+
+ @NonNull
+ public Builder setFirstTimeStamp(long firstTimeStamp) {
+ mUsageStats.mBeginTimeStamp = firstTimeStamp;
+ return this;
+ }
+
+ @NonNull
+ public Builder setLastTimeStamp(long lastTimeStamp) {
+ mUsageStats.mEndTimeStamp = lastTimeStamp;
+ return this;
+ }
+
+ @NonNull
+ public Builder setTotalTimeInForeground(long totalTimeInForeground) {
+ mUsageStats.mTotalTimeInForeground = totalTimeInForeground;
+ return this;
+ }
+
+ @NonNull
+ public Builder setLastTimeUsed(long lastTimeUsed) {
+ mUsageStats.mLastTimeUsed = lastTimeUsed;
+ return this;
+ }
+ }
}
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index a72877e..fe81df0 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -320,6 +320,15 @@
}
/**
+ * Set the host's interaction handler.
+ *
+ * @hide
+ */
+ public void setInteractionHandler(InteractionHandler interactionHandler) {
+ mInteractionHandler = interactionHandler;
+ }
+
+ /**
* Gets a list of all the appWidgetIds that are bound to the current host
*/
public int[] getAppWidgetIds() {
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 16413e1..0d21e09 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -20,9 +20,14 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -69,10 +74,10 @@
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
@@ -90,10 +95,10 @@
*
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PLAYING_STATE_CHANGED =
"android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
@@ -112,11 +117,11 @@
* be null if no device is active. </li>
* </ul>
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
- *
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@UnsupportedAppUsage(trackingBug = 171933273)
public static final String ACTION_ACTIVE_DEVICE_CHANGED =
@@ -133,11 +138,11 @@
* connected, otherwise it is not included.</li>
* </ul>
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
- *
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@UnsupportedAppUsage(trackingBug = 181103983)
public static final String ACTION_CODEC_CONFIG_CHANGED =
@@ -307,7 +312,9 @@
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@UnsupportedAppUsage
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
@@ -347,7 +354,9 @@
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@UnsupportedAppUsage
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
@@ -368,6 +377,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
try {
@@ -387,6 +398,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
try {
@@ -406,6 +419,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @BtProfileState int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
try {
@@ -441,7 +456,9 @@
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@UnsupportedAppUsage(trackingBug = 171933273)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
@@ -468,7 +485,9 @@
*/
@UnsupportedAppUsage(trackingBug = 171933273)
@Nullable
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getActiveDevice() {
if (VDBG) log("getActiveDevice()");
try {
@@ -495,7 +514,11 @@
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -514,7 +537,11 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -546,7 +573,9 @@
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
@@ -599,6 +628,7 @@
* @return true if device supports absolute volume
* @hide
*/
+ @RequiresNoPermission
public boolean isAvrcpAbsoluteVolumeSupported() {
if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
try {
@@ -620,6 +650,8 @@
* @param volume Absolute volume to be set on AVRCP side
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setAvrcpAbsoluteVolume(int volume) {
if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
try {
@@ -636,10 +668,11 @@
/**
* Check if A2DP profile is streaming music.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device BluetoothDevice device
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isA2dpPlaying(BluetoothDevice device) {
try {
final IBluetoothA2dp service = getService();
@@ -662,6 +695,8 @@
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean shouldSendVolumeKeys(BluetoothDevice device) {
if (isEnabled() && isValidDevice(device)) {
ParcelUuid[] uuids = device.getUuids();
@@ -686,7 +721,9 @@
*/
@UnsupportedAppUsage(trackingBug = 181103983)
@Nullable
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
verifyDeviceNotNull(device, "getCodecStatus");
@@ -714,7 +751,9 @@
* @hide
*/
@UnsupportedAppUsage(trackingBug = 181103983)
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setCodecConfigPreference(@NonNull BluetoothDevice device,
@NonNull BluetoothCodecConfig codecConfig) {
if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
@@ -744,7 +783,9 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void enableOptionalCodecs(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
verifyDeviceNotNull(device, "enableOptionalCodecs");
@@ -759,7 +800,9 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void disableOptionalCodecs(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
verifyDeviceNotNull(device, "disableOptionalCodecs");
@@ -773,6 +816,7 @@
* active A2DP Bluetooth device.
* @param enable if true, enable the optional codecs, other disable them
*/
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
try {
final IBluetoothA2dp service = getService();
@@ -800,7 +844,9 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@OptionalCodecsSupportStatus
public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) {
verifyDeviceNotNull(device, "isOptionalCodecsSupported");
@@ -826,7 +872,9 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@OptionalCodecsPreferenceStatus
public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) {
verifyDeviceNotNull(device, "isOptionalCodecsEnabled");
@@ -853,7 +901,9 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device,
@OptionalCodecsPreferenceStatus int value) {
verifyDeviceNotNull(device, "setOptionalCodecsEnabled");
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index 67f3d7b..280e8bc 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -21,6 +21,9 @@
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Binder;
@@ -160,7 +163,9 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothA2dpSink service = getService();
@@ -182,6 +187,8 @@
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothA2dpSink service = getService();
@@ -203,6 +210,8 @@
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothA2dpSink service = getService();
@@ -224,6 +233,8 @@
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothA2dpSink service = getService();
@@ -243,8 +254,6 @@
* Get the current audio configuration for the A2DP source device,
* or null if the device has no audio configuration
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Remote bluetooth device.
* @return audio configuration for the device, or null
*
@@ -252,6 +261,9 @@
*
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
if (VDBG) log("getAudioConfig(" + device + ")");
final IBluetoothA2dpSink service = getService();
@@ -278,7 +290,11 @@
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -297,7 +313,11 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 79fd807..052a773 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -17,19 +17,26 @@
package android.bluetooth;
-import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.app.PropertyInvalidatedCache;
import android.bluetooth.BluetoothDevice.Transport;
import android.bluetooth.BluetoothProfile.ConnectionPolicy;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
+import android.bluetooth.annotations.RequiresBluetoothScanPermission;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.PeriodicAdvertisingManager;
@@ -98,11 +105,6 @@
* Bluetooth LE devices with {@link #startLeScan(LeScanCallback callback)}.
* </p>
* <p>This class is thread safe.</p>
- * <p class="note"><strong>Note:</strong>
- * Most methods require the {@link android.Manifest.permission#BLUETOOTH}
- * permission and some also require the
- * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- * </p>
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>
@@ -144,8 +146,8 @@
* <p>Always contains the extra fields {@link #EXTRA_STATE} and {@link
* #EXTRA_PREVIOUS_STATE} containing the new and old states
* respectively.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED";
@@ -278,8 +280,10 @@
* <p>Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED}
* for global notification whenever the scan mode changes. For example, an
* application can be notified when the device has ended discoverability.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
@@ -305,8 +309,10 @@
* has rejected the request or an error has occurred.
* <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
* for global notification whenever Bluetooth is turned on or off.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE";
@@ -325,10 +331,12 @@
* has rejected the request or an error has occurred.
* <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
* for global notification whenever Bluetooth is turned on or off.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE";
@@ -355,8 +363,10 @@
* <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link
* #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes
* respectively.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
@@ -508,15 +518,19 @@
* progress, and existing connections will experience limited bandwidth
* and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
* discovery.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
/**
* Broadcast Action: The local Bluetooth adapter has finished the device
* discovery process.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
@@ -526,8 +540,10 @@
* <p>This name is visible to remote Bluetooth devices.
* <p>Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing
* the name.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
/**
@@ -559,9 +575,10 @@
* {@link #EXTRA_CONNECTION_STATE} or {@link #EXTRA_PREVIOUS_CONNECTION_STATE}
* can be any of {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
@@ -700,6 +717,7 @@
* Bluetooth metadata listener. Overrides the default BluetoothMetadataListener
* implementation.
*/
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private static final IBluetoothMetadataListener sBluetoothMetadataListener =
new IBluetoothMetadataListener.Stub() {
@Override
@@ -731,6 +749,7 @@
* @return the default local adapter, or null if Bluetooth is not supported on this hardware
* platform
*/
+ @RequiresNoPermission
public static synchronized BluetoothAdapter getDefaultAdapter() {
if (sAdapter == null) {
IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
@@ -776,6 +795,7 @@
* @param address valid Bluetooth MAC address
* @throws IllegalArgumentException if address is invalid
*/
+ @RequiresNoPermission
public BluetoothDevice getRemoteDevice(String address) {
return new BluetoothDevice(address);
}
@@ -791,6 +811,7 @@
* @param address Bluetooth MAC address (6 bytes)
* @throws IllegalArgumentException if address is invalid
*/
+ @RequiresNoPermission
public BluetoothDevice getRemoteDevice(byte[] address) {
if (address == null || address.length != 6) {
throw new IllegalArgumentException("Bluetooth address must have 6 bytes");
@@ -808,6 +829,7 @@
* Use {@link #isMultipleAdvertisementSupported()} to check whether LE Advertising is supported
* on this device before calling this method.
*/
+ @RequiresNoPermission
public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
if (!getLeAccess()) {
return null;
@@ -830,6 +852,7 @@
*
* @hide
*/
+ @RequiresNoPermission
public PeriodicAdvertisingManager getPeriodicAdvertisingManager() {
if (!getLeAccess()) {
return null;
@@ -850,6 +873,7 @@
/**
* Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
*/
+ @RequiresNoPermission
public BluetoothLeScanner getBluetoothLeScanner() {
if (!getLeAccess()) {
return null;
@@ -870,7 +894,8 @@
*
* @return true if the local adapter is turned on
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isEnabled() {
return getState() == BluetoothAdapter.STATE_ON;
}
@@ -884,6 +909,7 @@
* @hide
*/
@SystemApi
+ @RequiresNoPermission
public boolean isLeEnabled() {
final int state = getLeState();
if (DBG) {
@@ -921,6 +947,8 @@
* @hide
*/
@SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disableBLE() {
if (!isBleScanAlwaysAvailable()) {
return false;
@@ -966,6 +994,8 @@
* @hide
*/
@SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean enableBLE() {
if (!isBleScanAlwaysAvailable()) {
return false;
@@ -986,6 +1016,7 @@
new PropertyInvalidatedCache<Void, Integer>(
8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) {
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
protected Integer recompute(Void query) {
try {
return mService.getState();
@@ -996,6 +1027,7 @@
};
/** @hide */
+ @RequiresNoPermission
public void disableBluetoothGetStateCache() {
mBluetoothGetStateCache.disableLocal();
}
@@ -1039,7 +1071,8 @@
*
* @return current state of Bluetooth adapter
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
@AdapterState
public int getState() {
int state = getStateInternal();
@@ -1075,7 +1108,8 @@
* @return current state of Bluetooth adapter
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
@AdapterState
@UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine "
+ "whether you can use BLE & BT classic.")
@@ -1122,7 +1156,9 @@
*
* @return true to indicate adapter startup has begun, or false on immediate error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean enable() {
if (isEnabled()) {
if (DBG) {
@@ -1159,7 +1195,9 @@
*
* @return true to indicate adapter shutdown has begun, or false on immediate error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disable() {
try {
return mManagerService.disable(ActivityThread.currentPackageName(), true);
@@ -1172,13 +1210,13 @@
/**
* Turn off the local Bluetooth adapter and don't persist the setting.
*
- * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission
- *
* @return true to indicate adapter shutdown has begun, or false on immediate error
* @hide
*/
@UnsupportedAppUsage(trackingBug = 171933273)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disable(boolean persist) {
try {
@@ -1195,7 +1233,12 @@
*
* @return Bluetooth hardware address as string
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.LOCAL_MAC_ADDRESS,
+ })
public String getAddress() {
try {
return mManagerService.getAddress();
@@ -1208,10 +1251,12 @@
/**
* Get the friendly Bluetooth name of the local Bluetooth adapter.
* <p>This name is visible to remote Bluetooth devices.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @return the Bluetooth name, or null on error
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public String getName() {
try {
return mManagerService.getName();
@@ -1221,6 +1266,18 @@
return null;
}
+ /** {@hide} */
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
+ public int getNameLengthForAdvertise() {
+ try {
+ return mService.getNameLengthForAdvertise();
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ return -1;
+ }
+
/**
* Factory reset bluetooth settings.
*
@@ -1228,7 +1285,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean factoryReset() {
try {
mServiceLock.readLock().lock();
@@ -1253,7 +1310,9 @@
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @Nullable ParcelUuid[] getUuids() {
if (getState() != STATE_ON) {
return null;
@@ -1285,7 +1344,9 @@
* @param name a valid Bluetooth name
* @return true if the name was set, false otherwise
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setName(String name) {
if (getState() != STATE_ON) {
return false;
@@ -1311,7 +1372,9 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothClass getBluetoothClass() {
if (getState() != STATE_ON) {
return null;
@@ -1340,7 +1403,7 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setBluetoothClass(BluetoothClass bluetoothClass) {
if (getState() != STATE_ON) {
return false;
@@ -1367,7 +1430,9 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@IoCapability
public int getIoCapability() {
if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
@@ -1395,7 +1460,7 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setIoCapability(@IoCapability int capability) {
if (getState() != STATE_ON) return false;
try {
@@ -1418,7 +1483,9 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@IoCapability
public int getLeIoCapability() {
if (getState() != STATE_ON) return BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
@@ -1446,7 +1513,7 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setLeIoCapability(@IoCapability int capability) {
if (getState() != STATE_ON) return false;
try {
@@ -1475,7 +1542,9 @@
*
* @return scan mode
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@ScanMode
public int getScanMode() {
if (getState() != STATE_ON) {
@@ -1522,7 +1591,9 @@
*/
@UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which "
+ "shows UI that confirms the user wants to go into discoverable mode.")
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean setScanMode(@ScanMode int mode, long durationMillis) {
if (getState() != STATE_ON) {
return false;
@@ -1571,7 +1642,9 @@
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean setScanMode(@ScanMode int mode) {
if (getState() != STATE_ON) {
return false;
@@ -1591,6 +1664,8 @@
/** @hide */
@UnsupportedAppUsage
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public int getDiscoverableTimeout() {
if (getState() != STATE_ON) {
return -1;
@@ -1610,6 +1685,8 @@
/** @hide */
@UnsupportedAppUsage
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void setDiscoverableTimeout(int timeout) {
if (getState() != STATE_ON) {
return;
@@ -1635,7 +1712,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public long getDiscoveryEndMillis() {
try {
mServiceLock.readLock().lock();
@@ -1654,6 +1731,7 @@
* Set the context for this BluetoothAdapter (only called from BluetoothManager)
* @hide
*/
+ @RequiresNoPermission
public void setContext(Context context) {
mContext = context;
}
@@ -1703,7 +1781,10 @@
*
* @return true on success, false on error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean startDiscovery() {
if (getState() != STATE_ON) {
return false;
@@ -1737,7 +1818,9 @@
*
* @return true on success, false on error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean cancelDiscovery() {
if (getState() != STATE_ON) {
return false;
@@ -1773,7 +1856,9 @@
*
* @return true if discovering
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean isDiscovering() {
if (getState() != STATE_ON) {
return false;
@@ -1805,7 +1890,12 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean removeActiveDevice(@ActiveDeviceUse int profiles) {
if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
&& profiles != ACTIVE_DEVICE_ALL) {
@@ -1845,7 +1935,12 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean setActiveDevice(@NonNull BluetoothDevice device,
@ActiveDeviceUse int profiles) {
if (device == null) {
@@ -1889,7 +1984,12 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) {
try {
mServiceLock.readLock().lock();
@@ -1917,7 +2017,11 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) {
try {
mServiceLock.readLock().lock();
@@ -1938,6 +2042,8 @@
*
* @return true if Multiple Advertisement feature is supported
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isMultipleAdvertisementSupported() {
if (getState() != STATE_ON) {
return false;
@@ -1966,6 +2072,7 @@
* @hide
*/
@SystemApi
+ @RequiresNoPermission
public boolean isBleScanAlwaysAvailable() {
try {
return mManagerService.isBleScanAlwaysAvailable();
@@ -1981,6 +2088,7 @@
new PropertyInvalidatedCache<Void, Boolean>(
8, BLUETOOTH_FILTERING_CACHE_PROPERTY) {
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
protected Boolean recompute(Void query) {
try {
mServiceLock.readLock().lock();
@@ -1998,6 +2106,7 @@
};
/** @hide */
+ @RequiresNoPermission
public void disableIsOffloadedFilteringSupportedCache() {
mBluetoothFilteringCache.disableLocal();
}
@@ -2012,6 +2121,8 @@
*
* @return true if chipset supports on-chip filtering
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isOffloadedFilteringSupported() {
if (!getLeAccess()) {
return false;
@@ -2024,6 +2135,8 @@
*
* @return true if chipset supports on-chip scan batching
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isOffloadedScanBatchingSupported() {
if (!getLeAccess()) {
return false;
@@ -2046,6 +2159,8 @@
*
* @return true if chipset supports LE 2M PHY feature
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isLe2MPhySupported() {
if (!getLeAccess()) {
return false;
@@ -2068,6 +2183,8 @@
*
* @return true if chipset supports LE Coded PHY feature
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isLeCodedPhySupported() {
if (!getLeAccess()) {
return false;
@@ -2090,6 +2207,8 @@
*
* @return true if chipset supports LE Extended Advertising feature
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isLeExtendedAdvertisingSupported() {
if (!getLeAccess()) {
return false;
@@ -2112,6 +2231,8 @@
*
* @return true if chipset supports LE Periodic Advertising feature
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public boolean isLePeriodicAdvertisingSupported() {
if (!getLeAccess()) {
return false;
@@ -2135,6 +2256,8 @@
*
* @return the maximum LE advertising data length.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public int getLeMaximumAdvertisingDataLength() {
if (!getLeAccess()) {
return 0;
@@ -2157,6 +2280,7 @@
*
* @return true if phone supports Hearing Aid Profile
*/
+ @RequiresNoPermission
private boolean isHearingAidProfileSupported() {
try {
return mManagerService.isHearingAidProfileSupported();
@@ -2172,7 +2296,9 @@
* @return the maximum number of connected audio devices
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getMaxConnectedAudioDevices() {
try {
mServiceLock.readLock().lock();
@@ -2193,6 +2319,8 @@
* @return true if there are hw entries available for matching beacons
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isHardwareTrackingFiltersAvailable() {
if (!getLeAccess()) {
return false;
@@ -2223,6 +2351,7 @@
* instead.
*/
@Deprecated
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) {
SynchronousResultReceiver receiver = new SynchronousResultReceiver();
requestControllerActivityEnergyInfo(receiver);
@@ -2248,6 +2377,7 @@
* @param result The callback to which to send the activity info.
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public void requestControllerActivityEnergyInfo(ResultReceiver result) {
try {
mServiceLock.readLock().lock();
@@ -2275,7 +2405,9 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getMostRecentlyConnectedDevices() {
if (getState() != STATE_ON) {
return new ArrayList<>();
@@ -2303,7 +2435,9 @@
*
* @return unmodifiable set of {@link BluetoothDevice}, or null on error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public Set<BluetoothDevice> getBondedDevices() {
if (getState() != STATE_ON) {
return toDeviceSet(new BluetoothDevice[0]);
@@ -2332,6 +2466,7 @@
* BluetoothProfile}.
* @hide
*/
+ @RequiresNoPermission
public @NonNull List<Integer> getSupportedProfiles() {
final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>();
@@ -2368,6 +2503,7 @@
* This method must not be called when mService is null.
*/
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
protected Integer recompute(Void query) {
try {
return mService.getAdapterConnectionState();
@@ -2378,6 +2514,7 @@
};
/** @hide */
+ @RequiresNoPermission
public void disableGetAdapterConnectionStateCache() {
mBluetoothGetAdapterConnectionStateCache.disableLocal();
}
@@ -2401,6 +2538,8 @@
* @hide
*/
@UnsupportedAppUsage
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public int getConnectionState() {
if (getState() != STATE_ON) {
return BluetoothAdapter.STATE_DISCONNECTED;
@@ -2429,6 +2568,7 @@
new PropertyInvalidatedCache<Integer, Integer>(
8, BLUETOOTH_PROFILE_CACHE_PROPERTY) {
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
protected Integer recompute(Integer query) {
try {
mServiceLock.readLock().lock();
@@ -2450,6 +2590,7 @@
};
/** @hide */
+ @RequiresNoPermission
public void disableGetProfileConnectionStateCache() {
mGetProfileConnectionStateCache.disableLocal();
}
@@ -2471,7 +2612,10 @@
* {@link BluetoothProfile#STATE_CONNECTED},
* {@link BluetoothProfile#STATE_DISCONNECTING}
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public int getProfileConnectionState(int profile) {
if (getState() != STATE_ON) {
return BluetoothProfile.STATE_DISCONNECTED;
@@ -2486,7 +2630,6 @@
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
* connections from a listening {@link BluetoothServerSocket}.
* <p>Valid RFCOMM channels are in range 1 to 30.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param channel RFCOMM channel to listen on
* @return a listening RFCOMM BluetoothServerSocket
@@ -2494,6 +2637,9 @@
* permissions, or channel in use.
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
return listenUsingRfcommOn(channel, false, false);
}
@@ -2505,7 +2651,6 @@
* <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
* connections from a listening {@link BluetoothServerSocket}.
* <p>Valid RFCOMM channels are in range 1 to 30.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
* <p>To auto assign a channel without creating a SDP record use
* {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number.
*
@@ -2519,6 +2664,9 @@
* @hide
*/
@UnsupportedAppUsage
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm,
boolean min16DigitPin) throws IOException {
BluetoothServerSocket socket =
@@ -2559,7 +2707,9 @@
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions, or channel in use.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid)
throws IOException {
return createNewRfcommSocketAndRecord(name, uuid, true, true);
@@ -2591,7 +2741,9 @@
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions, or channel in use.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid)
throws IOException {
return createNewRfcommSocketAndRecord(name, uuid, false, false);
@@ -2622,7 +2774,6 @@
* closed, or if this application closes unexpectedly.
* <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
* connect to this socket from another device using the same {@link UUID}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param name service name for SDP record
* @param uuid uuid for SDP record
@@ -2632,12 +2783,16 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid)
throws IOException {
return createNewRfcommSocketAndRecord(name, uuid, false, true);
}
-
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid,
boolean auth, boolean encrypt) throws IOException {
BluetoothServerSocket socket;
@@ -2663,6 +2818,8 @@
* permissions.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
BluetoothServerSocket socket =
new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, false, port);
@@ -2694,6 +2851,8 @@
* permissions.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin)
throws IOException {
BluetoothServerSocket socket =
@@ -2726,11 +2885,12 @@
* permissions.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException {
return listenUsingL2capOn(port, false, false);
}
-
/**
* Construct an insecure L2CAP server socket.
* Call #accept to retrieve connections to this socket.
@@ -2743,6 +2903,8 @@
* permissions.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
Log.d(TAG, "listenUsingInsecureL2capOn: port=" + port);
BluetoothServerSocket socket =
@@ -2769,11 +2931,14 @@
/**
* Read the local Out of Band Pairing Data
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @return Pair<byte[], byte[]> of Hash and Randomizer
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public Pair<byte[], byte[]> readOutOfBandData() {
return null;
}
@@ -2794,6 +2959,10 @@
* BluetoothProfile#HEARING_AID} or {@link BluetoothProfile#GATT_SERVER}.
* @return true on success, false on error
*/
+ @SuppressLint({
+ "AndroidFrameworkRequiresPermission",
+ "AndroidFrameworkBluetoothPermission"
+ })
public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
int profile) {
if (context == null || listener == null) {
@@ -2863,6 +3032,10 @@
* @param profile
* @param proxy Profile proxy object
*/
+ @SuppressLint({
+ "AndroidFrameworkRequiresPermission",
+ "AndroidFrameworkBluetoothPermission"
+ })
public void closeProfileProxy(int profile, BluetoothProfile proxy) {
if (proxy == null) {
return;
@@ -2935,8 +3108,10 @@
}
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothManagerCallback mManagerCallback =
new IBluetoothManagerCallback.Stub() {
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onBluetoothServiceUp(IBluetooth bluetoothService) {
if (DBG) {
Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
@@ -3031,7 +3206,9 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean enableNoAutoConnect() {
if (isEnabled()) {
if (DBG) {
@@ -3095,8 +3272,6 @@
*
* @param transport - whether the {@link OobData} is generated for LE or Classic.
* @param oobData - data generated in the host stack(LE) or controller (Classic)
- *
- * @hide
*/
void onOobData(@Transport int transport, @Nullable OobData oobData);
@@ -3104,8 +3279,6 @@
* Provides feedback when things don't go as expected.
*
* @param errorCode - the code descibing the type of error that occurred.
- *
- * @hide
*/
void onError(@OobError int errorCode);
}
@@ -3188,7 +3361,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public void generateLocalOobData(@Transport int transport,
@NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) {
if (transport != BluetoothDevice.TRANSPORT_BREDR && transport
@@ -3232,12 +3405,14 @@
* reason. If Bluetooth is already on and if this function is called to turn
* it on, the api will return true and a callback will be called.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
- *
* @param on True for on, false for off.
* @param callback The callback to notify changes to the state.
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public boolean changeApplicationBluetoothState(boolean on,
BluetoothStateChangeCallback callback) {
return false;
@@ -3447,7 +3622,10 @@
* instead.
*/
@Deprecated
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean startLeScan(LeScanCallback callback) {
return startLeScan(null, callback);
}
@@ -3466,7 +3644,10 @@
* instead.
*/
@Deprecated
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {
if (DBG) {
Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids));
@@ -3500,6 +3681,7 @@
return false;
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
ScanCallback scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
@@ -3563,7 +3745,9 @@
* @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead.
*/
@Deprecated
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void stopLeScan(LeScanCallback callback) {
if (DBG) {
Log.d(TAG, "stopLeScan()");
@@ -3604,7 +3788,9 @@
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions, or unable to start this CoC
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull BluetoothServerSocket listenUsingL2capChannel()
throws IOException {
BluetoothServerSocket socket =
@@ -3650,7 +3836,9 @@
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions, or unable to start this CoC
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull BluetoothServerSocket listenUsingInsecureL2capChannel()
throws IOException {
BluetoothServerSocket socket =
@@ -3695,7 +3883,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device,
@NonNull Executor executor, @NonNull OnMetadataChangedListener listener) {
if (DBG) Log.d(TAG, "addOnMetadataChangedListener()");
@@ -3768,7 +3956,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device,
@NonNull OnMetadataChangedListener listener) {
if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()");
@@ -3824,6 +4012,7 @@
@Nullable byte[] value);
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothConnectionCallback mConnectionCallback =
new IBluetoothConnectionCallback.Stub() {
@Override
@@ -3857,6 +4046,7 @@
* @throws IllegalArgumentException if the callback is already registered
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean registerBluetoothConnectionCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull BluetoothConnectionCallback callback) {
if (DBG) Log.d(TAG, "registerBluetoothConnectionCallback()");
@@ -3899,6 +4089,7 @@
* @return true if the callback was unregistered successfully, false otherwise
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean unregisterBluetoothConnectionCallback(
@NonNull BluetoothConnectionCallback callback) {
if (DBG) Log.d(TAG, "unregisterBluetoothConnectionCallback()");
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index 4e7e441..5148d5b 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -16,6 +16,10 @@
package android.bluetooth;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -54,10 +58,10 @@
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
@@ -113,6 +117,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothAvrcpController service =
@@ -133,6 +139,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothAvrcpController service =
@@ -153,6 +161,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothAvrcpController service =
@@ -174,6 +184,8 @@
*
* @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getPlayerSettings");
BluetoothAvrcpPlayerSettings settings = null;
@@ -194,6 +206,8 @@
* Sets the player app setting for current player.
* returns true in case setting is supported by remote, false otherwise
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
final IBluetoothAvrcpController service =
@@ -214,6 +228,8 @@
* Send Group Navigation Command to Remote.
* possible keycode values: next_grp, previous_grp defined above
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
+ keyState);
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 0c208fd..1201663 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -16,7 +16,6 @@
package android.bluetooth;
-import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -26,6 +25,11 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.PropertyInvalidatedCache;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
+import android.bluetooth.annotations.RequiresBluetoothScanPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.companion.AssociationRequest;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -66,9 +70,6 @@
* {@link #createRfcommSocketToServiceRecord(UUID)} over Bluetooth BR/EDR or using
* {@link #createL2capChannel(int)} over Bluetooth LE.
*
- * <p class="note"><strong>Note:</strong>
- * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>
@@ -108,10 +109,12 @@
* <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
* #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or
* {@link #EXTRA_RSSI} if they are available.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} and
- * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to receive.
*/
// TODO: Change API to not broadcast RSSI if not available (incoming connection)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_FOUND =
"android.bluetooth.device.action.FOUND";
@@ -120,9 +123,11 @@
* Broadcast Action: Bluetooth class of a remote device has changed.
* <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
* #EXTRA_CLASS}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
* {@see BluetoothClass}
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CLASS_CHANGED =
"android.bluetooth.device.action.CLASS_CHANGED";
@@ -133,8 +138,10 @@
* <p>Always contains the extra field {@link #EXTRA_DEVICE}.
* <p>ACL connections are managed automatically by the Android Bluetooth
* stack.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_ACL_CONNECTED =
"android.bluetooth.device.action.ACL_CONNECTED";
@@ -146,8 +153,10 @@
* this intent as a hint to immediately terminate higher level connections
* (RFCOMM, L2CAP, or profile connections) to the remote device.
* <p>Always contains the extra field {@link #EXTRA_DEVICE}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_ACL_DISCONNECT_REQUESTED =
"android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED";
@@ -158,8 +167,10 @@
* <p>Always contains the extra field {@link #EXTRA_DEVICE}.
* <p>ACL connections are managed automatically by the Android Bluetooth
* stack.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_ACL_DISCONNECTED =
"android.bluetooth.device.action.ACL_DISCONNECTED";
@@ -169,8 +180,10 @@
* been retrieved for the first time, or changed since the last retrieval.
* <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
* #EXTRA_NAME}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_NAME_CHANGED =
"android.bluetooth.device.action.NAME_CHANGED";
@@ -179,9 +192,11 @@
* Broadcast Action: Indicates the alias of a remote device has been
* changed.
* <p>Always contains the extra field {@link #EXTRA_DEVICE}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
@SuppressLint("ActionValue")
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_ALIAS_CHANGED =
"android.bluetooth.device.action.ALIAS_CHANGED";
@@ -191,10 +206,12 @@
* device. For example, if a device is bonded (paired).
* <p>Always contains the extra fields {@link #EXTRA_DEVICE}, {@link
* #EXTRA_BOND_STATE} and {@link #EXTRA_PREVIOUS_BOND_STATE}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
// Note: When EXTRA_BOND_STATE is BOND_NONE then this will also
// contain a hidden extra field EXTRA_REASON with the result code.
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_BOND_STATE_CHANGED =
"android.bluetooth.device.action.BOND_STATE_CHANGED";
@@ -204,10 +221,12 @@
* been retrieved for the first time, or changed since the last retrieval
* <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
* #EXTRA_BATTERY_LEVEL}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_BATTERY_LEVEL_CHANGED =
"android.bluetooth.device.action.BATTERY_LEVEL_CHANGED";
@@ -642,8 +661,10 @@
* device are requested to be fetched using Service Discovery Protocol
* <p> Always contains the extra field {@link #EXTRA_DEVICE}
* <p> Always contains the extra field {@link #EXTRA_UUID}
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} to receive.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_UUID =
"android.bluetooth.device.action.UUID";
@@ -657,20 +678,23 @@
* Broadcast Action: Indicates a failure to retrieve the name of a remote
* device.
* <p>Always contains the extra field {@link #EXTRA_DEVICE}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*
* @hide
*/
//TODO: is this actually useful?
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_NAME_FAILED =
"android.bluetooth.device.action.NAME_FAILED";
/**
* Broadcast Action: This intent is used to broadcast PAIRING REQUEST
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} to
- * receive.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PAIRING_REQUEST =
"android.bluetooth.device.action.PAIRING_REQUEST";
@@ -1206,7 +1230,9 @@
*
* @return the Bluetooth name, or null if there was a problem.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public String getName() {
final IBluetooth service = sService;
if (service == null) {
@@ -1235,7 +1261,9 @@
* @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE} {@link
* #DEVICE_TYPE_DUAL}. {@link #DEVICE_TYPE_UNKNOWN} if it's not available
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getType() {
final IBluetooth service = sService;
if (service == null) {
@@ -1257,7 +1285,9 @@
* null if there was a problem
*/
@Nullable
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public String getAlias() {
final IBluetooth service = sService;
if (service == null) {
@@ -1293,7 +1323,9 @@
* @return {@code true} if the alias is successfully set, {@code false} on error
* @throws IllegalArgumentException if the alias is {@code null} or the empty string
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setAlias(@NonNull String alias) {
if (alias == null || alias.isEmpty()) {
throw new IllegalArgumentException("Cannot set the alias to null or the empty string");
@@ -1321,7 +1353,9 @@
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getBatteryLevel() {
final IBluetooth service = sService;
if (service == null) {
@@ -1346,7 +1380,9 @@
*
* @return false on immediate error, true if bonding will begin
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean createBond() {
return createBond(TRANSPORT_AUTO);
}
@@ -1367,7 +1403,9 @@
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean createBond(int transport) {
return createBondInternal(transport, null, null);
}
@@ -1395,7 +1433,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean createBondOutOfBand(int transport, @Nullable OobData remoteP192Data,
@Nullable OobData remoteP256Data) {
if (remoteP192Data == null && remoteP256Data == null) {
@@ -1406,6 +1444,7 @@
return createBondInternal(transport, remoteP192Data, remoteP256Data);
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private boolean createBondInternal(int transport, @Nullable OobData remoteP192Data,
@Nullable OobData remoteP256Data) {
final IBluetooth service = sService;
@@ -1430,7 +1469,9 @@
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isBondingInitiatedLocally() {
final IBluetooth service = sService;
if (service == null) {
@@ -1452,7 +1493,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean cancelBondProcess() {
final IBluetooth service = sService;
if (service == null) {
@@ -1480,7 +1521,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean removeBond() {
final IBluetooth service = sService;
if (service == null) {
@@ -1504,6 +1545,7 @@
new PropertyInvalidatedCache<BluetoothDevice, Integer>(
8, BLUETOOTH_BONDING_CACHE_PROPERTY) {
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
protected Integer recompute(BluetoothDevice query) {
try {
return sService.getBondState(query);
@@ -1532,7 +1574,10 @@
*
* @return the bond state
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public int getBondState() {
final IBluetooth service = sService;
if (service == null) {
@@ -1560,7 +1605,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean canBondWithoutDialog() {
final IBluetooth service = sService;
if (service == null) {
@@ -1583,7 +1628,9 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isConnected() {
final IBluetooth service = sService;
if (service == null) {
@@ -1606,7 +1653,9 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isEncrypted() {
final IBluetooth service = sService;
if (service == null) {
@@ -1626,7 +1675,9 @@
*
* @return Bluetooth class object, or null on error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothClass getBluetoothClass() {
final IBluetooth service = sService;
if (service == null) {
@@ -1653,7 +1704,9 @@
*
* @return the supported features (UUIDs) of the remote device, or null on error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public ParcelUuid[] getUuids() {
final IBluetooth service = sService;
if (service == null || !isBluetoothEnabled()) {
@@ -1681,7 +1734,9 @@
* @return False if the check fails, True if the process of initiating an ACL connection
* to the remote device was started.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean fetchUuidsWithSdp() {
final IBluetooth service = sService;
if (service == null || !isBluetoothEnabled()) {
@@ -1707,8 +1762,7 @@
* {@link #EXTRA_SDP_SEARCH_STATUS} different from 0.
* Detailed status error codes can be found by members of the Bluetooth package in
* the AbstractionLayer class.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
- * The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}.
+ * <p>The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}.
* The object type will match one of the SdpXxxRecord types, depending on the UUID searched
* for.
*
@@ -1717,6 +1771,9 @@
* was started.
*/
/** @hide */
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sdpSearch(ParcelUuid uuid) {
final IBluetooth service = sService;
if (service == null) {
@@ -1733,10 +1790,12 @@
/**
* Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
* @return true pin has been set false for error
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setPin(byte[] pin) {
final IBluetooth service = sService;
if (service == null) {
@@ -1758,7 +1817,9 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setPin(@NonNull String pin) {
byte[] pinBytes = convertPinToBytes(pin);
if (pinBytes == null) {
@@ -1772,7 +1833,7 @@
*
* @return true confirmation has been sent out false for error
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setPairingConfirmation(boolean confirm) {
final IBluetooth service = sService;
if (service == null) {
@@ -1795,7 +1856,9 @@
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean cancelPairing() {
final IBluetooth service = sService;
if (service == null) {
@@ -1827,7 +1890,9 @@
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @AccessPermission int getPhonebookAccessPermission() {
final IBluetooth service = sService;
if (service == null) {
@@ -1859,8 +1924,6 @@
* If the {@link BluetoothDevice} is not connected with A2DP or HFP, it cannot
* enter silence mode.
*
- * <p> Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
- *
* @param silence true to enter silence mode, false to exit
* @return true on success, false on error.
* @throws IllegalStateException if Bluetooth is not turned ON.
@@ -1884,8 +1947,6 @@
/**
* Check whether the {@link BluetoothDevice} is in silence mode
*
- * <p> Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
- *
* @return true on device in silence mode, otherwise false.
* @throws IllegalStateException if Bluetooth is not turned ON.
* @hide
@@ -1935,7 +1996,9 @@
* @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @AccessPermission int getMessageAccessPermission() {
final IBluetooth service = sService;
if (service == null) {
@@ -1959,7 +2022,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setMessageAccessPermission(@AccessPermission int value) {
// Validates param value is one of the accepted constants
if (value != ACCESS_ALLOWED && value != ACCESS_REJECTED && value != ACCESS_UNKNOWN) {
@@ -1984,7 +2047,9 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @AccessPermission int getSimAccessPermission() {
final IBluetooth service = sService;
if (service == null) {
@@ -2008,7 +2073,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setSimAccessPermission(int value) {
final IBluetooth service = sService;
if (service == null) {
@@ -2039,7 +2104,6 @@
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
* connection.
* <p>Valid RFCOMM channels are in range 1 to 30.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param channel RFCOMM channel to connect to
* @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
@@ -2048,6 +2112,10 @@
* @hide
*/
@UnsupportedAppUsage
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createRfcommSocket(int channel) throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "Bluetooth is not enabled");
@@ -2074,7 +2142,6 @@
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
* connection.
* <p>Valid L2CAP PSM channels are in range 1 to 2^16.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param channel L2cap PSM/channel to connect to
* @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
@@ -2082,6 +2149,10 @@
* permissions
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createL2capSocket(int channel) throws IOException {
return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, true, true, this, channel,
null);
@@ -2095,7 +2166,6 @@
* <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
* connection.
* <p>Valid L2CAP PSM channels are in range 1 to 2^16.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @param channel L2cap PSM/channel to connect to
* @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
@@ -2103,6 +2173,10 @@
* permissions
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createInsecureL2capSocket(int channel) throws IOException {
return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, false, false, this, channel,
null);
@@ -2138,7 +2212,10 @@
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "Bluetooth is not enabled");
@@ -2176,7 +2253,10 @@
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "Bluetooth is not enabled");
@@ -2192,7 +2272,6 @@
* Call #connect on the returned #BluetoothSocket to begin the connection.
* The remote device will not be authenticated and communication on this
* socket will not be encrypted.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param port remote port
* @return An RFCOMM BluetoothSocket
@@ -2202,6 +2281,10 @@
*/
@UnsupportedAppUsage(publicAlternatives = "Use "
+ "{@link #createInsecureRfcommSocketToServiceRecord} instead.")
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "Bluetooth is not enabled");
@@ -2214,7 +2297,6 @@
/**
* Construct a SCO socket ready to start an outgoing connection.
* Call #connect on the returned #BluetoothSocket to begin the connection.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @return a SCO BluetoothSocket
* @throws IOException on error, for example Bluetooth not available, or insufficient
@@ -2222,6 +2304,10 @@
* @hide
*/
@UnsupportedAppUsage
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public BluetoothSocket createScoSocket() throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "Bluetooth is not enabled");
@@ -2269,6 +2355,8 @@
* automatically connect as soon as the remote device becomes available (true).
* @throws IllegalArgumentException if callback is null
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback) {
return (connectGatt(context, autoConnect, callback, TRANSPORT_AUTO));
@@ -2289,6 +2377,8 @@
* BluetoothDevice#TRANSPORT_LE}
* @throws IllegalArgumentException if callback is null
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback, int transport) {
return (connectGatt(context, autoConnect, callback, transport, PHY_LE_1M_MASK));
@@ -2313,6 +2403,8 @@
* is set to true.
* @throws NullPointerException if callback is null
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback, int transport, int phy) {
return connectGatt(context, autoConnect, callback, transport, phy, null);
@@ -2339,6 +2431,8 @@
* an un-specified background thread.
* @throws NullPointerException if callback is null
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback, int transport, int phy,
Handler handler) {
@@ -2372,6 +2466,8 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback, int transport,
boolean opportunistic, int phy, Handler handler) {
@@ -2416,7 +2512,10 @@
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public @NonNull BluetoothSocket createL2capChannel(int psm) throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "createL2capChannel: Bluetooth is not enabled");
@@ -2444,7 +2543,10 @@
* @throws IOException on error, for example Bluetooth not available, or insufficient
* permissions
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public @NonNull BluetoothSocket createInsecureL2capChannel(int psm) throws IOException {
if (!isBluetoothEnabled()) {
Log.e(TAG, "createInsecureL2capChannel: Bluetooth is not enabled");
@@ -2472,7 +2574,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setMetadata(@MetadataKey int key, @NonNull byte[] value) {
final IBluetooth service = sService;
if (service == null) {
@@ -2500,7 +2602,7 @@
*/
@SystemApi
@Nullable
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public byte[] getMetadata(@MetadataKey int key) {
final IBluetooth service = sService;
if (service == null) {
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 381318b..9d3eed8 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,6 +16,11 @@
package android.bluetooth;
+import android.annotation.RequiresNoPermission;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Handler;
@@ -150,6 +155,7 @@
/**
* Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
*/
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothGattCallback mBluetoothGattCallback =
new IBluetoothGattCallback.Stub() {
/**
@@ -157,6 +163,7 @@
* @hide
*/
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onClientRegistered(int status, int clientIf) {
if (DBG) {
Log.d(TAG, "onClientRegistered() - status=" + status
@@ -347,6 +354,7 @@
* @hide
*/
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onCharacteristicRead(String address, int status, int handle,
byte[] value) {
if (VDBG) {
@@ -404,6 +412,7 @@
* @hide
*/
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onCharacteristicWrite(String address, int status, int handle) {
if (VDBG) {
Log.d(TAG, "onCharacteristicWrite() - Device=" + address
@@ -487,6 +496,7 @@
* @hide
*/
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onDescriptorRead(String address, int status, int handle, byte[] value) {
if (VDBG) {
Log.d(TAG,
@@ -538,6 +548,7 @@
* @hide
*/
@Override
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void onDescriptorWrite(String address, int status, int handle) {
if (VDBG) {
Log.d(TAG,
@@ -734,6 +745,8 @@
* Application should call this method as early as possible after it is done with
* this GATT client.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void close() {
if (DBG) Log.d(TAG, "close()");
@@ -817,12 +830,13 @@
* <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
* is used to notify success or failure if the function returns true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param callback GATT callback handler that will receive asynchronous callbacks.
* @return If true, the callback will be called to notify success or failure, false on immediate
* error
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private boolean registerApp(BluetoothGattCallback callback, Handler handler) {
return registerApp(callback, handler, false);
}
@@ -833,14 +847,15 @@
* <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
* is used to notify success or failure if the function returns true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param callback GATT callback handler that will receive asynchronous callbacks.
* @param eatt_support indicate to allow for eatt support
* @return If true, the callback will be called to notify success or failure, false on immediate
* error
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private boolean registerApp(BluetoothGattCallback callback, Handler handler,
boolean eatt_support) {
if (DBG) Log.d(TAG, "registerApp()");
@@ -865,6 +880,8 @@
* Unregister the current application and callbacks.
*/
@UnsupportedAppUsage
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private void unregisterApp() {
if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
if (mService == null || mClientIf == 0) return;
@@ -893,14 +910,15 @@
* subsequent connections to known devices should be invoked with the
* autoConnect parameter set to true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Remote device to connect to
* @param autoConnect Whether to directly connect to the remote device (false) or to
* automatically connect as soon as the remote device becomes available (true).
* @return true, if the connection attempt was initiated successfully
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
/*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
Handler handler) {
if (DBG) {
@@ -931,9 +949,10 @@
/**
* Disconnects an established connection, or cancels a connection attempt
* currently in progress.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void disconnect() {
if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return;
@@ -954,6 +973,8 @@
*
* @return true, if the connection attempt was initiated successfully
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect() {
try {
mService.clientConnect(mClientIf, mDevice.getAddress(), false, mTransport,
@@ -983,6 +1004,8 @@
* of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
* {@link BluetoothDevice#PHY_OPTION_S8}
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
try {
mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
@@ -996,6 +1019,8 @@
* Read the current transmitter PHY and receiver PHY of the connection. The values are returned
* in {@link BluetoothGattCallback#onPhyRead}
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void readPhy() {
try {
mService.clientReadPhy(mClientIf, mDevice.getAddress());
@@ -1009,6 +1034,7 @@
*
* @return remote bluetooth device
*/
+ @RequiresNoPermission
public BluetoothDevice getDevice() {
return mDevice;
}
@@ -1022,10 +1048,11 @@
* triggered. If the discovery was successful, the remote services can be
* retrieved using the {@link #getServices} function.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return true, if the remote service discovery has been started
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean discoverServices() {
if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
@@ -1047,11 +1074,12 @@
* It should never be used by real applications. The service is not searched
* for characteristics and descriptors, or returned in any callback.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return true, if the remote service discovery has been started
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean discoverServiceByUuid(UUID uuid) {
if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
@@ -1073,11 +1101,11 @@
* <p>This function requires that service discovery has been completed
* for the given device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return List of services on the remote device. Returns an empty list if service discovery has
* not yet been performed.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public List<BluetoothGattService> getServices() {
List<BluetoothGattService> result =
new ArrayList<BluetoothGattService>();
@@ -1101,12 +1129,12 @@
* <p>If multiple instances of the same service (as identified by UUID)
* exist, the first instance of the service is returned.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param uuid UUID of the requested service
* @return BluetoothGattService if supported, or null if the requested service is not offered by
* the remote device.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public BluetoothGattService getService(UUID uuid) {
for (BluetoothGattService service : mServices) {
if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) {
@@ -1124,11 +1152,12 @@
* is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
* callback.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param characteristic Characteristic to read from the remote device
* @return true, if the read operation was initiated successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
return false;
@@ -1167,12 +1196,13 @@
* is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
* callback.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param uuid UUID of characteristic to read from the remote device
* @return true, if the read operation was initiated successfully
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) {
if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
if (mService == null || mClientIf == 0) return false;
@@ -1202,11 +1232,12 @@
* {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
* reporting the result of the operation.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param characteristic Characteristic to write on the remote device
* @return true, if the write operation was initiated successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
&& (characteristic.getProperties()
@@ -1248,11 +1279,12 @@
* {@link BluetoothGattCallback#onDescriptorRead} callback is
* triggered, signaling the result of the operation.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param descriptor Descriptor value to read from the remote device
* @return true, if the read operation was initiated successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean readDescriptor(BluetoothGattDescriptor descriptor) {
if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid());
if (mService == null || mClientIf == 0) return false;
@@ -1289,11 +1321,12 @@
* <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
* triggered to report the result of the write operation.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param descriptor Descriptor to write to the associated remote device
* @return true, if the write operation was initiated successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
@@ -1340,10 +1373,11 @@
* cancel the current transaction without committing any values on the
* remote device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return true, if the reliable write transaction has been initiated
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean beginReliableWrite() {
if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
@@ -1367,10 +1401,11 @@
* <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
* invoked to indicate whether the transaction has been executed correctly.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return true, if the request to execute the transaction has been sent
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean executeReliableWrite() {
if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
@@ -1396,9 +1431,10 @@
*
* <p>Calling this function will discard all queued characteristic write
* operations for a given remote device.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void abortReliableWrite() {
if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return;
@@ -1414,6 +1450,8 @@
* @deprecated Use {@link #abortReliableWrite()}
*/
@Deprecated
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void abortReliableWrite(BluetoothDevice mDevice) {
abortReliableWrite();
}
@@ -1426,12 +1464,13 @@
* triggered if the remote device indicates that the given characteristic
* has changed.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param characteristic The characteristic for which to enable notifications
* @param enable Set to true to enable notifications/indications
* @return true, if the requested notification status was set successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enable) {
if (DBG) {
@@ -1464,6 +1503,8 @@
* @hide
*/
@UnsupportedAppUsage
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean refresh() {
if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
@@ -1484,10 +1525,11 @@
* <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
* invoked when the RSSI value has been read.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return true, if the RSSI value has been requested successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean readRemoteRssi() {
if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
if (mService == null || mClientIf == 0) return false;
@@ -1512,10 +1554,11 @@
* <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
* whether this operation was successful.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return true, if the new MTU value has been requested successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean requestMtu(int mtu) {
if (DBG) {
Log.d(TAG, "configureMTU() - device: " + mDevice.getAddress()
@@ -1544,6 +1587,8 @@
* or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}.
* @throws IllegalArgumentException If the parameters are outside of their specified range.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean requestConnectionPriority(int connectionPriority) {
if (connectionPriority < CONNECTION_PRIORITY_BALANCED
|| connectionPriority > CONNECTION_PRIORITY_LOW_POWER) {
@@ -1571,6 +1616,8 @@
* @return true, if the request is send to the Bluetooth stack.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean requestLeConnectionUpdate(int minConnectionInterval, int maxConnectionInterval,
int slaveLatency, int supervisionTimeout,
int minConnectionEventLen, int maxConnectionEventLen) {
@@ -1604,6 +1651,7 @@
* @throws UnsupportedOperationException
*/
@Override
+ @RequiresNoPermission
public int getConnectionState(BluetoothDevice device) {
throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
}
@@ -1615,6 +1663,7 @@
* @throws UnsupportedOperationException
*/
@Override
+ @RequiresNoPermission
public List<BluetoothDevice> getConnectedDevices() {
throw new UnsupportedOperationException(
"Use BluetoothManager#getConnectedDevices instead.");
@@ -1628,6 +1677,7 @@
* @throws UnsupportedOperationException
*/
@Override
+ @RequiresNoPermission
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
throw new UnsupportedOperationException(
"Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index 8f1b59c..8a7d4ba 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -237,7 +237,6 @@
/**
* Create a new BluetoothGattCharacteristic.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param uuid The UUID for this characteristic
* @param properties Properties of this characteristic
@@ -344,7 +343,6 @@
/**
* Adds a descriptor to this characteristic.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param descriptor Descriptor to be added to this characteristic.
* @return true, if the descriptor was added to the characteristic
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
index 49ba281..ed5ea08 100644
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -128,7 +128,6 @@
/**
* Create a new BluetoothGattDescriptor.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param uuid The UUID for this descriptor
* @param permissions Permissions for this descriptor
@@ -139,7 +138,6 @@
/**
* Create a new BluetoothGattDescriptor.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param characteristic The characteristic this descriptor belongs to
* @param uuid The UUID for this descriptor
@@ -228,8 +226,6 @@
* <p>If a remote device offers multiple descriptors with the same UUID,
* the instance ID is used to distuinguish between descriptors.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return Instance ID of this descriptor
* @hide
*/
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 088b016..865f476 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -16,6 +16,11 @@
package android.bluetooth;
+import android.annotation.RequiresNoPermission;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Log;
@@ -55,6 +60,7 @@
/**
* Bluetooth GATT interface callbacks
*/
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
new IBluetoothGattServerCallback.Stub() {
/**
@@ -425,6 +431,8 @@
* Application should call this method as early as possible after it is done with
* this GATT server.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void close() {
if (DBG) Log.d(TAG, "close()");
unregisterCallback();
@@ -436,12 +444,13 @@
* <p>This is an asynchronous call. The callback is used to notify
* success or failure if the function returns true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param callback GATT callback handler that will receive asynchronous callbacks.
* @return true, the callback will be called to notify success or failure, false on immediate
* error
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
/*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
return registerCallback(callback, false);
}
@@ -452,14 +461,15 @@
* <p>This is an asynchronous call. The callback is used to notify
* success or failure if the function returns true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param callback GATT callback handler that will receive asynchronous callbacks.
* @param eatt_support indicates if server can use eatt
* @return true, the callback will be called to notify success or failure, false on immediate
* error
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
/*package*/ boolean registerCallback(BluetoothGattServerCallback callback,
boolean eatt_support) {
if (DBG) Log.d(TAG, "registerCallback()");
@@ -504,6 +514,8 @@
/**
* Unregister the current application and callbacks.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private void unregisterCallback() {
if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
if (mService == null || mServerIf == 0) return;
@@ -548,12 +560,13 @@
* subsequent connections to known devices should be invoked with the
* autoConnect parameter set to true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param autoConnect Whether to directly connect to the remote device (false) or to
* automatically connect as soon as the remote device becomes available (true).
* @return true, if the connection attempt was initiated successfully
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(BluetoothDevice device, boolean autoConnect) {
if (DBG) {
Log.d(TAG,
@@ -576,10 +589,11 @@
* Disconnects an established connection, or cancels a connection attempt
* currently in progress.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Remote device
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void cancelConnection(BluetoothDevice device) {
if (DBG) Log.d(TAG, "cancelConnection() - device: " + device.getAddress());
if (mService == null || mServerIf == 0) return;
@@ -609,6 +623,8 @@
* of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} or
* {@link BluetoothDevice#PHY_OPTION_S8}
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) {
try {
mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy,
@@ -624,6 +640,8 @@
*
* @param device The remote device to send this response to
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void readPhy(BluetoothDevice device) {
try {
mService.serverReadPhy(mServerIf, device.getAddress());
@@ -645,14 +663,15 @@
* <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
* </ul>
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device The remote device to send this response to
* @param requestId The ID of the request that was received with the callback
* @param status The status of the request to be sent to the remote devices
* @param offset Value offset for partial read/write response
* @param value The value of the attribute that was read/written (optional)
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendResponse(BluetoothDevice device, int requestId,
int status, int offset, byte[] value) {
if (VDBG) Log.d(TAG, "sendResponse() - device: " + device.getAddress());
@@ -677,8 +696,6 @@
* for every client that requests notifications/indications by writing
* to the "Client Configuration" descriptor for the given characteristic.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device The remote device to receive the notification/indication
* @param characteristic The local characteristic that has been updated
* @param confirm true to request confirmation from the client (indication), false to send a
@@ -686,6 +703,9 @@
* @return true, if the notification has been triggered successfully
* @throws IllegalArgumentException
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean notifyCharacteristicChanged(BluetoothDevice device,
BluetoothGattCharacteristic characteristic, boolean confirm) {
if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress());
@@ -724,11 +744,12 @@
* whether this service has been added successfully. Do not add another service
* before this callback.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param service Service to be added to the list of services provided by this device.
* @return true, if the request to add service has been initiated
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean addService(BluetoothGattService service) {
if (DBG) Log.d(TAG, "addService() - service: " + service.getUuid());
if (mService == null || mServerIf == 0) return false;
@@ -748,11 +769,12 @@
/**
* Removes a service from the list of services to be provided.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param service Service to be removed.
* @return true, if the service has been removed
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean removeService(BluetoothGattService service) {
if (DBG) Log.d(TAG, "removeService() - service: " + service.getUuid());
if (mService == null || mServerIf == 0) return false;
@@ -774,8 +796,10 @@
/**
* Remove all services from the list of provided services.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void clearServices() {
if (DBG) Log.d(TAG, "clearServices()");
if (mService == null || mServerIf == 0) return;
@@ -794,10 +818,10 @@
* <p>An application must call {@link #addService} to add a serice to the
* list of services offered by this device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return List of services. Returns an empty list if no services have been added yet.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public List<BluetoothGattService> getServices() {
return mServices;
}
@@ -809,12 +833,12 @@
* <p>If multiple instances of the same service (as identified by UUID)
* exist, the first instance of the service is returned.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param uuid UUID of the requested service
* @return BluetoothGattService if supported, or null if the requested service is not offered by
* this device.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresNoPermission
public BluetoothGattService getService(UUID uuid) {
for (BluetoothGattService service : mServices) {
if (service.getUuid().equals(uuid)) {
@@ -833,6 +857,7 @@
* @throws UnsupportedOperationException
*/
@Override
+ @RequiresNoPermission
public int getConnectionState(BluetoothDevice device) {
throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
}
@@ -844,6 +869,7 @@
* @throws UnsupportedOperationException
*/
@Override
+ @RequiresNoPermission
public List<BluetoothDevice> getConnectedDevices() {
throw new UnsupportedOperationException(
"Use BluetoothManager#getConnectedDevices instead.");
@@ -857,6 +883,7 @@
* @throws UnsupportedOperationException
*/
@Override
+ @RequiresNoPermission
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
throw new UnsupportedOperationException(
"Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java
index 23dc7c8..f64d09f 100644
--- a/core/java/android/bluetooth/BluetoothGattService.java
+++ b/core/java/android/bluetooth/BluetoothGattService.java
@@ -15,6 +15,9 @@
*/
package android.bluetooth;
+import android.annotation.RequiresPermission;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -98,7 +101,6 @@
/**
* Create a new BluetoothGattService.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param uuid The UUID for this service
* @param serviceType The type of this service,
@@ -225,11 +227,11 @@
/**
* Add an included service to this service.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param service The service to be added
* @return true, if the included service was added to the service
*/
+ @RequiresLegacyBluetoothPermission
public boolean addService(BluetoothGattService service) {
mIncludedServices.add(service);
return true;
@@ -237,11 +239,11 @@
/**
* Add a characteristic to this service.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param characteristic The characteristics to be added
* @return true, if the characteristic was added to the service
*/
+ @RequiresLegacyBluetoothPermission
public boolean addCharacteristic(BluetoothGattCharacteristic characteristic) {
mCharacteristics.add(characteristic);
characteristic.setService(this);
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 632572d..a1ece7f 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -21,7 +21,11 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -70,10 +74,10 @@
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
@@ -90,10 +94,10 @@
* </ul>
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
- * to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_AUDIO_STATE_CHANGED =
"android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
@@ -107,11 +111,11 @@
* be null if no device is active. </li>
* </ul>
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
- *
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@UnsupportedAppUsage(trackingBug = 171933273)
public static final String ACTION_ACTIVE_DEVICE_CHANGED =
@@ -147,9 +151,10 @@
* <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET </li>
* <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 </li>
* </ul>
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission
- * to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
"android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT";
@@ -299,10 +304,12 @@
* are given an assigned number. Below shows the assigned number of Indicator added so far
* - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled
* - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive.
*
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public static final String ACTION_HF_INDICATORS_VALUE_CHANGED =
"android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED";
@@ -333,6 +340,7 @@
private volatile IBluetoothHeadset mService;
private BluetoothAdapter mAdapter;
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
new IBluetoothStateChangeCallback.Stub() {
public void onBluetoothStateChange(boolean up) {
@@ -432,15 +440,17 @@
* the state. Users can get the connection state of the profile
* from this intent.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
- *
* @param device Remote Bluetooth Device
* @return false on immediate error, true otherwise
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHeadset service = mService;
@@ -474,15 +484,14 @@
* {@link #STATE_DISCONNECTING} can be used to distinguish between the
* two scenarios.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
- *
* @param device Remote Bluetooth Device
* @return false on immediate error, true otherwise
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHeadset service = mService;
@@ -502,6 +511,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHeadset service = mService;
@@ -521,6 +532,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHeadset service = mService;
@@ -540,6 +553,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getConnectionState(" + device + ")");
final IBluetoothHeadset service = mService;
@@ -571,7 +586,12 @@
*/
@Deprecated
@SystemApi
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
final IBluetoothHeadset service = mService;
@@ -605,7 +625,12 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -638,7 +663,9 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
final IBluetoothHeadset service = mService;
@@ -688,7 +715,9 @@
* @param device Bluetooth device
* @return true if echo cancellation and/or noise reduction is supported, false otherwise
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isNoiseReductionSupported(@NonNull BluetoothDevice device) {
if (DBG) log("isNoiseReductionSupported()");
final IBluetoothHeadset service = mService;
@@ -709,7 +738,9 @@
* @param device Bluetooth device
* @return true if voice recognition is supported, false otherwise
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isVoiceRecognitionSupported(@NonNull BluetoothDevice device) {
if (DBG) log("isVoiceRecognitionSupported()");
final IBluetoothHeadset service = mService;
@@ -738,13 +769,17 @@
* audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
* in case of failure to establish the audio connection.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Bluetooth headset
* @return false if there is no headset connected, or the connected headset doesn't support
* voice recognition, or voice recognition is already started, or audio channel is occupied,
* or on error, true otherwise
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
final IBluetoothHeadset service = mService;
@@ -767,12 +802,13 @@
* If this function returns true, this intent will be broadcasted with
* {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Bluetooth headset
* @return false if there is no headset connected, or voice recognition has not started,
* or voice recognition has ended on this headset, or on error, true otherwise
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
final IBluetoothHeadset service = mService;
@@ -790,11 +826,12 @@
/**
* Check if Bluetooth SCO audio is connected.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Bluetooth headset
* @return true if SCO is connected, false otherwise or on error
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isAudioConnected(BluetoothDevice device) {
if (VDBG) log("isAudioConnected()");
final IBluetoothHeadset service = mService;
@@ -827,6 +864,8 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getAudioState(BluetoothDevice device) {
if (VDBG) log("getAudioState");
final IBluetoothHeadset service = mService;
@@ -853,6 +892,8 @@
* @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setAudioRouteAllowed(boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
final IBluetoothHeadset service = mService;
@@ -874,6 +915,8 @@
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getAudioRouteAllowed() {
if (VDBG) log("getAudioRouteAllowed");
final IBluetoothHeadset service = mService;
@@ -897,6 +940,8 @@
* False to use SCO audio in normal manner
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setForceScoAudio(boolean forced) {
if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
final IBluetoothHeadset service = mService;
@@ -915,12 +960,13 @@
/**
* Check if at least one headset's SCO audio is connected or connecting
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @return true if at least one device's SCO audio is connected or connecting, false otherwise
* or on error
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isAudioOn() {
if (VDBG) log("isAudioOn()");
final IBluetoothHeadset service = mService;
@@ -955,6 +1001,8 @@
* @hide
*/
@UnsupportedAppUsage
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connectAudio() {
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
@@ -982,6 +1030,8 @@
* @hide
*/
@UnsupportedAppUsage
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnectAudio() {
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
@@ -1018,7 +1068,12 @@
* - binder is dead or Bluetooth is disabled or other error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
@UnsupportedAppUsage
public boolean startScoUsingVirtualVoiceCall() {
if (DBG) log("startScoUsingVirtualVoiceCall()");
@@ -1048,7 +1103,12 @@
* - binder is dead or Bluetooth is disabled or other error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
@UnsupportedAppUsage
public boolean stopScoUsingVirtualVoiceCall() {
if (DBG) log("stopScoUsingVirtualVoiceCall()");
@@ -1075,6 +1135,11 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
int type, String name) {
final IBluetoothHeadset service = mService;
@@ -1095,6 +1160,11 @@
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
String number, int type) {
final IBluetoothHeadset service = mService;
@@ -1119,8 +1189,6 @@
*
* <p>Currently only {@link #VENDOR_RESULT_CODE_COMMAND_ANDROID} is allowed as {@code command}.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device Bluetooth headset.
* @param command A vendor-specific command.
* @param arg The argument that will be attached to the command.
@@ -1128,6 +1196,9 @@
* vendor-specific unsolicited result code, or on error. {@code true} otherwise.
* @throws IllegalArgumentException if {@code command} is {@code null}.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command,
String arg) {
if (DBG) {
@@ -1164,15 +1235,17 @@
* {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
* with the active device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
- *
* @param device Remote Bluetooth Device, could be null if phone call audio should not be
* streamed to a headset
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
@UnsupportedAppUsage(trackingBug = 171933273)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) {
@@ -1201,7 +1274,9 @@
*/
@UnsupportedAppUsage(trackingBug = 171933273)
@Nullable
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getActiveDevice() {
if (VDBG) {
Log.d(TAG, "getActiveDevice");
@@ -1227,7 +1302,9 @@
* @return true if in-band ringing is enabled, false if in-band ringing is disabled
* @hide
*/
- @RequiresPermission(android.Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isInbandRingingEnabled() {
if (DBG) {
log("isInbandRingingEnabled()");
@@ -1257,6 +1334,7 @@
com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support);
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothProfileServiceConnection mConnection =
new IBluetoothProfileServiceConnection.Stub() {
@Override
@@ -1293,6 +1371,7 @@
Log.d(TAG, msg);
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index e5b2a1e..eef42d1 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -16,9 +16,10 @@
package android.bluetooth;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Binder;
@@ -447,6 +448,8 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHeadsetClient service =
@@ -473,6 +476,8 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHeadsetClient service =
@@ -495,6 +500,8 @@
* @return list of connected devices; empty list if nothing is connected.
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHeadsetClient service =
@@ -519,6 +526,8 @@
* list if nothing matches the <code>states</code>
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHeadsetClient service =
@@ -542,6 +551,8 @@
* @return the state of connection of the device
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getConnectionState(" + device + ")");
final IBluetoothHeadsetClient service =
@@ -569,7 +580,8 @@
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -587,7 +599,8 @@
* @return true if connectionPolicy is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -619,7 +632,9 @@
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -636,7 +651,9 @@
* @return connection policy of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothHeadsetClient service =
@@ -664,6 +681,8 @@
* #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature
* is not supported.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
final IBluetoothHeadsetClient service =
@@ -688,6 +707,8 @@
* @return <code>true</code> if command has been issued successfully; <code>false</code>
* otherwise.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId,
String atCommand) {
if (DBG) log("sendVendorSpecificCommand()");
@@ -715,6 +736,8 @@
* #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature
* is not supported.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
final IBluetoothHeadsetClient service =
@@ -736,6 +759,8 @@
* @param device remote device
* @return list of calls; empty list if none call exists
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
if (DBG) log("getCurrentCalls()");
final IBluetoothHeadsetClient service =
@@ -757,6 +782,8 @@
* @param device remote device
* @return bundle of AG indicators; null if device is not in CONNECTED state
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public Bundle getCurrentAgEvents(BluetoothDevice device) {
if (DBG) log("getCurrentCalls()");
final IBluetoothHeadsetClient service =
@@ -782,6 +809,8 @@
* otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean acceptCall(BluetoothDevice device, int flag) {
if (DBG) log("acceptCall()");
final IBluetoothHeadsetClient service =
@@ -804,6 +833,8 @@
* @return <code>true</code> if command has been issued successfully; <code>false</code>
* otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean holdCall(BluetoothDevice device) {
if (DBG) log("holdCall()");
final IBluetoothHeadsetClient service =
@@ -831,6 +862,8 @@
* supported.</p>
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean rejectCall(BluetoothDevice device) {
if (DBG) log("rejectCall()");
final IBluetoothHeadsetClient service =
@@ -862,6 +895,8 @@
* #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not
* supported.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
if (DBG) log("terminateCall()");
final IBluetoothHeadsetClient service =
@@ -891,6 +926,8 @@
* #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not
* supported.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean enterPrivateMode(BluetoothDevice device, int index) {
if (DBG) log("enterPrivateMode()");
final IBluetoothHeadsetClient service =
@@ -919,6 +956,8 @@
* #EXTRA_AG_FEATURE_MERGE_AND_DETACH}. This method invocation will fail silently when feature
* is not supported.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean explicitCallTransfer(BluetoothDevice device) {
if (DBG) log("explicitCallTransfer()");
final IBluetoothHeadsetClient service =
@@ -943,6 +982,8 @@
* successfully; <code>{@link null}</code> otherwise; upon completion HFP sends {@link
* #ACTION_CALL_CHANGED} intent in case of success; {@link #ACTION_RESULT} is sent otherwise;
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
if (DBG) log("dial()");
final IBluetoothHeadsetClient service =
@@ -968,6 +1009,8 @@
* @return <code>true</code> if command has been issued successfully; <code>false</code>
* otherwise; upon completion HFP sends {@link #ACTION_RESULT} intent;
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendDTMF(BluetoothDevice device, byte code) {
if (DBG) log("sendDTMF()");
final IBluetoothHeadsetClient service =
@@ -995,6 +1038,8 @@
* #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}. This method invocation will fail silently when
* feature is not supported.</p>
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getLastVoiceTagNumber(BluetoothDevice device) {
if (DBG) log("getLastVoiceTagNumber()");
final IBluetoothHeadsetClient service =
@@ -1016,6 +1061,8 @@
* Note: This is an internal function and shouldn't be exposed
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getAudioState(BluetoothDevice device) {
if (VDBG) log("getAudioState");
final IBluetoothHeadsetClient service =
@@ -1040,6 +1087,8 @@
* @param allowed if routing is allowed to the device Note: This is an internal function and
* shouldn't be exposed
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
final IBluetoothHeadsetClient service =
@@ -1063,6 +1112,8 @@
* @return whether the command succeeded Note: This is an internal function and shouldn't be
* exposed
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getAudioRouteAllowed(BluetoothDevice device) {
if (VDBG) log("getAudioRouteAllowed");
final IBluetoothHeadsetClient service =
@@ -1089,6 +1140,8 @@
* @return <code>true</code> if command has been issued successfully; <code>false</code>
* otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connectAudio(BluetoothDevice device) {
final IBluetoothHeadsetClient service =
getService();
@@ -1114,6 +1167,8 @@
* @return <code>true</code> if command has been issued successfully; <code>false</code>
* otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnectAudio(BluetoothDevice device) {
final IBluetoothHeadsetClient service =
getService();
@@ -1136,6 +1191,8 @@
* @param device remote device
* @return bundle of AG features; null if no service or AG not connected
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public Bundle getCurrentAgFeatures(BluetoothDevice device) {
final IBluetoothHeadsetClient service =
getService();
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index 5fd60e0..65f68a9 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -16,6 +16,10 @@
package android.bluetooth;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -111,8 +115,6 @@
* which will act as the {@link #SOURCE_ROLE}. This is an asynchronous call and so
* the callback is used to notify success or failure if the function returns true.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param name The friendly name associated with the application or configuration.
* @param dataType The dataType of the Source role of Health Profile to which the sink wants to
* connect to.
@@ -126,6 +128,10 @@
* {@link BluetoothDevice#createL2capChannel(int)}
*/
@Deprecated
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public boolean registerSinkAppConfiguration(String name, int dataType,
BluetoothHealthCallback callback) {
Log.e(TAG, "registerSinkAppConfiguration(): BluetoothHealth is deprecated");
@@ -136,8 +142,6 @@
* Unregister an application configuration that has been registered using
* {@link #registerSinkAppConfiguration}
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param config The health app configuration
* @return Success or failure.
*
@@ -147,6 +151,10 @@
* {@link BluetoothDevice#createL2capChannel(int)}
*/
@Deprecated
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
Log.e(TAG, "unregisterAppConfiguration(): BluetoothHealth is deprecated");
return false;
@@ -157,8 +165,6 @@
* This is an asynchronous call. If this function returns true, the callback
* associated with the application configuration will be called.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device The remote Bluetooth device.
* @param config The application configuration which has been registered using {@link
* #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
@@ -170,6 +176,10 @@
* {@link BluetoothDevice#createL2capChannel(int)}
*/
@Deprecated
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public boolean connectChannelToSource(BluetoothDevice device,
BluetoothHealthAppConfiguration config) {
Log.e(TAG, "connectChannelToSource(): BluetoothHealth is deprecated");
@@ -181,8 +191,6 @@
* This is an asynchronous call. If this function returns true, the callback
* associated with the application configuration will be called.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* @param device The remote Bluetooth device.
* @param config The application configuration which has been registered using {@link
* #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
@@ -195,6 +203,10 @@
* {@link BluetoothDevice#createL2capChannel(int)}
*/
@Deprecated
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public boolean disconnectChannel(BluetoothDevice device,
BluetoothHealthAppConfiguration config, int channelId) {
Log.e(TAG, "disconnectChannel(): BluetoothHealth is deprecated");
@@ -205,8 +217,6 @@
* Get the file descriptor of the main channel associated with the remote device
* and application configuration.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* <p> Its the responsibility of the caller to close the ParcelFileDescriptor
* when done.
*
@@ -220,6 +230,10 @@
* {@link BluetoothDevice#createL2capChannel(int)}
*/
@Deprecated
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
BluetoothHealthAppConfiguration config) {
Log.e(TAG, "getMainChannelFd(): BluetoothHealth is deprecated");
@@ -229,8 +243,6 @@
/**
* Get the current connection state of the profile.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* This is not specific to any application configuration but represents the connection
* state of the local Bluetooth adapter with the remote device. This can be used
* by applications like status bar which would just like to know the state of the
@@ -241,6 +253,10 @@
* #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
*/
@Override
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public int getConnectionState(BluetoothDevice device) {
Log.e(TAG, "getConnectionState(): BluetoothHealth is deprecated");
return STATE_DISCONNECTED;
@@ -251,8 +267,6 @@
*
* <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* This is not specific to any application configuration but represents the connection
* state of the local Bluetooth adapter for this profile. This can be used
* by applications like status bar which would just like to know the state of the
@@ -261,6 +275,10 @@
* @return List of devices. The list will be empty on error.
*/
@Override
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public List<BluetoothDevice> getConnectedDevices() {
Log.e(TAG, "getConnectedDevices(): BluetoothHealth is deprecated");
return new ArrayList<>();
@@ -273,8 +291,7 @@
* <p> If none of the devices match any of the given states,
* an empty list will be returned.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- * This is not specific to any application configuration but represents the connection
+ * <p>This is not specific to any application configuration but represents the connection
* state of the local Bluetooth adapter for this profile. This can be used
* by applications like status bar which would just like to know the state of the
* local adapter.
@@ -284,6 +301,10 @@
* @return List of devices. The list will be empty on error.
*/
@Override
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Log.e(TAG, "getDevicesMatchingConnectionStates(): BluetoothHealth is deprecated");
return new ArrayList<>();
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index ff78825e..fa52eda 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -21,7 +21,11 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -64,10 +68,10 @@
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
@@ -81,11 +85,11 @@
* be null if no device is active. </li>
* </ul>
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
- *
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_ACTIVE_DEVICE_CHANGED =
@@ -167,7 +171,11 @@
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHearingAid service = getService();
@@ -225,6 +233,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHearingAid service = getService();
@@ -244,6 +254,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
@NonNull int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
@@ -264,6 +276,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @BluetoothProfile.BtProfileState int getConnectionState(
@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
@@ -295,14 +309,14 @@
* {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
* with the active device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
- *
* @param device the remote Bluetooth device. Could be null to clear
* the active device and stop streaming audio to a Bluetooth device.
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
@@ -330,7 +344,9 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getActiveDevices() {
if (VDBG) log("getActiveDevices()");
final IBluetoothHearingAid service = getService();
@@ -357,7 +373,11 @@
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -376,7 +396,11 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -473,6 +497,8 @@
* @param volume Absolute volume to be set on remote
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setVolume(int volume) {
if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
@@ -531,7 +557,9 @@
* @return SIDE_LEFT or SIDE_RIGHT
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getDeviceSide(BluetoothDevice device) {
if (VDBG) {
log("getDeviceSide(" + device + ")");
@@ -557,7 +585,9 @@
* @return MODE_MONAURAL or MODE_BINAURAL
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getDeviceMode(BluetoothDevice device) {
if (VDBG) {
log("getDeviceMode(" + device + ")");
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index 2baa738..6565ec0 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -20,7 +20,10 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Binder;
@@ -56,9 +59,10 @@
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of {@link
* #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, {@link #STATE_CONNECTED}, {@link
* #STATE_DISCONNECTING}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED";
@@ -436,6 +440,8 @@
/** {@inheritDoc} */
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
final IBluetoothHidDevice service = getService();
if (service != null) {
@@ -453,6 +459,8 @@
/** {@inheritDoc} */
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
final IBluetoothHidDevice service = getService();
if (service != null) {
@@ -470,6 +478,8 @@
/** {@inheritDoc} */
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
final IBluetoothHidDevice service = getService();
if (service != null) {
@@ -508,6 +518,8 @@
* object is required.
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean registerApp(
BluetoothHidDeviceAppSdpSettings sdp,
BluetoothHidDeviceAppQosSettings inQos,
@@ -553,6 +565,8 @@
*
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean unregisterApp() {
boolean result = false;
@@ -578,6 +592,8 @@
* @param data Report data, not including Report Id.
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
boolean result = false;
@@ -604,6 +620,8 @@
* @param data Report data, not including Report Id.
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
boolean result = false;
@@ -628,6 +646,8 @@
* @param error Error to be sent for SET_REPORT via HANDSHAKE.
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean reportError(BluetoothDevice device, byte error) {
boolean result = false;
@@ -651,6 +671,8 @@
* @return the current user name, or empty string if cannot get the name
* {@hide}
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public String getUserAppName() {
final IBluetoothHidDevice service = getService();
@@ -675,6 +697,8 @@
*
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(BluetoothDevice device) {
boolean result = false;
@@ -699,6 +723,8 @@
*
* @return true if the command is successfully sent; otherwise false.
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
boolean result = false;
@@ -734,7 +760,11 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 9561d93..bef4472 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -21,6 +21,9 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Context;
@@ -65,11 +68,11 @@
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
@SuppressLint("ActionValue")
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
@@ -328,7 +331,8 @@
*/
@SystemApi
@Override
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHidHost service = getService();
@@ -350,6 +354,8 @@
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHidHost service = getService();
@@ -372,7 +378,8 @@
*/
@SystemApi
@Override
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
if (device == null) {
@@ -503,12 +510,13 @@
/**
* Initiate virtual unplug for a HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean virtualUnplug(BluetoothDevice device) {
if (DBG) log("virtualUnplug(" + device + ")");
final IBluetoothHidHost service = getService();
@@ -529,12 +537,13 @@
/**
* Send Get_Protocol_Mode command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getProtocolMode(BluetoothDevice device) {
if (VDBG) log("getProtocolMode(" + device + ")");
final IBluetoothHidHost service = getService();
@@ -553,12 +562,13 @@
/**
* Send Set_Protocol_Mode command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
if (DBG) log("setProtocolMode(" + device + ")");
final IBluetoothHidHost service = getService();
@@ -577,8 +587,6 @@
/**
* Send Get_Report command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @param reportType Report type
* @param reportId Report ID
@@ -586,6 +594,9 @@
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getReport(BluetoothDevice device, byte reportType, byte reportId,
int bufferSize) {
if (VDBG) {
@@ -608,14 +619,15 @@
/**
* Send Set_Report command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @param reportType Report type
* @param report Report receiving buffer size
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setReport(BluetoothDevice device, byte reportType, String report) {
if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
final IBluetoothHidHost service = getService();
@@ -634,13 +646,14 @@
/**
* Send Send_Data command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @param report Report to send
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendData(BluetoothDevice device, String report) {
if (DBG) log("sendData(" + device + "), report=" + report);
final IBluetoothHidHost service = getService();
@@ -659,12 +672,13 @@
/**
* Send Get_Idle_Time command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getIdleTime(BluetoothDevice device) {
if (DBG) log("getIdletime(" + device + ")");
final IBluetoothHidHost service = getService();
@@ -683,13 +697,14 @@
/**
* Send Set_Idle_Time command to the connected HID input device.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
- *
* @param device Remote Bluetooth Device
* @param idleTime Idle time to be set on HID Device
* @return false on immediate error, true otherwise
* @hide
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime);
final IBluetoothHidHost service = getService();
diff --git a/core/java/android/bluetooth/BluetoothInputStream.java b/core/java/android/bluetooth/BluetoothInputStream.java
index 8eb79b2..95f9229 100644
--- a/core/java/android/bluetooth/BluetoothInputStream.java
+++ b/core/java/android/bluetooth/BluetoothInputStream.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import android.annotation.SuppressLint;
+
import java.io.IOException;
import java.io.InputStream;
@@ -26,6 +28,7 @@
*
* @hide
*/
+@SuppressLint("AndroidFrameworkBluetoothPermission")
/*package*/ final class BluetoothInputStream extends InputStream {
private BluetoothSocket mSocket;
diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java
index 3f00fa6..462c7b7 100644
--- a/core/java/android/bluetooth/BluetoothLeAudio.java
+++ b/core/java/android/bluetooth/BluetoothLeAudio.java
@@ -23,6 +23,9 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -65,10 +68,10 @@
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED =
"android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED";
@@ -82,11 +85,11 @@
* be null if no device is active. </li>
* </ul>
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
- *
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED =
"android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED";
@@ -122,7 +125,6 @@
/**
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public void close() {
mProfileConnector.disconnect();
}
@@ -131,7 +133,6 @@
return mProfileConnector.getService();
}
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
protected void finalize() {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
@@ -154,7 +155,8 @@
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(@Nullable BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
try {
@@ -193,7 +195,8 @@
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(@Nullable BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
try {
@@ -213,6 +216,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
try {
@@ -232,6 +237,8 @@
* {@inheritDoc}
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
@NonNull int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
@@ -252,7 +259,9 @@
* {@inheritDoc}
*/
@Override
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
try {
@@ -289,7 +298,8 @@
* @return false on immediate error, true otherwise
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
try {
@@ -314,7 +324,9 @@
* @hide
*/
@NonNull
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getActiveDevices() {
if (VDBG) log("getActiveDevices()");
try {
@@ -337,7 +349,9 @@
* @return group id that this device currently belongs to
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getGroupId(@NonNull BluetoothDevice device) {
if (VDBG) log("getGroupId()");
try {
@@ -365,7 +379,11 @@
* @return true if connectionPolicy is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -398,7 +416,8 @@
* @return connection policy of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
try {
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index d5c1c3e..2374f1c 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -18,8 +18,12 @@
import android.Manifest;
import android.annotation.RequiresFeature;
+import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemService;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.IBinder;
@@ -91,6 +95,7 @@
*
* @return the BLUETOOTH Adapter
*/
+ @RequiresNoPermission
public BluetoothAdapter getAdapter() {
return mAdapter;
}
@@ -109,7 +114,9 @@
* {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_DISCONNECTED},
* {@link BluetoothProfile#STATE_DISCONNECTING}
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device, int profile) {
if (DBG) Log.d(TAG, "getConnectionState()");
@@ -136,7 +143,9 @@
* @param profile GATT or GATT_SERVER
* @return List of devices. The list will be empty on error.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices(int profile) {
if (DBG) Log.d(TAG, "getConnectedDevices");
if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) {
@@ -177,7 +186,9 @@
* {@link BluetoothProfile#STATE_DISCONNECTING},
* @return List of devices. The list will be empty on error.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int profile, int[] states) {
if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates");
@@ -210,6 +221,8 @@
* @param callback GATT server callback handler that will receive asynchronous callbacks.
* @return BluetoothGattServer instance
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGattServer openGattServer(Context context,
BluetoothGattServerCallback callback) {
@@ -229,6 +242,8 @@
* @return BluetoothGattServer instance
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGattServer openGattServer(Context context,
BluetoothGattServerCallback callback, boolean eatt_support) {
return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO, eatt_support));
@@ -249,6 +264,8 @@
* @return BluetoothGattServer instance
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGattServer openGattServer(Context context,
BluetoothGattServerCallback callback, int transport) {
return (openGattServer(context, callback, transport, false));
@@ -270,6 +287,8 @@
* @return BluetoothGattServer instance
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothGattServer openGattServer(Context context,
BluetoothGattServerCallback callback, int transport, boolean eatt_support) {
if (context == null || callback == null) {
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 3554995..998fde0 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -18,9 +18,11 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Binder;
@@ -93,7 +95,6 @@
mCloseGuard.open("close");
}
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
protected void finalize() {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
@@ -110,7 +111,6 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public void close() {
if (VDBG) log("close()");
mProfileConnector.disconnect();
@@ -128,6 +128,8 @@
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getState() {
if (VDBG) log("getState()");
final IBluetoothMap service = getService();
@@ -152,6 +154,8 @@
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
final IBluetoothMap service = getService();
@@ -175,6 +179,8 @@
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
final IBluetoothMap service = getService();
@@ -197,6 +203,7 @@
*
* @hide
*/
+ @RequiresNoPermission
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")" + "not supported for MAPS");
return false;
@@ -211,6 +218,8 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothMap service = getService();
@@ -257,7 +266,11 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothMap service = getService();
@@ -280,6 +293,8 @@
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothMap service = getService();
@@ -302,6 +317,8 @@
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothMap service = getService();
@@ -328,7 +345,11 @@
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -347,7 +368,11 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -378,7 +403,11 @@
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -396,7 +425,11 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothMap service = getService();
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 0312a21..f20b533 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -20,8 +20,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.PendingIntent;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.net.Uri;
@@ -192,6 +194,8 @@
* currently connected to the Map service.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isConnected(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
final IBluetoothMapClient service = getService();
@@ -214,7 +218,11 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean connect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
final IBluetoothMapClient service = getService();
@@ -239,7 +247,11 @@
*
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean disconnect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "disconnect(" + device + ")");
final IBluetoothMapClient service = getService();
@@ -261,6 +273,8 @@
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) Log.d(TAG, "getConnectedDevices()");
final IBluetoothMapClient service = getService();
@@ -283,6 +297,8 @@
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
final IBluetoothMapClient service = getService();
@@ -305,6 +321,8 @@
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
final IBluetoothMapClient service = getService();
@@ -331,7 +349,11 @@
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -349,7 +371,11 @@
* @return true if connectionPolicy is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -380,7 +406,11 @@
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public int getPriority(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "getPriority(" + device + ")");
return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -397,7 +427,11 @@
* @return connection policy of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")");
final IBluetoothMapClient service = getService();
@@ -427,7 +461,11 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.SEND_SMS)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.SEND_SMS,
+ })
public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection<Uri> contacts,
@NonNull String message, @Nullable PendingIntent sentIntent,
@Nullable PendingIntent deliveredIntent) {
@@ -459,6 +497,11 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.SEND_SMS,
+ })
public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
PendingIntent sentIntent, PendingIntent deliveredIntent) {
if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
@@ -481,6 +524,11 @@
* @return true if the message is enqueued, false on error
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.READ_SMS,
+ })
public boolean getUnreadMessages(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
final IBluetoothMapClient service = getService();
@@ -503,6 +551,8 @@
* MapSupportedFeatures field is set. False is returned otherwise.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isUploadingSupported(BluetoothDevice device) {
final IBluetoothMapClient service = getService();
try {
@@ -530,7 +580,11 @@
* @return <code>true</code> if request has been sent, <code>false</code> on error
* @hide
*/
- @RequiresPermission(Manifest.permission.READ_SMS)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.READ_SMS,
+ })
public boolean setMessageStatus(BluetoothDevice device, String handle, int status) {
if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")");
final IBluetoothMapClient service = getService();
diff --git a/core/java/android/bluetooth/BluetoothOutputStream.java b/core/java/android/bluetooth/BluetoothOutputStream.java
index a0aa2de..ac2b3ed 100644
--- a/core/java/android/bluetooth/BluetoothOutputStream.java
+++ b/core/java/android/bluetooth/BluetoothOutputStream.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import android.annotation.SuppressLint;
+
import java.io.IOException;
import java.io.OutputStream;
@@ -26,6 +28,7 @@
*
* @hide
*/
+@SuppressLint("AndroidFrameworkBluetoothPermission")
/*package*/ final class BluetoothOutputStream extends OutputStream {
private BluetoothSocket mSocket;
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index ecd718c..c41c9de 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -22,6 +22,8 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -74,10 +76,11 @@
*
* <p> {@link #EXTRA_LOCAL_ROLE} can be one of {@link #LOCAL_NAP_ROLE} or
* {@link #LOCAL_PANU_ROLE}
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
@SuppressLint("ActionValue")
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
@@ -102,9 +105,10 @@
*
* <p> {@link #EXTRA_TETHERING_STATE} can be any of {@link #TETHERING_STATE_OFF} or
* {@link #TETHERING_STATE_ON}
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_TETHERING_STATE_CHANGED =
"android.bluetooth.action.TETHERING_STATE_CHANGED";
@@ -236,6 +240,11 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothPan service = getService();
@@ -274,6 +283,8 @@
* @hide
*/
@UnsupportedAppUsage
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothPan service = getService();
@@ -302,7 +313,11 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -330,7 +345,11 @@
*/
@SystemApi
@Override
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothPan service = getService();
@@ -351,7 +370,12 @@
* @hide
*/
@Override
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothPan service = getService();
@@ -396,7 +420,12 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.TETHER_PRIVILEGED,
+ })
public void setBluetoothTethering(boolean value) {
String pkgName = mContext.getOpPackageName();
if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
@@ -417,7 +446,8 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isTetheringOn() {
if (VDBG) log("isTetheringOn()");
final IBluetoothPan service = getService();
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 6e5c45f..e41eb4f 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -22,6 +22,8 @@
import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -82,8 +84,6 @@
* can be any of {@link BluetoothProfile#STATE_DISCONNECTED},
* {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED},
* {@link BluetoothProfile#STATE_DISCONNECTING}.
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
*
* @hide
*/
@@ -110,6 +110,7 @@
*/
public static final int RESULT_CANCELED = 2;
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
new IBluetoothStateChangeCallback.Stub() {
public void onBluetoothStateChange(boolean up) {
@@ -142,6 +143,7 @@
doBind();
}
+ @SuppressLint("AndroidFrameworkRequiresPermission")
boolean doBind() {
synchronized (mConnection) {
try {
@@ -216,6 +218,8 @@
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
log("getConnectedDevices()");
final IBluetoothPbap service = mService;
@@ -262,6 +266,8 @@
* @hide
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
log("getDevicesMatchingConnectionStates: states=" + Arrays.toString(states));
final IBluetoothPbap service = mService;
@@ -294,7 +300,11 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -324,6 +334,8 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
log("disconnect()");
final IBluetoothPbap service = mService;
@@ -340,6 +352,7 @@
return false;
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
log("Proxy object connected");
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index f356da1..85b8650 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -19,6 +19,8 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -160,6 +162,8 @@
* @return list of connected devices
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) {
log("getConnectedDevices()");
@@ -185,6 +189,8 @@
* @return list of matching devices
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) {
log("getDevicesMatchingStates()");
@@ -210,6 +216,8 @@
* @return device connection state
*/
@Override
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (DBG) {
log("getConnectionState(" + device + ")");
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 201d6c4..161c843 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-
package android.bluetooth;
-import android.Manifest;
import android.annotation.IntDef;
-import android.annotation.RequiresPermission;
+import android.annotation.RequiresNoPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -300,7 +298,6 @@
*
* @return List of devices. The list will be empty on error.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
public List<BluetoothDevice> getConnectedDevices();
/**
@@ -314,7 +311,6 @@
* #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
* @return List of devices. The list will be empty on error.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states);
/**
@@ -324,7 +320,6 @@
* @return State of the profile connection. One of {@link #STATE_CONNECTED}, {@link
* #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH)
@BtProfileState int getConnectionState(BluetoothDevice device);
/**
@@ -339,6 +334,7 @@
* @param profile - One of {@link #HEADSET} or {@link #A2DP}
* @param proxy - One of {@link BluetoothHeadset} or {@link BluetoothA2dp}
*/
+ @RequiresNoPermission
public void onServiceConnected(int profile, BluetoothProfile proxy);
/**
@@ -347,6 +343,7 @@
*
* @param profile - One of {@link #HEADSET} or {@link #A2DP}
*/
+ @RequiresNoPermission
public void onServiceDisconnected(int profile);
}
diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java
index 863fd36..b20ab75 100644
--- a/core/java/android/bluetooth/BluetoothProfileConnector.java
+++ b/core/java/android/bluetooth/BluetoothProfileConnector.java
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +32,7 @@
* @param <T> The Bluetooth profile interface for this connection.
* @hide
*/
+@SuppressLint("AndroidFrameworkBluetoothPermission")
public abstract class BluetoothProfileConnector<T> {
private final int mProfileId;
private BluetoothProfile.ServiceListener mServiceListener;
@@ -78,6 +80,7 @@
mServiceName = serviceName;
}
+ @SuppressLint("AndroidFrameworkRequiresPermission")
private boolean doBind() {
synchronized (mConnection) {
if (mService == null) {
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index 0d70dbd..87da22c 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -17,7 +17,11 @@
package android.bluetooth;
import android.Manifest;
+import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Binder;
@@ -61,11 +65,11 @@
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
*
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
- *
* @hide
*/
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
@@ -140,6 +144,8 @@
* connected to the Sap service.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getState() {
if (VDBG) log("getState()");
final IBluetoothSap service = getService();
@@ -163,6 +169,8 @@
* this proxy object is not connected to the Sap service.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
final IBluetoothSap service = getService();
@@ -186,6 +194,8 @@
*
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
final IBluetoothSap service = getService();
@@ -208,6 +218,7 @@
*
* @hide
*/
+ @RequiresNoPermission
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")" + "not supported for SAPS");
return false;
@@ -221,6 +232,8 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothSap service = getService();
@@ -242,6 +255,8 @@
* @return list of connected devices
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothSap service = getService();
@@ -263,6 +278,8 @@
* @return list of matching devices
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothSap service = getService();
@@ -284,6 +301,8 @@
* @return device connection state
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothSap service = getService();
@@ -310,7 +329,11 @@
* @return true if priority is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -328,7 +351,11 @@
* @return true if connectionPolicy is set, false on error
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean setConnectionPolicy(BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -359,7 +386,11 @@
* @return priority of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -376,7 +407,11 @@
* @return connection policy of the device
* @hide
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothSap service = getService();
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index 5c1bcaf..bb4e354 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.ParcelUuid;
@@ -62,9 +63,6 @@
* safe. In particular, {@link #close} will always immediately abort ongoing
* operations and close the server socket.
*
- * <p class="note"><strong>Note:</strong>
- * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For more information about using Bluetooth, read the
@@ -73,6 +71,7 @@
*
* {@see BluetoothSocket}
*/
+@SuppressLint("AndroidFrameworkBluetoothPermission")
public final class BluetoothServerSocket implements Closeable {
private static final String TAG = "BluetoothServerSocket";
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 65381db..bb409d5 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -16,6 +16,10 @@
package android.bluetooth;
+import android.annotation.RequiresNoPermission;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.LocalSocket;
import android.os.Build;
@@ -70,9 +74,6 @@
* safe. In particular, {@link #close} will always immediately abort ongoing
* operations and close the socket.
*
- * <p class="note"><strong>Note:</strong>
- * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
- *
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For more information about using Bluetooth, read the
@@ -326,6 +327,7 @@
*
* @return remote device
*/
+ @RequiresNoPermission
public BluetoothDevice getRemoteDevice() {
return mDevice;
}
@@ -338,6 +340,7 @@
*
* @return InputStream
*/
+ @RequiresNoPermission
public InputStream getInputStream() throws IOException {
return mInputStream;
}
@@ -350,6 +353,7 @@
*
* @return OutputStream
*/
+ @RequiresNoPermission
public OutputStream getOutputStream() throws IOException {
return mOutputStream;
}
@@ -360,6 +364,7 @@
*
* @return true if connected false if not connected
*/
+ @RequiresNoPermission
public boolean isConnected() {
return mSocketState == SocketState.CONNECTED;
}
@@ -386,6 +391,8 @@
*
* @throws IOException on error, for example connection failure
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void connect() throws IOException {
if (mDevice == null) throw new IOException("Connect is called on null device");
@@ -427,6 +434,7 @@
* Currently returns unix errno instead of throwing IOException,
* so that BluetoothAdapter can check the error code for EADDRINUSE
*/
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
/*package*/ int bindListen() {
int ret;
if (mSocketState == SocketState.CLOSED) return EBADFD;
@@ -635,6 +643,7 @@
*
* @return the maximum supported Transmit packet size for the underlying transport.
*/
+ @RequiresNoPermission
public int getMaxTransmitPacketSize() {
return mMaxTxPacketSize;
}
@@ -647,6 +656,7 @@
*
* @return the maximum supported Receive packet size for the underlying transport.
*/
+ @RequiresNoPermission
public int getMaxReceivePacketSize() {
return mMaxRxPacketSize;
}
@@ -656,6 +666,7 @@
*
* @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
*/
+ @RequiresNoPermission
public int getConnectionType() {
if (mType == TYPE_L2CAP_LE) {
// Treat the LE CoC to be the same type as L2CAP.
@@ -672,6 +683,7 @@
* generate SPP SDP record.
* @hide
*/
+ @RequiresNoPermission
public void setExcludeSdp(boolean excludeSdp) {
mExcludeSdp = excludeSdp;
}
@@ -682,6 +694,8 @@
* connection. This function is currently used for testing only.
* @hide
*/
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void requestMaximumTxDataLength() throws IOException {
if (mDevice == null) {
throw new IOException("requestMaximumTxDataLength is called on null device");
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index d82cf19..bc3754a 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.ParcelUuid;
@@ -34,6 +35,7 @@
* @hide
*/
@SystemApi
+@SuppressLint("AndroidFrameworkBluetoothPermission")
public final class BluetoothUuid {
/* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
index 08d694e..d6868e0 100644
--- a/core/java/android/bluetooth/OobData.java
+++ b/core/java/android/bluetooth/OobData.java
@@ -830,7 +830,7 @@
@Nullable
@SystemApi
public byte[] getLeAppearance() {
- return mLeTemporaryKey;
+ return mLeAppearance;
}
/**
diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java
new file mode 100644
index 0000000..c508c2c
--- /dev/null
+++ b/core/java/android/bluetooth/annotations/RequiresBluetoothAdvertisePermission.java
@@ -0,0 +1,39 @@
+/*
+ * 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.bluetooth.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+import android.os.Build;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher,
+ * this requires the {@link Manifest.permission#BLUETOOTH_ADVERTISE}
+ * permission which can be gained with
+ * {@link android.app.Activity#requestPermissions(String[], int)}.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, FIELD})
+public @interface RequiresBluetoothAdvertisePermission {
+}
diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java
new file mode 100644
index 0000000..e159eaa
--- /dev/null
+++ b/core/java/android/bluetooth/annotations/RequiresBluetoothConnectPermission.java
@@ -0,0 +1,39 @@
+/*
+ * 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.bluetooth.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+import android.os.Build;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher,
+ * this requires the {@link Manifest.permission#BLUETOOTH_CONNECT}
+ * permission which can be gained with
+ * {@link android.app.Activity#requestPermissions(String[], int)}.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, FIELD})
+public @interface RequiresBluetoothConnectPermission {
+}
diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java
new file mode 100644
index 0000000..2bb3204
--- /dev/null
+++ b/core/java/android/bluetooth/annotations/RequiresBluetoothLocationPermission.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc In addition, this requires either the
+ * {@link Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission or a strong assertion that you will never derive the
+ * physical location of the device. You can make this assertion by
+ * declaring {@code usesPermissionFlags="neverForLocation"} on the
+ * relevant {@code <uses-permission>} manifest tag, but it may
+ * restrict the types of Bluetooth devices you can interact with.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, FIELD})
+public @interface RequiresBluetoothLocationPermission {
+}
diff --git a/core/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java b/core/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java
new file mode 100644
index 0000000..800ff39
--- /dev/null
+++ b/core/java/android/bluetooth/annotations/RequiresBluetoothScanPermission.java
@@ -0,0 +1,39 @@
+/*
+ * 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.bluetooth.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+import android.os.Build;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc For apps targeting {@link Build.VERSION_CODES#S} or or higher,
+ * this requires the {@link Manifest.permission#BLUETOOTH_SCAN}
+ * permission which can be gained with
+ * {@link android.app.Activity#requestPermissions(String[], int)}.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, FIELD})
+public @interface RequiresBluetoothScanPermission {
+}
diff --git a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java b/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java
new file mode 100644
index 0000000..9adf695
--- /dev/null
+++ b/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothAdminPermission.java
@@ -0,0 +1,39 @@
+/*
+ * 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.bluetooth.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+import android.os.Build;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc For apps targeting {@link Build.VERSION_CODES#R} or lower, this
+ * requires the {@link Manifest.permission#BLUETOOTH_ADMIN}
+ * permission which can be gained with a simple
+ * {@code <uses-permission>} manifest tag.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, FIELD})
+public @interface RequiresLegacyBluetoothAdminPermission {
+}
diff --git a/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java b/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java
new file mode 100644
index 0000000..79621c3
--- /dev/null
+++ b/core/java/android/bluetooth/annotations/RequiresLegacyBluetoothPermission.java
@@ -0,0 +1,39 @@
+/*
+ * 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.bluetooth.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.Manifest;
+import android.os.Build;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * @memberDoc For apps targeting {@link Build.VERSION_CODES#R} or lower, this
+ * requires the {@link Manifest.permission#BLUETOOTH} permission
+ * which can be gained with a simple {@code <uses-permission>}
+ * manifest tag.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, FIELD})
+public @interface RequiresLegacyBluetoothPermission {
+}
diff --git a/core/java/android/bluetooth/le/AdvertisingSet.java b/core/java/android/bluetooth/le/AdvertisingSet.java
index 1df35e1..d7e48ca 100644
--- a/core/java/android/bluetooth/le/AdvertisingSet.java
+++ b/core/java/android/bluetooth/le/AdvertisingSet.java
@@ -16,9 +16,14 @@
package android.bluetooth.le;
+import android.annotation.RequiresNoPermission;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManager;
+import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.os.RemoteException;
import android.util.Log;
@@ -27,9 +32,6 @@
* <p>
* To get an instance of {@link AdvertisingSet}, call the
* {@link BluetoothLeAdvertiser#startAdvertisingSet} method.
- * <p>
- * <b>Note:</b> Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
*
* @see AdvertiseData
*/
@@ -58,8 +60,6 @@
/**
* Enables Advertising. This method returns immediately, the operation status is
* delivered through {@code callback.onAdvertisingEnabled()}.
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param enable whether the advertising should be enabled (true), or disabled (false)
* @param duration advertising duration, in 10ms unit. Valid range is from 1 (10ms) to 65535
@@ -68,6 +68,9 @@
* controller shall attempt to send prior to terminating the extended advertising, even if the
* duration has not expired. Valid range is from 1 to 255.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void enableAdvertising(boolean enable, int duration,
int maxExtendedAdvertisingEvents) {
try {
@@ -90,6 +93,9 @@
* three bytes will be added for flags. If the update takes place when the advertising set is
* enabled, the data can be maximum 251 bytes long.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void setAdvertisingData(AdvertiseData advertiseData) {
try {
mGatt.setAdvertisingData(mAdvertiserId, advertiseData);
@@ -107,6 +113,9 @@
* exceed {@link BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place
* when the advertising set is enabled, the data can be maximum 251 bytes long.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void setScanResponseData(AdvertiseData scanResponse) {
try {
mGatt.setScanResponseData(mAdvertiserId, scanResponse);
@@ -122,6 +131,9 @@
*
* @param parameters advertising set parameters.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void setAdvertisingParameters(AdvertisingSetParameters parameters) {
try {
mGatt.setAdvertisingParameters(mAdvertiserId, parameters);
@@ -135,6 +147,9 @@
* periodic advertising is not enabled. This method returns immediately, the operation
* status is delivered through {@code callback.onPeriodicAdvertisingParametersUpdated()}.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) {
try {
mGatt.setPeriodicAdvertisingParameters(mAdvertiserId, parameters);
@@ -153,6 +168,9 @@
* BluetoothAdapter#getLeMaximumAdvertisingDataLength}. If the update takes place when the
* periodic advertising is enabled for this set, the data can be maximum 251 bytes long.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void setPeriodicAdvertisingData(AdvertiseData periodicData) {
try {
mGatt.setPeriodicAdvertisingData(mAdvertiserId, periodicData);
@@ -168,6 +186,9 @@
* @param enable whether the periodic advertising should be enabled (true), or disabled
* (false).
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void setPeriodicAdvertisingEnabled(boolean enable) {
try {
mGatt.setPeriodicAdvertisingEnable(mAdvertiserId, enable);
@@ -181,10 +202,9 @@
* This method is exposed only for Bluetooth PTS tests, no app or system service
* should ever use it.
*
- * This method requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission.
- *
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public void getOwnAddress() {
try {
mGatt.getOwnAddress(mAdvertiserId);
@@ -198,6 +218,7 @@
*
* @hide
*/
+ @RequiresNoPermission
public int getAdvertiserId() {
return mAdvertiserId;
}
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 5f166f4..ff279d8 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -16,11 +16,16 @@
package android.bluetooth.le;
+import android.annotation.RequiresNoPermission;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManager;
+import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
@@ -38,9 +43,6 @@
* <p>
* To get an instance of {@link BluetoothLeAdvertiser}, call the
* {@link BluetoothAdapter#getBluetoothLeAdvertiser()} method.
- * <p>
- * <b>Note:</b> Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
- * permission.
*
* @see AdvertiseData
*/
@@ -81,13 +83,14 @@
/**
* Start Bluetooth LE Advertising. On success, the {@code advertiseData} will be broadcasted.
* Returns immediately, the operation status is delivered through {@code callback}.
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
*
* @param settings Settings for Bluetooth LE advertising.
* @param advertiseData Advertisement data to be broadcasted.
* @param callback Callback for advertising status.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void startAdvertising(AdvertiseSettings settings,
AdvertiseData advertiseData, final AdvertiseCallback callback) {
startAdvertising(settings, advertiseData, null, callback);
@@ -98,14 +101,15 @@
* operation succeeds. The {@code scanResponse} is returned when a scanning device sends an
* active scan request. This method returns immediately, the operation status is delivered
* through {@code callback}.
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param settings Settings for Bluetooth LE advertising.
* @param advertiseData Advertisement data to be advertised in advertisement packet.
* @param scanResponse Scan response associated with the advertisement data.
* @param callback Callback for advertising status.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void startAdvertising(AdvertiseSettings settings,
AdvertiseData advertiseData, AdvertiseData scanResponse,
final AdvertiseCallback callback) {
@@ -160,6 +164,10 @@
}
}
+ @SuppressLint({
+ "AndroidFrameworkBluetoothPermission",
+ "AndroidFrameworkRequiresPermission",
+ })
AdvertisingSetCallback wrapOldCallback(AdvertiseCallback callback, AdvertiseSettings settings) {
return new AdvertisingSetCallback() {
@Override
@@ -192,11 +200,12 @@
/**
* Stop Bluetooth LE advertising. The {@code callback} must be the same one use in
* {@link BluetoothLeAdvertiser#startAdvertising}.
- * <p>
- * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
*
* @param callback {@link AdvertiseCallback} identifies the advertising instance to stop.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void stopAdvertising(final AdvertiseCallback callback) {
synchronized (mLegacyAdvertisers) {
if (callback == null) {
@@ -232,6 +241,9 @@
* size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
* feature is made when it's not supported by the controller.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void startAdvertisingSet(AdvertisingSetParameters parameters,
AdvertiseData advertiseData, AdvertiseData scanResponse,
PeriodicAdvertisingParameters periodicParameters,
@@ -262,6 +274,9 @@
* size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
* feature is made when it's not supported by the controller.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void startAdvertisingSet(AdvertisingSetParameters parameters,
AdvertiseData advertiseData, AdvertiseData scanResponse,
PeriodicAdvertisingParameters periodicParameters,
@@ -297,6 +312,9 @@
* size, or unsupported advertising PHY is selected, or when attempt to use Periodic Advertising
* feature is made when it's not supported by the controller.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void startAdvertisingSet(AdvertisingSetParameters parameters,
AdvertiseData advertiseData, AdvertiseData scanResponse,
PeriodicAdvertisingParameters periodicParameters,
@@ -337,6 +355,9 @@
* maxExtendedAdvertisingEvents is used on a controller that doesn't support the LE Extended
* Advertising
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void startAdvertisingSet(AdvertisingSetParameters parameters,
AdvertiseData advertiseData, AdvertiseData scanResponse,
PeriodicAdvertisingParameters periodicParameters,
@@ -445,6 +466,9 @@
* Used to dispose of a {@link AdvertisingSet} object, obtained with {@link
* BluetoothLeAdvertiser#startAdvertisingSet}.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
public void stopAdvertisingSet(AdvertisingSetCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
@@ -469,6 +493,7 @@
*
* @hide
*/
+ @RequiresNoPermission
public void cleanup() {
mLegacyAdvertisers.clear();
mCallbackWrappers.clear();
@@ -476,6 +501,8 @@
}
// Compute the size of advertisement data or scan resp
+ @RequiresBluetoothAdvertisePermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADVERTISE)
private int totalBytes(AdvertiseData data, boolean isFlagsIncluded) {
if (data == null) return 0;
// Flags field is omitted if the advertising is not connectable.
@@ -546,8 +573,11 @@
if (data.getIncludeTxPowerLevel()) {
size += OVERHEAD_BYTES_PER_FIELD + 1; // tx power level value is one byte.
}
- if (data.getIncludeDeviceName() && mBluetoothAdapter.getName() != null) {
- size += OVERHEAD_BYTES_PER_FIELD + mBluetoothAdapter.getName().length();
+ if (data.getIncludeDeviceName()) {
+ final int length = mBluetoothAdapter.getNameLengthForAdvertise();
+ if (length >= 0) {
+ size += OVERHEAD_BYTES_PER_FIELD + length;
+ }
}
return size;
}
@@ -556,6 +586,7 @@
return array == null ? 0 : array.length;
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
IAdvertisingSetCallback wrap(AdvertisingSetCallback callback, Handler handler) {
return new IAdvertisingSetCallback.Stub() {
@Override
@@ -680,6 +711,7 @@
};
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private void postStartSetFailure(Handler handler, final AdvertisingSetCallback callback,
final int error) {
handler.post(new Runnable() {
@@ -690,6 +722,7 @@
});
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private void postStartFailure(final AdvertiseCallback callback, final int error) {
mHandler.post(new Runnable() {
@Override
@@ -699,6 +732,7 @@
});
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private void postStartSuccess(final AdvertiseCallback callback,
final AdvertiseSettings settings) {
mHandler.post(new Runnable() {
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 2601cd4..f27f22b 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -16,16 +16,20 @@
package android.bluetooth.le;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManager;
+import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
+import android.bluetooth.annotations.RequiresBluetoothScanPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.content.AttributionSource;
import android.os.Handler;
import android.os.Looper;
@@ -45,9 +49,6 @@
* <p>
* Use {@link BluetoothAdapter#getBluetoothLeScanner()} to get an instance of
* {@link BluetoothLeScanner}.
- * <p>
- * <b>Note:</b> Most of the scan methods here require
- * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
*
* @see ScanFilter
*/
@@ -117,7 +118,10 @@
* @param callback Callback used to deliver scan results.
* @throws IllegalArgumentException If {@code callback} is null.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void startScan(final ScanCallback callback) {
startScan(null, new ScanSettings.Builder().build(), callback);
}
@@ -139,7 +143,10 @@
* @param callback Callback used to deliver scan results.
* @throws IllegalArgumentException If {@code settings} or {@code callback} is null.
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void startScan(List<ScanFilter> filters, ScanSettings settings,
final ScanCallback callback) {
startScan(filters, settings, null, callback, /*callbackIntent=*/ null, null);
@@ -168,7 +175,10 @@
* could not be sent.
* @see #stopScan(PendingIntent)
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public int startScan(@Nullable List<ScanFilter> filters, @Nullable ScanSettings settings,
@NonNull PendingIntent callbackIntent) {
return startScan(filters,
@@ -186,8 +196,13 @@
* @hide
*/
@SystemApi
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
@RequiresPermission(allOf = {
- Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS})
+ android.Manifest.permission.BLUETOOTH_SCAN,
+ android.Manifest.permission.UPDATE_DEVICE_STATS
+ })
public void startScanFromSource(final WorkSource workSource, final ScanCallback callback) {
startScanFromSource(null, new ScanSettings.Builder().build(), workSource, callback);
}
@@ -204,13 +219,20 @@
* @hide
*/
@SystemApi
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
@RequiresPermission(allOf = {
- Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS})
+ android.Manifest.permission.BLUETOOTH_SCAN,
+ android.Manifest.permission.UPDATE_DEVICE_STATS
+ })
+ @SuppressLint("AndroidFrameworkRequiresPermission")
public void startScanFromSource(List<ScanFilter> filters, ScanSettings settings,
final WorkSource workSource, final ScanCallback callback) {
startScan(filters, settings, workSource, callback, null, null);
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
private int startScan(List<ScanFilter> filters, ScanSettings settings,
final WorkSource workSource, final ScanCallback callback,
final PendingIntent callbackIntent,
@@ -268,7 +290,9 @@
*
* @param callback
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void stopScan(ScanCallback callback) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
synchronized (mLeScanClients) {
@@ -289,7 +313,9 @@
* @param callbackIntent The PendingIntent that was used to start the scan.
* @see #startScan(List, ScanSettings, PendingIntent)
*/
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void stopScan(PendingIntent callbackIntent) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
IBluetoothGatt gatt;
@@ -308,6 +334,9 @@
* @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
* used to start scan.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void flushPendingScanResults(ScanCallback callback) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
if (callback == null) {
@@ -328,6 +357,8 @@
* @hide
*/
@SystemApi
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void startTruncatedScan(List<TruncatedFilter> truncatedFilters, ScanSettings settings,
final ScanCallback callback) {
int filterSize = truncatedFilters.size();
@@ -346,6 +377,7 @@
*
* @hide
*/
+ @RequiresNoPermission
public void cleanup() {
mLeScanClients.clear();
}
@@ -353,6 +385,7 @@
/**
* Bluetooth GATT interface callbacks
*/
+ @SuppressLint("AndroidFrameworkRequiresPermission")
private class BleScanCallbackWrapper extends IScannerCallback.Stub {
private static final int REGISTRATION_CALLBACK_TIMEOUT_MILLIS = 2000;
@@ -409,6 +442,7 @@
}
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void stopLeScan() {
synchronized (this) {
if (mScannerId <= 0) {
@@ -425,6 +459,7 @@
}
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
void flushPendingBatchResults() {
synchronized (this) {
if (mScannerId <= 0) {
@@ -555,6 +590,7 @@
}
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private void postCallbackError(final ScanCallback callback, final int errorCode) {
mHandler.post(new Runnable() {
@Override
@@ -595,6 +631,7 @@
return true;
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) {
final int callbackType = settings.getCallbackType();
if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
index 0f1a8e9..26978e3 100644
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
@@ -16,10 +16,15 @@
package android.bluetooth.le;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManager;
+import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
+import android.bluetooth.annotations.RequiresBluetoothScanPermission;
+import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -35,9 +40,6 @@
* <p>
* Use {@link BluetoothAdapter#getPeriodicAdvertisingManager()} to get an
* instance of {@link PeriodicAdvertisingManager}.
- * <p>
- * <b>Note:</b> Most of the methods here require
- * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
*
* @hide
*/
@@ -89,6 +91,10 @@
* @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or
* {@code timeout} is invalid or {@code callback} is null.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void registerSync(ScanResult scanResult, int skip, int timeout,
PeriodicAdvertisingCallback callback) {
registerSync(scanResult, skip, timeout, callback, null);
@@ -113,6 +119,10 @@
* @throws IllegalArgumentException if {@code scanResult} is null or {@code skip} is invalid or
* {@code timeout} is invalid or {@code callback} is null.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresBluetoothLocationPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void registerSync(ScanResult scanResult, int skip, int timeout,
PeriodicAdvertisingCallback callback, Handler handler) {
if (callback == null) {
@@ -170,6 +180,9 @@
* @throws IllegalArgumentException if {@code callback} is null, or not a properly registered
* callback.
*/
+ @RequiresLegacyBluetoothAdminPermission
+ @RequiresBluetoothScanPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void unregisterSync(PeriodicAdvertisingCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback can't be null");
@@ -196,6 +209,7 @@
}
}
+ @SuppressLint("AndroidFrameworkBluetoothPermission")
private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback,
Handler handler) {
return new IPeriodicAdvertisingCallback.Stub() {
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 27c579b..c5c4277 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -16,7 +16,6 @@
package android.bluetooth.le;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
index 794b512..9b8c2ea 100644
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.bluetooth.BluetoothUuid;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.ParcelUuid;
@@ -34,6 +35,7 @@
/**
* Represents a scan record from Bluetooth LE scan.
*/
+@SuppressLint("AndroidFrameworkBluetoothPermission")
public final class ScanRecord {
private static final String TAG = "ScanRecord";
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index 368d1ee..f3e971a 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -20,6 +20,7 @@
import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
+import android.provider.DeviceConfig;
/**
* Bluetooth LE scan settings are passed to {@link BluetoothLeScanner#startScan} to define the
@@ -141,6 +142,12 @@
*/
public static final int PHY_LE_ALL_SUPPORTED = 255;
+ /**
+ * The default floor value for report delays greater than 0 in
+ * {@link Builder#setReportDelay(long)}.
+ */
+ private static final long DEFAULT_REPORT_DELAY_FLOOR = 5000;
+
// Bluetooth LE scan mode.
private int mScanMode;
@@ -345,18 +352,28 @@
}
/**
- * Set report delay timestamp for Bluetooth LE scan.
+ * Set report delay timestamp for Bluetooth LE scan. If set to 0, you will be notified of
+ * scan results immediately. If > 0, scan results are queued up and delivered after the
+ * requested delay or 5000 milliseconds (whichever is higher). Note scan results may be
+ * delivered sooner if the internal buffers fill up.
*
- * @param reportDelayMillis Delay of report in milliseconds. Set to 0 to be notified of
- * results immediately. Values > 0 causes the scan results to be queued up and delivered
- * after the requested delay or when the internal buffers fill up.
- * @throws IllegalArgumentException If {@code reportDelayMillis} < 0.
+ * @param reportDelayMillis how frequently scan results should be delivered in
+ * milliseconds
+ * @throws IllegalArgumentException if {@code reportDelayMillis} < 0
*/
public Builder setReportDelay(long reportDelayMillis) {
if (reportDelayMillis < 0) {
throw new IllegalArgumentException("reportDelay must be > 0");
}
- mReportDelayMillis = reportDelayMillis;
+
+ long floor = DeviceConfig.getLong(DeviceConfig.NAMESPACE_BLUETOOTH, "report_delay",
+ DEFAULT_REPORT_DELAY_FLOOR);
+
+ if (reportDelayMillis > 0 && reportDelayMillis < floor) {
+ mReportDelayMillis = floor;
+ } else {
+ mReportDelayMillis = reportDelayMillis;
+ }
return this;
}
diff --git a/core/java/android/bluetooth/le/TruncatedFilter.java b/core/java/android/bluetooth/le/TruncatedFilter.java
index a753aa6..93f526b 100644
--- a/core/java/android/bluetooth/le/TruncatedFilter.java
+++ b/core/java/android/bluetooth/le/TruncatedFilter.java
@@ -16,6 +16,7 @@
package android.bluetooth.le;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import java.util.List;
@@ -26,6 +27,7 @@
* @hide
*/
@SystemApi
+@SuppressLint("AndroidFrameworkBluetoothPermission")
public final class TruncatedFilter {
private final ScanFilter mFilter;
private final List<ResultStorageDescriptor> mStorageDescriptors;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 09ac810..a88c9ed 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2208,6 +2208,17 @@
}
/**
+ * Like {@link #sendBroadcastMultiplePermissions(Intent, String[])}, but also allows
+ * specification of a list of excluded permissions. This allows sending a broadcast to an
+ * app that has the permissions in `receiverPermissions` but not `excludedPermissions`.
+ * @hide
+ */
+ public void sendBroadcastMultiplePermissions(@NonNull Intent intent,
+ @NonNull String[] receiverPermissions, @Nullable String[] excludedPermissions) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Broadcast the given intent to all interested BroadcastReceivers, allowing
* an array of required permissions to be enforced. This call is asynchronous; it returns
* immediately, and you will continue executing while the receivers are run. No results are
@@ -4694,10 +4705,9 @@
* @hide
* @see #getSystemService(String)
*/
- // TODO(b/176208267): change it back to translation before S release.
@SystemApi
@SuppressLint("ServiceName")
- public static final String TRANSLATION_MANAGER_SERVICE = "transformer";
+ public static final String TRANSLATION_MANAGER_SERVICE = "translation";
/**
* Official published name of the translation service which supports ui translation function.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 8936d0c..dddcbea 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -493,6 +493,13 @@
/** @hide */
@Override
+ public void sendBroadcastMultiplePermissions(@NonNull Intent intent,
+ @NonNull String[] receiverPermissions, @Nullable String[] excludedPermissions) {
+ mBase.sendBroadcastMultiplePermissions(intent, receiverPermissions, excludedPermissions);
+ }
+
+ /** @hide */
+ @Override
public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
String[] receiverPermissions) {
mBase.sendBroadcastAsUserMultiplePermissions(intent, user, receiverPermissions);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index f8d407d..3cc7ff8 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5368,8 +5368,8 @@
public static final String EXTRA_ATTRIBUTION_TAGS = "android.intent.extra.ATTRIBUTION_TAGS";
/**
- * A long representing the start timestamp (in millis) of the permission usage when used with
- * {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
+ * A long representing the start timestamp (epoch time in millis) of the permission usage
+ * when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
*
* @hide
*/
@@ -5377,8 +5377,8 @@
public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
/**
- * A long representing the end timestamp (in millis) of the permission usage when used with
- * {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
+ * A long representing the end timestamp (epoch time in millis) of the permission usage when
+ * used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
*
* @hide
*/
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6ad204e..9934b2a 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -2130,6 +2130,7 @@
*
* @hide
*/
+ @SuppressWarnings("AutoBoxing")
@SystemApi
@Nullable
public Boolean hasRequestRawExternalStorageAccess() {
diff --git a/core/java/android/content/pm/DataLoaderParams.java b/core/java/android/content/pm/DataLoaderParams.java
index f808cfd..a6d3b45 100644
--- a/core/java/android/content/pm/DataLoaderParams.java
+++ b/core/java/android/content/pm/DataLoaderParams.java
@@ -24,7 +24,9 @@
* This class represents the parameters used to configure a DataLoader.
*
* {@see android.service.dataloader.DataLoaderService.DataLoader}
+ * @hide
*/
+@SystemApi
public class DataLoaderParams {
@NonNull
private final DataLoaderParamsParcel mData;
diff --git a/core/java/android/content/pm/InstallationFile.java b/core/java/android/content/pm/InstallationFile.java
index e764020..7e07c45 100644
--- a/core/java/android/content/pm/InstallationFile.java
+++ b/core/java/android/content/pm/InstallationFile.java
@@ -18,16 +18,18 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
/**
* Definition of a file in a streaming installation session.
* You can use this class to retrieve the information of such a file, such as its name, size and
* metadata. These file attributes will be consistent with those used in:
- * {@code android.content.pm.PackageInstaller.Session#addFile}, when the file was first added
- * into the session.
+ * {@code PackageInstaller.Session#addFile}, when the file was first added into the session.
*
* @see android.content.pm.PackageInstaller.Session#addFile
+ * @hide
*/
+@SystemApi
public final class InstallationFile {
private final @NonNull InstallationFileParcel mParcel;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 8b380b7..5157e08 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -229,12 +229,15 @@
/**
* Type of DataLoader for this session. Will be one of
- * {@link #DATA_LOADER_TYPE_NONE}, {@link #DATA_LOADER_TYPE_STREAMING}.
+ * {@link #DATA_LOADER_TYPE_NONE}, {@link #DATA_LOADER_TYPE_STREAMING},
+ * {@link #DATA_LOADER_TYPE_INCREMENTAL}.
* <p>
* See the individual types documentation for details.
*
* @see Intent#getIntExtra(String, int)
+ * {@hide}
*/
+ @SystemApi
public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
/**
@@ -242,6 +245,7 @@
* Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
*
* @see #EXTRA_SESSION_ID
+ * {@hide}
*/
public static final int STATUS_PENDING_STREAMING = -2;
@@ -344,21 +348,25 @@
* Default value, non-streaming installation session.
*
* @see #EXTRA_DATA_LOADER_TYPE
+ * {@hide}
*/
+ @SystemApi
public static final int DATA_LOADER_TYPE_NONE = DataLoaderType.NONE;
/**
* Streaming installation using data loader.
*
* @see #EXTRA_DATA_LOADER_TYPE
+ * {@hide}
*/
+ @SystemApi
public static final int DATA_LOADER_TYPE_STREAMING = DataLoaderType.STREAMING;
/**
* Streaming installation using Incremental FileSystem.
*
* @see #EXTRA_DATA_LOADER_TYPE
- * @hide
+ * {@hide}
*/
@SystemApi
public static final int DATA_LOADER_TYPE_INCREMENTAL = DataLoaderType.INCREMENTAL;
@@ -366,13 +374,18 @@
/**
* Target location for the file in installation session is /data/app/<packageName>-<id>.
* This is the intended location for APKs.
+ * Requires permission to install packages.
+ * {@hide}
*/
+ @SystemApi
public static final int LOCATION_DATA_APP = InstallationFileLocation.DATA_APP;
/**
* Target location for the file in installation session is
* /data/media/<userid>/Android/obb/<packageName>. This is the intended location for OBBs.
+ * {@hide}
*/
+ @SystemApi
public static final int LOCATION_MEDIA_OBB = InstallationFileLocation.MEDIA_OBB;
/**
@@ -380,7 +393,9 @@
* /data/media/<userid>/Android/data/<packageName>.
* This is the intended location for application data.
* Can only be used by an app itself running under specific user.
+ * {@hide}
*/
+ @SystemApi
public static final int LOCATION_MEDIA_DATA = InstallationFileLocation.MEDIA_DATA;
/** @hide */
@@ -1152,7 +1167,10 @@
/**
* @return data loader params or null if the session is not using one.
+ * {@hide}
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
public @Nullable DataLoaderParams getDataLoaderParams() {
try {
DataLoaderParamsParcel data = mSession.getDataLoaderParams();
@@ -1189,7 +1207,11 @@
* @throws IllegalStateException if called for non-streaming session
*
* @see android.content.pm.InstallationFile
+ *
+ * {@hide}
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
public void addFile(@FileLocation int location, @NonNull String name, long lengthBytes,
@NonNull byte[] metadata, @Nullable byte[] signature) {
try {
@@ -1209,8 +1231,11 @@
* @param name name of a file, e.g. split.
* @throws SecurityException if called after the session has been
* sealed or abandoned
- * @throws IllegalStateException if called for non-streaming session
+ * @throws IllegalStateException if called for non-DataLoader session
+ * {@hide}
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
public void removeFile(@FileLocation int location, @NonNull String name) {
try {
mSession.removeFile(location, name);
@@ -2026,7 +2051,13 @@
* staging folder.
*
* @see android.service.dataloader.DataLoaderService.DataLoader
+ *
+ * {@hide}
*/
+ @SystemApi
+ @RequiresPermission(allOf = {
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.USE_INSTALLER_V2})
public void setDataLoaderParams(@NonNull DataLoaderParams dataLoaderParams) {
this.dataLoaderParams = dataLoaderParams;
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 5ff1124..86a8a9d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -8923,6 +8923,8 @@
private final @ParseFlags int mFlags;
private AssetManager mCachedAssetManager;
+ private ApkAssets mBaseApkAssets;
+
DefaultSplitAssetLoader(PackageLite pkg, @ParseFlags int flags) {
mBaseCodePath = pkg.baseCodePath;
mSplitCodePaths = pkg.splitCodePaths;
@@ -8953,9 +8955,11 @@
ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null
? mSplitCodePaths.length : 0) + 1];
+ mBaseApkAssets = loadApkAssets(mBaseCodePath, mFlags);
+
// Load the base.
int splitIdx = 0;
- apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags);
+ apkAssets[splitIdx++] = mBaseApkAssets;
// Load any splits.
if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
@@ -8982,6 +8986,11 @@
public void close() throws Exception {
IoUtils.closeQuietly(mCachedAssetManager);
}
+
+ @Override
+ public ApkAssets getBaseApkAssets() {
+ return mBaseApkAssets;
+ }
}
/**
@@ -9085,5 +9094,10 @@
IoUtils.closeQuietly(assets);
}
}
+
+ @Override
+ public ApkAssets getBaseApkAssets() {
+ return mCachedSplitApks[0][0];
+ }
}
}
diff --git a/core/java/android/content/pm/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java
index 98a20f7..52ee4de 100644
--- a/core/java/android/content/pm/PackagePartitions.java
+++ b/core/java/android/content/pm/PackagePartitions.java
@@ -47,7 +47,7 @@
public static final int PARTITION_PRODUCT = 4;
public static final int PARTITION_SYSTEM_EXT = 5;
- @IntDef(flag = true, prefix = { "PARTITION_" }, value = {
+ @IntDef(prefix = { "PARTITION_" }, value = {
PARTITION_SYSTEM,
PARTITION_VENDOR,
PARTITION_ODM,
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index a1ffc0c..0fc6b2b 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -383,10 +383,9 @@
}
try {
- final AssetManager assets = assetLoader.getBaseAssetManager();
final File baseApk = new File(lite.getBaseApkPath());
final ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
- lite.getPath(), assets, flags);
+ lite.getPath(), assetLoader, flags);
if (result.isError()) {
return input.error(result);
}
@@ -442,7 +441,7 @@
final ParseResult<ParsingPackage> result = parseBaseApk(input,
apkFile,
apkFile.getCanonicalPath(),
- assetLoader.getBaseAssetManager(), flags);
+ assetLoader, flags);
if (result.isError()) {
return input.error(result);
}
@@ -458,7 +457,8 @@
}
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
- String codePath, AssetManager assets, int flags) {
+ String codePath, SplitAssetLoader assetLoader, int flags)
+ throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
String volumeUuid = null;
@@ -469,6 +469,7 @@
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
+ final AssetManager assets = assetLoader.getBaseAssetManager();
final int cookie = assets.findCookieForPath(apkPath);
if (cookie == 0) {
return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
@@ -500,12 +501,19 @@
}
}
- ApkAssets apkAssets = assets.getApkAssets()[0];
- if (apkAssets.definesOverlayable()) {
+ ApkAssets apkAssets = assetLoader.getBaseApkAssets();
+ boolean definesOverlayable = false;
+ try {
+ definesOverlayable = apkAssets.definesOverlayable();
+ } catch (IOException ignored) {
+ // Will fail if there's no packages in the ApkAssets, which can be treated as false
+ }
+
+ if (definesOverlayable) {
SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
int size = packageNames.size();
for (int index = 0; index < size; index++) {
- String packageName = packageNames.get(index);
+ String packageName = packageNames.valueAt(index);
Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
for (String overlayable : overlayableToActor.keySet()) {
@@ -2799,7 +2807,15 @@
}
}
+ @SuppressWarnings("AndroidFrameworkCompatChange")
private void convertSplitPermissions(ParsingPackage pkg) {
+ // STOPSHIP(b/183905675): REMOVE THIS TERRIBLE, HORRIBLE, NO GOOD, VERY BAD HACK
+ if ("com.android.chrome".equals(pkg.getPackageName())
+ && pkg.getVersionCode() <= 445500399
+ && pkg.getTargetSdkVersion() > Build.VERSION_CODES.R) {
+ pkg.setTargetSdkVersion(Build.VERSION_CODES.R);
+ }
+
final int listSize = mSplitPermissionInfos.size();
for (int is = 0; is < listSize; is++) {
final PermissionManager.SplitPermissionInfo spi = mSplitPermissionInfos.get(is);
diff --git a/core/java/android/content/pm/parsing/component/ParsedAttribution.java b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
index 3a4aae1..4ec2e73 100644
--- a/core/java/android/content/pm/parsing/component/ParsedAttribution.java
+++ b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
@@ -40,7 +40,7 @@
public static final int MAX_ATTRIBUTION_TAG_LEN = 50;
/** Maximum amount of attributions per package */
- private static final int MAX_NUM_ATTRIBUTIONS = 1000;
+ private static final int MAX_NUM_ATTRIBUTIONS = 10000;
/** Tag of the attribution */
public final @NonNull String tag;
@@ -100,7 +100,7 @@
- // Code below generated by codegen v1.0.22.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -215,8 +215,8 @@
};
@DataClass.Generated(
- time = 1607463855175L,
- codegenVersion = "1.0.22",
+ time = 1618351459610L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java",
inputSignatures = "public static final int MAX_ATTRIBUTION_TAG_LEN\nprivate static final int MAX_NUM_ATTRIBUTIONS\npublic final @android.annotation.NonNull java.lang.String tag\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedAttribution>)\nclass ParsedAttribution extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false)")
@Deprecated
diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
index f3caf60..c1a8396 100644
--- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
+++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
@@ -43,6 +43,8 @@
private final @ParseFlags int mFlags;
private AssetManager mCachedAssetManager;
+ private ApkAssets mBaseApkAssets;
+
public DefaultSplitAssetLoader(PackageLite pkg, @ParseFlags int flags) {
mBaseApkPath = pkg.getBaseApkPath();
mSplitApkPaths = pkg.getSplitApkPaths();
@@ -76,7 +78,7 @@
// Load the base.
int splitIdx = 0;
- apkAssets[splitIdx++] = loadApkAssets(mBaseApkPath, mFlags);
+ apkAssets[splitIdx++] = mBaseApkAssets = loadApkAssets(mBaseApkPath, mFlags);
// Load any splits.
if (!ArrayUtils.isEmpty(mSplitApkPaths)) {
@@ -100,6 +102,11 @@
}
@Override
+ public ApkAssets getBaseApkAssets() {
+ return mBaseApkAssets;
+ }
+
+ @Override
public void close() throws Exception {
IoUtils.closeQuietly(mCachedAssetManager);
}
diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
index 523ca40..e5c2158 100644
--- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -128,6 +128,11 @@
}
@Override
+ public ApkAssets getBaseApkAssets() {
+ return mCachedSplitApks[0][0];
+ }
+
+ @Override
public void close() throws Exception {
for (AssetManager assets : mCachedAssetManagers) {
IoUtils.closeQuietly(assets);
diff --git a/core/java/android/content/pm/split/SplitAssetLoader.java b/core/java/android/content/pm/split/SplitAssetLoader.java
index 108fb95..7584e15f 100644
--- a/core/java/android/content/pm/split/SplitAssetLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetLoader.java
@@ -16,6 +16,7 @@
package android.content.pm.split;
import android.content.pm.PackageParser;
+import android.content.res.ApkAssets;
import android.content.res.AssetManager;
/**
@@ -27,4 +28,6 @@
public interface SplitAssetLoader extends AutoCloseable {
AssetManager getBaseAssetManager() throws PackageParser.PackageParserException;
AssetManager getSplitAssetManager(int splitIdx) throws PackageParser.PackageParserException;
+
+ ApkAssets getBaseApkAssets();
}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
index adc668f..77bd147 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -32,7 +32,9 @@
import com.android.internal.util.CollectionUtils;
+import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -350,6 +352,8 @@
*
* The set will be ordered from lowest to highest priority.
*
+ * @param domain The host to query for. An invalid domain will result in an empty set.
+ *
* @hide
*/
@SystemApi
@@ -357,11 +361,11 @@
@RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
public SortedSet<DomainOwner> getOwnersForDomain(@NonNull String domain) {
try {
+ Objects.requireNonNull(domain);
final List<DomainOwner> orderedList = mDomainVerificationManager.getOwnersForDomain(
domain, mContext.getUserId());
SortedSet<DomainOwner> set = new TreeSet<>(
- (first, second) -> Integer.compare(orderedList.indexOf(first),
- orderedList.indexOf(second)));
+ Comparator.comparingInt(orderedList::indexOf));
set.addAll(orderedList);
return set;
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 3c11d8e..bc2dcb3 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -571,10 +571,10 @@
}
int sensorHandle = (sensor == null) ? -1 : sensor.getHandle();
- if (Compatibility.isChangeEnabled(CHANGE_ID_SAMPLING_RATE_SENSORS_PERMISSION)
- && rate > CAPPED_SAMPLING_RATE_LEVEL
+ if (rate > CAPPED_SAMPLING_RATE_LEVEL
&& mIsPackageDebuggable
- && !mHasHighSamplingRateSensorsPermission) {
+ && !mHasHighSamplingRateSensorsPermission
+ && Compatibility.isChangeEnabled(CHANGE_ID_SAMPLING_RATE_SENSORS_PERMISSION)) {
throw new SecurityException("To use the sampling rate level " + rate
+ ", app needs to declare the normal permission"
+ " HIGH_SAMPLING_RATE_SENSORS.");
@@ -782,10 +782,10 @@
Sensor sensor, int rateUs, int maxBatchReportLatencyUs) {
if (mNativeSensorEventQueue == 0) throw new NullPointerException();
if (sensor == null) throw new NullPointerException();
- if (Compatibility.isChangeEnabled(CHANGE_ID_SAMPLING_RATE_SENSORS_PERMISSION)
- && rateUs < CAPPED_SAMPLING_PERIOD_US
+ if (rateUs < CAPPED_SAMPLING_PERIOD_US
&& mManager.mIsPackageDebuggable
- && !mManager.mHasHighSamplingRateSensorsPermission) {
+ && !mManager.mHasHighSamplingRateSensorsPermission
+ && Compatibility.isChangeEnabled(CHANGE_ID_SAMPLING_RATE_SENSORS_PERMISSION)) {
throw new SecurityException("To use the sampling rate of " + rateUs
+ " microseconds, app needs to declare the normal permission"
+ " HIGH_SAMPLING_RATE_SENSORS.");
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 0af18df..7648cf2 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.security.identity.IdentityCredential;
-import android.security.keystore.AndroidKeyStoreProvider;
+import android.security.keystore2.AndroidKeyStoreProvider;
import java.security.Signature;
diff --git a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl
index 62d727c..1268658 100644
--- a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl
@@ -16,11 +16,9 @@
package android.hardware.biometrics;
-import android.hardware.biometrics.BiometricSourceType;
-
/**
* @hide
*/
oneway interface IBiometricEnabledOnKeyguardCallback {
- void onChanged(in BiometricSourceType type, boolean enabled, int userId);
+ void onChanged(boolean enabled, int userId);
}
\ No newline at end of file
diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java
index 11c426a..835b3fd 100644
--- a/core/java/android/hardware/display/DeviceProductInfo.java
+++ b/core/java/android/hardware/display/DeviceProductInfo.java
@@ -74,12 +74,26 @@
Integer modelYear,
ManufactureDate manufactureDate,
int connectionToSinkType) {
- this.mName = name;
- this.mManufacturerPnpId = manufacturerPnpId;
- this.mProductId = productId;
- this.mModelYear = modelYear;
- this.mManufactureDate = manufactureDate;
- this.mConnectionToSinkType = connectionToSinkType;
+ mName = name;
+ mManufacturerPnpId = manufacturerPnpId;
+ mProductId = productId;
+ mModelYear = modelYear;
+ mManufactureDate = manufactureDate;
+ mConnectionToSinkType = connectionToSinkType;
+ }
+
+ public DeviceProductInfo(
+ @Nullable String name,
+ @NonNull String manufacturerPnpId,
+ @NonNull String productId,
+ @IntRange(from = 1990) int modelYear,
+ @ConnectionToSinkType int connectionToSinkType) {
+ mName = name;
+ mManufacturerPnpId = Objects.requireNonNull(manufacturerPnpId);
+ mProductId = Objects.requireNonNull(productId);
+ mModelYear = modelYear;
+ mManufactureDate = null;
+ mConnectionToSinkType = connectionToSinkType;
}
private DeviceProductInfo(Parcel in) {
@@ -100,6 +114,9 @@
}
/**
+ * Returns the Manufacturer Plug and Play ID. This ID identifies the manufacture according to
+ * the list: https://uefi.org/PNP_ID_List. It consist of 3 characters, each character
+ * is an uppercase letter (A-Z).
* @return Manufacturer Plug and Play ID.
*/
@NonNull
diff --git a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
index bdd5ab6..e7d76f6 100644
--- a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
+++ b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
@@ -63,7 +63,6 @@
*
* @hide
*/
- // TODO(b/110094868): unhide and add @SystemApi for Q
public interface SetSystemAudioModeCallback {
/**
* Called when the input was changed.
@@ -74,7 +73,6 @@
}
/** @hide */
- // TODO(b/110094868): unhide and add @SystemApi for Q
@Override
public int getDeviceType() {
return HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
@@ -143,7 +141,6 @@
*
* @hide
*/
- // TODO(b/110094868): unhide and add @SystemApi for Q
public void setSystemAudioMode(boolean state, @NonNull SetSystemAudioModeCallback callback) {
// TODO(amyjojo): implement this when needed.
}
@@ -156,7 +153,6 @@
*
* @hide
*/
- // TODO(b/110094868): unhide and add @SystemApi for Q
public void setSystemAudioModeOnForAudioOnlySource() {
try {
mService.setSystemAudioModeOnForAudioOnlySource();
diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java
index a921215..0c21746 100644
--- a/core/java/android/hardware/hdmi/HdmiClient.java
+++ b/core/java/android/hardware/hdmi/HdmiClient.java
@@ -65,7 +65,6 @@
* @param isPressed true if this is key press event
*
* @hide
- * TODO(b/110094868): unhide for Q
*/
public void sendVolumeKeyEvent(int keyCode, boolean isPressed) {
try {
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index ad71f15..6079c57 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -1009,7 +1009,6 @@
*
* @return {@link HdmiAudioSystemClient} instance. {@code null} on failure.
*
- * TODO(b/110094868): unhide for Q
* @hide
*/
@Nullable
diff --git a/core/java/android/hardware/hdmi/HdmiUtils.java b/core/java/android/hardware/hdmi/HdmiUtils.java
index 8c94b78..2f4378e 100644
--- a/core/java/android/hardware/hdmi/HdmiUtils.java
+++ b/core/java/android/hardware/hdmi/HdmiUtils.java
@@ -24,7 +24,6 @@
/**
* Various utilities related to HDMI CEC.
*
- * TODO(b/110094868): unhide for Q
* @hide
*/
public final class HdmiUtils {
@@ -84,7 +83,6 @@
}
/**
- * TODO(b/110094868): unhide for Q
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@@ -95,49 +93,49 @@
public @interface HdmiAddressRelativePosition {}
/**
* HDMI relative position is not determined.
- * TODO(b/110094868): unhide for Q
+ *
* @hide
*/
public static final int HDMI_RELATIVE_POSITION_UNKNOWN = 0;
/**
* HDMI relative position: directly blow the device.
- * TODO(b/110094868): unhide for Q
+ *
* @hide
*/
public static final int HDMI_RELATIVE_POSITION_DIRECTLY_BELOW = 1;
/**
* HDMI relative position: indirectly below the device.
- * TODO(b/110094868): unhide for Q
+ *
* @hide
*/
public static final int HDMI_RELATIVE_POSITION_BELOW = 2;
/**
* HDMI relative position: the same device.
- * TODO(b/110094868): unhide for Q
+ *
* @hide
*/
public static final int HDMI_RELATIVE_POSITION_SAME = 3;
/**
* HDMI relative position: directly above the device.
- * TODO(b/110094868): unhide for Q
+ *
* @hide
*/
public static final int HDMI_RELATIVE_POSITION_DIRECTLY_ABOVE = 4;
/**
* HDMI relative position: indirectly above the device.
- * TODO(b/110094868): unhide for Q
+ *
* @hide
*/
public static final int HDMI_RELATIVE_POSITION_ABOVE = 5;
/**
* HDMI relative position: directly below a same device.
- * TODO(b/110094868): unhide for Q
+ *
* @hide
*/
public static final int HDMI_RELATIVE_POSITION_SIBLING = 6;
/**
* HDMI relative position: different branch.
- * TODO(b/110094868): unhide for Q
+ *
* @hide
*/
public static final int HDMI_RELATIVE_POSITION_DIFFERENT_BRANCH = 7;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4ee5383..4b8e37c 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -718,7 +718,7 @@
public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
@NonNull IBinder startInputToken) {
- mPrivOps.reportStartInput(startInputToken);
+ mPrivOps.reportStartInputAsync(startInputToken);
if (restarting) {
restartInput(inputConnection, editorInfo);
@@ -819,10 +819,9 @@
if (dispatchOnShowInputRequested(flags, false)) {
showWindow(true);
applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */);
- } else {
- // If user uses hard keyboard, IME button should always be shown.
- setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
}
+ setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+
final boolean isVisible = isInputViewShown();
final boolean visibilityChanged = isVisible != wasVisible;
if (resultReceiver != null) {
diff --git a/core/java/android/net/DnsResolverServiceManager.java b/core/java/android/net/DnsResolverServiceManager.java
deleted file mode 100644
index 1597322..0000000
--- a/core/java/android/net/DnsResolverServiceManager.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net;
-
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.ServiceManager;
-
-import java.util.Objects;
-
-/**
- * Provides a way to obtain the DnsResolver binder objects.
- *
- * @hide
- */
-@SystemApi
-public class DnsResolverServiceManager {
- /**
- * Name to retrieve a {@link android.net.IDnsResolver} IBinder.
- */
- private static final String DNS_RESOLVER_SERVICE = "dnsresolver";
-
- private DnsResolverServiceManager() {}
-
- /**
- * Get an {@link IBinder} representing the DnsResolver stable AIDL interface
- *
- * @param context the context for permission check.
- * @return {@link android.net.IDnsResolver} IBinder.
- */
- @NonNull
- @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
- public static IBinder getService(@NonNull final Context context) {
- Objects.requireNonNull(context);
- context.enforceCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
- "DnsResolverServiceManager");
- try {
- return ServiceManager.getServiceOrThrow(DNS_RESOLVER_SERVICE);
- } catch (ServiceManager.ServiceNotFoundException e) {
- // Catch ServiceManager#ServiceNotFoundException and rethrow IllegalStateException
- // because ServiceManager#ServiceNotFoundException is @hide so that it can't be listed
- // on the system api. Thus, rethrow IllegalStateException if dns resolver service cannot
- // be found.
- throw new IllegalStateException("Cannot find dns resolver service.");
- }
- }
-}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index ec83c4e..053856b 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -479,7 +479,8 @@
* @param overrideMask the bitmask that specifies which of the overrides is being
* set or cleared.
* @param overrideValue the override values to set or clear.
- * @param networkTypes the network types this override applies to.
+ * @param networkTypes the network types this override applies to. If no
+ * network types are specified, override values will be ignored.
* {@see TelephonyManager#getAllNetworkTypes()}
* @param timeoutMillis the timeout after which the requested override will
* be automatically cleared, or {@code 0} to leave in the
diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java
index 8f6510e..da01dcb 100644
--- a/core/java/android/net/NetworkWatchlistManager.java
+++ b/core/java/android/net/NetworkWatchlistManager.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -31,6 +32,7 @@
* Class that manage network watchlist in system.
* @hide
*/
+@TestApi
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@SystemService(Context.NETWORK_WATCHLIST_SERVICE)
public class NetworkWatchlistManager {
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index 5f65d46..662ebb3 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -78,10 +78,8 @@
/**
* An IPsec VPN created by the built-in LegacyVpnRunner.
- * @deprecated new Android devices should use VPN_TYPE_PLATFORM instead.
* @hide
*/
- @Deprecated
@SystemApi(client = MODULE_LIBRARIES)
public static final int TYPE_VPN_LEGACY = 3;
@@ -418,4 +416,4 @@
throw e.rethrowFromSystemServer();
}
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
index 9d3462c..8950c4b 100644
--- a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
@@ -83,7 +83,7 @@
/** Serializes an IkeSessionParams to a PersistableBundle. */
@NonNull
public static PersistableBundle toPersistableBundle(@NonNull IkeSessionParams params) {
- if (params.getConfiguredNetwork() != null || params.getIke3gppExtension() != null) {
+ if (params.getNetwork() != null || params.getIke3gppExtension() != null) {
throw new IllegalStateException(
"Cannot convert a IkeSessionParams with a caller configured network or with"
+ " 3GPP extension enabled");
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index d5cc01a..cb9a3e4 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -24,7 +24,7 @@
import android.nfc.TechListParcel;
import android.nfc.IAppCallback;
import android.nfc.INfcAdapterExtras;
-import android.nfc.INfcControllerAlwaysOnStateCallback;
+import android.nfc.INfcControllerAlwaysOnListener;
import android.nfc.INfcTag;
import android.nfc.INfcCardEmulation;
import android.nfc.INfcFCardEmulation;
@@ -76,6 +76,6 @@
boolean setControllerAlwaysOn(boolean value);
boolean isControllerAlwaysOn();
boolean isControllerAlwaysOnSupported();
- void registerControllerAlwaysOnStateCallback(in INfcControllerAlwaysOnStateCallback callback);
- void unregisterControllerAlwaysOnStateCallback(in INfcControllerAlwaysOnStateCallback callback);
+ void registerControllerAlwaysOnListener(in INfcControllerAlwaysOnListener listener);
+ void unregisterControllerAlwaysOnListener(in INfcControllerAlwaysOnListener listener);
}
diff --git a/core/java/android/nfc/INfcControllerAlwaysOnStateCallback.aidl b/core/java/android/nfc/INfcControllerAlwaysOnListener.aidl
similarity index 87%
rename from core/java/android/nfc/INfcControllerAlwaysOnStateCallback.aidl
rename to core/java/android/nfc/INfcControllerAlwaysOnListener.aidl
index 1e4fdd7..1bb7680 100644
--- a/core/java/android/nfc/INfcControllerAlwaysOnStateCallback.aidl
+++ b/core/java/android/nfc/INfcControllerAlwaysOnListener.aidl
@@ -19,11 +19,11 @@
/**
* @hide
*/
-oneway interface INfcControllerAlwaysOnStateCallback {
+oneway interface INfcControllerAlwaysOnListener {
/**
* Called whenever the controller always on state changes
*
* @param isEnabled true if the state is enabled, false otherwise
*/
- void onControllerAlwaysOnStateChanged(boolean isEnabled);
+ void onControllerAlwaysOnChanged(boolean isEnabled);
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index bbf802c..64c1211 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -67,7 +67,7 @@
public final class NfcAdapter {
static final String TAG = "NFC";
- private final NfcControllerAlwaysOnStateListener mControllerAlwaysOnStateListener;
+ private final NfcControllerAlwaysOnListener mControllerAlwaysOnListener;
/**
* Intent to start an activity when a tag with NDEF payload is discovered.
@@ -418,19 +418,19 @@
}
/**
- * A callback to be invoked when NFC controller always on state changes.
- * <p>Register your {@code ControllerAlwaysOnStateCallback} implementation with {@link
- * NfcAdapter#registerControllerAlwaysOnStateCallback} and disable it with {@link
- * NfcAdapter#unregisterControllerAlwaysOnStateCallback}.
- * @see #registerControllerAlwaysOnStateCallback
+ * A listener to be invoked when NFC controller always on state changes.
+ * <p>Register your {@code ControllerAlwaysOnListener} implementation with {@link
+ * NfcAdapter#registerControllerAlwaysOnListener} and disable it with {@link
+ * NfcAdapter#unregisterControllerAlwaysOnListener}.
+ * @see #registerControllerAlwaysOnListener
* @hide
*/
@SystemApi
- public interface ControllerAlwaysOnStateCallback {
+ public interface ControllerAlwaysOnListener {
/**
* Called on NFC controller always on state changes
*/
- void onStateChanged(boolean isEnabled);
+ void onControllerAlwaysOnChanged(boolean isEnabled);
}
/**
@@ -748,7 +748,7 @@
mNfcUnlockHandlers = new HashMap<NfcUnlockHandler, INfcUnlockHandler>();
mTagRemovedListener = null;
mLock = new Object();
- mControllerAlwaysOnStateListener = new NfcControllerAlwaysOnStateListener(getService());
+ mControllerAlwaysOnListener = new NfcControllerAlwaysOnListener(getService());
}
/**
@@ -2246,12 +2246,12 @@
* <p>This API is for the NFCC internal state management. It allows to discriminate
* the controller function from the NFC function by keeping the NFC controller on without
* any NFC RF enabled if necessary.
- * <p>This call is asynchronous. Register a callback {@link #ControllerAlwaysOnStateCallback}
- * by {@link #registerControllerAlwaysOnStateCallback} to find out when the operation is
+ * <p>This call is asynchronous. Register a listener {@link #ControllerAlwaysOnListener}
+ * by {@link #registerControllerAlwaysOnListener} to find out when the operation is
* complete.
* <p>If this returns true, then either NFCC always on state has been set based on the value,
- * or a {@link ControllerAlwaysOnStateCallback#onStateChanged(boolean)} will be invoked to
- * indicate the state change.
+ * or a {@link ControllerAlwaysOnListener#onControllerAlwaysOnChanged(boolean)} will be invoked
+ * to indicate the state change.
* If this returns false, then there is some problem that prevents an attempt to turn NFCC
* always on.
* @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is
@@ -2344,37 +2344,37 @@
}
/**
- * Register a {@link ControllerAlwaysOnStateCallback} to listen for NFC controller always on
+ * Register a {@link ControllerAlwaysOnListener} to listen for NFC controller always on
* state changes
- * <p>The provided callback will be invoked by the given {@link Executor}.
+ * <p>The provided listener will be invoked by the given {@link Executor}.
*
- * @param executor an {@link Executor} to execute given callback
- * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+ * @param executor an {@link Executor} to execute given listener
+ * @param listener user implementation of the {@link ControllerAlwaysOnListener}
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
- public void registerControllerAlwaysOnStateCallback(
+ public void registerControllerAlwaysOnListener(
@NonNull @CallbackExecutor Executor executor,
- @NonNull ControllerAlwaysOnStateCallback callback) {
- mControllerAlwaysOnStateListener.register(executor, callback);
+ @NonNull ControllerAlwaysOnListener listener) {
+ mControllerAlwaysOnListener.register(executor, listener);
}
/**
- * Unregister the specified {@link ControllerAlwaysOnStateCallback}
- * <p>The same {@link ControllerAlwaysOnStateCallback} object used when calling
- * {@link #registerControllerAlwaysOnStateCallback(Executor, ControllerAlwaysOnStateCallback)}
+ * Unregister the specified {@link ControllerAlwaysOnListener}
+ * <p>The same {@link ControllerAlwaysOnListener} object used when calling
+ * {@link #registerControllerAlwaysOnListener(Executor, ControllerAlwaysOnListener)}
* must be used.
*
- * <p>Callbacks are automatically unregistered when application process goes away
+ * <p>Listeners are automatically unregistered when application process goes away
*
- * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+ * @param listener user implementation of the {@link ControllerAlwaysOnListener}
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
- public void unregisterControllerAlwaysOnStateCallback(
- @NonNull ControllerAlwaysOnStateCallback callback) {
- mControllerAlwaysOnStateListener.unregister(callback);
+ public void unregisterControllerAlwaysOnListener(
+ @NonNull ControllerAlwaysOnListener listener) {
+ mControllerAlwaysOnListener.unregister(listener);
}
}
diff --git a/core/java/android/nfc/NfcControllerAlwaysOnListener.java b/core/java/android/nfc/NfcControllerAlwaysOnListener.java
new file mode 100644
index 0000000..96707bb
--- /dev/null
+++ b/core/java/android/nfc/NfcControllerAlwaysOnListener.java
@@ -0,0 +1,120 @@
+/*
+ * 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.nfc;
+
+import android.annotation.NonNull;
+import android.nfc.NfcAdapter.ControllerAlwaysOnListener;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class NfcControllerAlwaysOnListener extends INfcControllerAlwaysOnListener.Stub {
+ private static final String TAG = NfcControllerAlwaysOnListener.class.getSimpleName();
+
+ private final INfcAdapter mAdapter;
+
+ private final Map<ControllerAlwaysOnListener, Executor> mListenerMap = new HashMap<>();
+
+ private boolean mCurrentState = false;
+ private boolean mIsRegistered = false;
+
+ public NfcControllerAlwaysOnListener(@NonNull INfcAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ /**
+ * Register a {@link ControllerAlwaysOnListener} with this
+ * {@link NfcControllerAlwaysOnListener}
+ *
+ * @param executor an {@link Executor} to execute given listener
+ * @param listener user implementation of the {@link ControllerAlwaysOnListener}
+ */
+ public void register(@NonNull Executor executor,
+ @NonNull ControllerAlwaysOnListener listener) {
+ synchronized (this) {
+ if (mListenerMap.containsKey(listener)) {
+ return;
+ }
+
+ mListenerMap.put(listener, executor);
+ if (!mIsRegistered) {
+ try {
+ mAdapter.registerControllerAlwaysOnListener(this);
+ mIsRegistered = true;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to register");
+ }
+ }
+ }
+ }
+
+ /**
+ * Unregister the specified {@link ControllerAlwaysOnListener}
+ *
+ * @param listener user implementation of the {@link ControllerAlwaysOnListener}
+ */
+ public void unregister(@NonNull ControllerAlwaysOnListener listener) {
+ synchronized (this) {
+ if (!mListenerMap.containsKey(listener)) {
+ return;
+ }
+
+ mListenerMap.remove(listener);
+
+ if (mListenerMap.isEmpty() && mIsRegistered) {
+ try {
+ mAdapter.unregisterControllerAlwaysOnListener(this);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to unregister");
+ }
+ mIsRegistered = false;
+ }
+ }
+ }
+
+ private void sendCurrentState(@NonNull ControllerAlwaysOnListener listener) {
+ synchronized (this) {
+ Executor executor = mListenerMap.get(listener);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> listener.onControllerAlwaysOnChanged(
+ mCurrentState));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @Override
+ public void onControllerAlwaysOnChanged(boolean isEnabled) {
+ synchronized (this) {
+ mCurrentState = isEnabled;
+ for (ControllerAlwaysOnListener cb : mListenerMap.keySet()) {
+ sendCurrentState(cb);
+ }
+ }
+ }
+}
+
diff --git a/core/java/android/nfc/NfcControllerAlwaysOnStateListener.java b/core/java/android/nfc/NfcControllerAlwaysOnStateListener.java
deleted file mode 100644
index 69a9ec7..0000000
--- a/core/java/android/nfc/NfcControllerAlwaysOnStateListener.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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.nfc;
-
-import android.annotation.NonNull;
-import android.nfc.NfcAdapter.ControllerAlwaysOnStateCallback;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- */
-public class NfcControllerAlwaysOnStateListener extends INfcControllerAlwaysOnStateCallback.Stub {
- private static final String TAG = "NfcControllerAlwaysOnStateListener";
-
- private final INfcAdapter mAdapter;
-
- private final Map<ControllerAlwaysOnStateCallback, Executor> mCallbackMap = new HashMap<>();
-
- private boolean mCurrentState = false;
- private boolean mIsRegistered = false;
-
- public NfcControllerAlwaysOnStateListener(@NonNull INfcAdapter adapter) {
- mAdapter = adapter;
- }
-
- /**
- * Register a {@link ControllerAlwaysOnStateCallback} with this
- * {@link NfcControllerAlwaysOnStateListener}
- *
- * @param executor an {@link Executor} to execute given callback
- * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
- */
- public void register(@NonNull Executor executor,
- @NonNull ControllerAlwaysOnStateCallback callback) {
- synchronized (this) {
- if (mCallbackMap.containsKey(callback)) {
- return;
- }
-
- mCallbackMap.put(callback, executor);
- if (!mIsRegistered) {
- try {
- mAdapter.registerControllerAlwaysOnStateCallback(this);
- mIsRegistered = true;
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to register ControllerAlwaysOnStateListener");
- }
- }
- }
- }
-
- /**
- * Unregister the specified {@link ControllerAlwaysOnStateCallback}
- *
- * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
- */
- public void unregister(@NonNull ControllerAlwaysOnStateCallback callback) {
- synchronized (this) {
- if (!mCallbackMap.containsKey(callback)) {
- return;
- }
-
- mCallbackMap.remove(callback);
-
- if (mCallbackMap.isEmpty() && mIsRegistered) {
- try {
- mAdapter.unregisterControllerAlwaysOnStateCallback(this);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to unregister ControllerAlwaysOnStateListener");
- }
- mIsRegistered = false;
- }
- }
- }
-
- private void sendCurrentState(@NonNull ControllerAlwaysOnStateCallback callback) {
- synchronized (this) {
- Executor executor = mCallbackMap.get(callback);
-
- final long identity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> callback.onStateChanged(
- mCurrentState));
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- @Override
- public void onControllerAlwaysOnStateChanged(boolean isEnabled) {
- synchronized (this) {
- mCurrentState = isEnabled;
- for (ControllerAlwaysOnStateCallback cb : mCallbackMap.keySet()) {
- sendCurrentState(cb);
- }
- }
- }
-}
-
diff --git a/core/java/android/nfc/TEST_MAPPING b/core/java/android/nfc/TEST_MAPPING
new file mode 100644
index 0000000..71ad687
--- /dev/null
+++ b/core/java/android/nfc/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "NfcManagerTests"
+ }
+ ]
+}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c47fc57..043a22b 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -21,6 +21,7 @@
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.job.JobParameters;
@@ -2622,6 +2623,15 @@
*/
public abstract @Nullable long[] getCustomConsumerMeasuredBatteryConsumptionUC();
+ /**
+ * Returns the names of all {@link android.hardware.power.stats.EnergyConsumer}'s
+ * of (custom) energy consumer type
+ * {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
+ *
+ * {@hide}
+ */
+ public abstract @NonNull String[] getCustomEnergyConsumerNames();
+
public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS = new BitDescription[] {
new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"),
new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock", "w"),
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 48f4ca4..8ea59ce 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -24,7 +24,6 @@
import com.android.internal.os.BatteryStatsHistoryIterator;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
@@ -233,8 +232,6 @@
mHistoryBuffer = null;
mHistoryTagPool = null;
}
- System.out.println("From Parcel = " + Arrays.toString(
- mCustomPowerComponentNames));
}
@Override
@@ -293,6 +290,7 @@
* Builder for BatteryUsageStats.
*/
public static final class Builder {
+ @NonNull
private final String[] mCustomPowerComponentNames;
private final int mCustomTimeComponentCount;
private final boolean mIncludePowerModels;
@@ -311,11 +309,11 @@
private Parcel mHistoryBuffer;
private List<BatteryStats.HistoryTag> mHistoryTagPool;
- public Builder(String[] customPowerComponentNames, int customTimeComponentCount) {
+ public Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount) {
this(customPowerComponentNames, customTimeComponentCount, false);
}
- public Builder(String[] customPowerComponentNames, int customTimeComponentCount,
+ public Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount,
boolean includePowerModels) {
mCustomPowerComponentNames = customPowerComponentNames;
mCustomTimeComponentCount = customTimeComponentCount;
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 16d041a..d026e95 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -34,6 +34,9 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
/**
* Java proxy for a native IBinder object.
@@ -262,27 +265,45 @@
Log.e(Binder.TAG, "RemoteException while disabling app freezer");
}
- for (WeakReference<BinderProxy> weakRef : proxiesToQuery) {
- BinderProxy bp = weakRef.get();
- String key;
- if (bp == null) {
- key = "<cleared weak-ref>";
- } else {
- try {
- key = bp.getInterfaceDescriptor();
- if ((key == null || key.isEmpty()) && !bp.isBinderAlive()) {
- key = "<proxy to dead node>";
+ // We run the dump on a separate thread, because there are known cases where
+ // a process overrides getInterfaceDescriptor() and somehow blocks on it, causing
+ // the calling thread (usually AMS) to hit the watchdog.
+ // Do the dumping on a separate thread instead, and give up after a while.
+ ExecutorService executorService = Executors.newSingleThreadExecutor();
+ executorService.submit(() -> {
+ for (WeakReference<BinderProxy> weakRef : proxiesToQuery) {
+ BinderProxy bp = weakRef.get();
+ String key;
+ if (bp == null) {
+ key = "<cleared weak-ref>";
+ } else {
+ try {
+ key = bp.getInterfaceDescriptor();
+ if ((key == null || key.isEmpty()) && !bp.isBinderAlive()) {
+ key = "<proxy to dead node>";
+ }
+ } catch (Throwable t) {
+ key = "<exception during getDescriptor>";
}
- } catch (Throwable t) {
- key = "<exception during getDescriptor>";
+ }
+ Integer i = counts.get(key);
+ if (i == null) {
+ counts.put(key, 1);
+ } else {
+ counts.put(key, i + 1);
}
}
- Integer i = counts.get(key);
- if (i == null) {
- counts.put(key, 1);
- } else {
- counts.put(key, i + 1);
+ });
+
+ try {
+ executorService.shutdown();
+ boolean dumpDone = executorService.awaitTermination(20, TimeUnit.SECONDS);
+ if (!dumpDone) {
+ Log.e(Binder.TAG, "Failed to complete binder proxy dump,"
+ + " dumping what we have so far.");
}
+ } catch (InterruptedException e) {
+ // Ignore
}
try {
ActivityManager.getService().enableAppFreezer(true);
diff --git a/core/java/android/os/CountDownTimer.java b/core/java/android/os/CountDownTimer.java
index c7bf0fd..51faa85 100644
--- a/core/java/android/os/CountDownTimer.java
+++ b/core/java/android/os/CountDownTimer.java
@@ -22,7 +22,22 @@
*
* Example of showing a 30 second countdown in a text field:
*
- * <pre class="prettyprint">
+ * <div>
+ * <div class="ds-selector-tabs"><section><h3 id="kotlin">Kotlin</h3>
+ * <pre class="prettyprint lang-kotlin">
+ * object : CountDownTimer(30000, 1000) {
+ *
+ * override fun onTick(millisUntilFinished: Long) {
+ * mTextField.setText("seconds remaining: " + millisUntilFinished / 1000)
+ * }
+ *
+ * override fun onFinish() {
+ * mTextField.setText("done!")
+ * }
+ * }.start()
+ * </pre>
+ * </section><section><h3 id="java">Java</h3>
+ * <pre class="prettyprint lang-java">
* new CountDownTimer(30000, 1000) {
*
* public void onTick(long millisUntilFinished) {
@@ -32,8 +47,8 @@
* public void onFinish() {
* mTextField.setText("done!");
* }
- * }.start();
- * </pre>
+ * }.start();
+ * </pre></section></div></div>
*
* The calls to {@link #onTick(long)} are synchronized to this object so that
* one call to {@link #onTick(long)} won't ever occur before the previous
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index e3b13f4..b8bb353 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -623,7 +623,7 @@
* </tr>
* <tr>
* <td>summary.total-pss</td>
- * <td>Total PPS memory usage in kB.</td>
+ * <td>Total PSS memory usage in kB.</td>
* <td>{@code 1442}</td>
* <td>23</td>
* </tr>
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 7a624e1..a537c98 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -27,10 +27,39 @@
* and restored from a {@link Parcel}. Classes implementing the Parcelable
* interface must also have a non-null static field called <code>CREATOR</code>
* of a type that implements the {@link Parcelable.Creator} interface.
- *
+ *
* <p>A typical implementation of Parcelable is:</p>
- *
- * <pre>
+ *
+ * <div>
+ * <div class="ds-selector-tabs"><section><h3 id="kotlin">Kotlin</h3>
+ * <pre class="prettyprint lang-kotlin">
+ * class MyParcelable private constructor(`in`: Parcel) : Parcelable {
+ * private val mData: Int = `in`.readInt()
+ *
+ * override fun describeContents(): Int {
+ * return 0
+ * }
+ *
+ * override fun writeToParcel(out: Parcel, flags: Int) {
+ * out.writeInt(mData)
+ * }
+ *
+ * companion object {
+ * val CREATOR: Parcelable.Creator<MyParcelable?>
+ * = object : Parcelable.Creator<MyParcelable?> {
+ * override fun createFromParcel(`in`: Parcel): MyParcelable? {
+ * return MyParcelable(`in`)
+ * }
+ *
+ * override fun newArray(size: Int): Array<MyParcelable?> {
+ * return arrayOfNulls(size)
+ * }
+ * }
+ * }
+ * }
+ * </pre>
+ * </section><section><h3 id="java">Java</h3>
+ * <pre class="prettyprint lang-java">
* public class MyParcelable implements Parcelable {
* private int mData;
*
@@ -52,11 +81,11 @@
* return new MyParcelable[size];
* }
* };
- *
+ *
* private MyParcelable(Parcel in) {
* mData = in.readInt();
* }
- * }</pre>
+ * }</pre></section></div></div>
*/
public interface Parcelable {
/** @hide */
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 77d8664..688e3e9 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -41,6 +41,8 @@
public static final int HASHING_ALGORITHM_SHA256 = 1;
public static final byte LOG2_BLOCK_SIZE_4096_BYTES = 12;
+ public static final int INCFS_MAX_SIGNATURE_SIZE = 8096; // incrementalfs.h
+
/**
* IncFS hashing data.
*/
@@ -191,8 +193,12 @@
private static V4Signature readFrom(InputStream stream) throws IOException {
final int version = readIntLE(stream);
- final byte[] hashingInfo = readBytes(stream);
- final byte[] signingInfo = readBytes(stream);
+ int maxSize = INCFS_MAX_SIGNATURE_SIZE;
+ final byte[] hashingInfo = readBytes(stream, maxSize);
+ if (hashingInfo != null) {
+ maxSize -= hashingInfo.length;
+ }
+ final byte[] signingInfo = readBytes(stream, maxSize);
return new V4Signature(version, hashingInfo, signingInfo);
}
@@ -231,9 +237,13 @@
stream.write(buffer);
}
- private static byte[] readBytes(InputStream stream) throws IOException {
+ private static byte[] readBytes(InputStream stream, int maxSize) throws IOException {
try {
final int size = readIntLE(stream);
+ if (size > maxSize) {
+ throw new IOException(
+ "Signature is too long. Max allowed is " + INCFS_MAX_SIGNATURE_SIZE);
+ }
final byte[] bytes = new byte[size];
readFully(stream, bytes);
return bytes;
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 36177c4..2adcbc3 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -551,4 +552,82 @@
parcel.writeString8(mFsUuid);
parcel.writeString8(mState);
}
+
+ /** @hide */
+ // This class is used by the mainline test suite, so we have to keep these APIs around across
+ // releases. Consider making this class public to help external developers to write tests as
+ // well.
+ @TestApi
+ public static final class Builder {
+ private String mId;
+ private File mPath;
+ private String mDescription;
+ private boolean mPrimary;
+ private boolean mRemovable;
+ private boolean mEmulated;
+ private UserHandle mOwner;
+ private UUID mStorageUuid;
+ private String mUuid;
+ private String mState;
+
+ @SuppressLint("StreamFiles")
+ public Builder(
+ @NonNull String id, @NonNull File path, @NonNull String description,
+ @NonNull UserHandle owner, @NonNull String state) {
+ mId = id;
+ mPath = path;
+ mDescription = description;
+ mOwner = owner;
+ mState = state;
+ }
+
+ @NonNull
+ public Builder setStorageUuid(@Nullable UUID storageUuid) {
+ mStorageUuid = storageUuid;
+ return this;
+ }
+
+ @NonNull
+ public Builder setUuid(@Nullable String uuid) {
+ mUuid = uuid;
+ return this;
+ }
+
+ @NonNull
+ public Builder setPrimary(boolean primary) {
+ mPrimary = primary;
+ return this;
+ }
+
+ @NonNull
+ public Builder setRemovable(boolean removable) {
+ mRemovable = removable;
+ return this;
+ }
+
+ @NonNull
+ public Builder setEmulated(boolean emulated) {
+ mEmulated = emulated;
+ return this;
+ }
+
+ @NonNull
+ public StorageVolume build() {
+ return new StorageVolume(
+ mId,
+ mPath,
+ /* internalPath= */ mPath,
+ mDescription,
+ mPrimary,
+ mRemovable,
+ mEmulated,
+ /* allowMassStorage= */ false,
+ /* maxFileSize= */ 0,
+ mOwner,
+ mStorageUuid,
+ mUuid,
+ mState);
+ }
+ }
+
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 1a40f06..17c90d6 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -886,6 +886,24 @@
}
/**
+ * @param micMuted whether to consider the microphone muted when retrieving audio ops
+ * @return A list of permission groups currently or recently used by all apps by all users in
+ * the current profile group.
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
+ public List<PermGroupUsage> getIndicatorAppOpUsageData(boolean micMuted) {
+ // Lazily initialize the usage helper
+ if (mUsageHelper == null) {
+ mUsageHelper = new PermissionUsageHelper(mContext);
+ }
+ return mUsageHelper.getOpUsageData(micMuted);
+ }
+
+ /**
* Determine if a package should be shown in indicators. Only a select few roles, and the
* system app itself, are hidden. These values are updated at most every 15 seconds.
* @hide
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 9bfd75e..51f19eb 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -22,10 +22,10 @@
import android.annotation.IntDef;
import android.annotation.LongDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.UserHandleAware;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -129,7 +129,7 @@
public static final int ERROR_STORAGE_FULL = 2;
/**
- * Indicates that the {@link InputStream} passed to {@link #storeCallComposerPictureAsUser}
+ * Indicates that the {@link InputStream} passed to {@link #storeCallComposerPicture}
* was closed.
*
* The caller should retry if this error is encountered, and be sure to not close the stream
@@ -195,9 +195,8 @@
* The caller is responsible for closing the {@link InputStream} after the callback indicating
* success or failure.
*
- * @param context An instance of {@link Context}.
- * @param user The user for whom the picture is stored. If {@code null}, the picture will be
- * stored for all users.
+ * @param context An instance of {@link Context}. The picture will be stored to the user
+ * corresponding to {@link Context#getUser()}.
* @param input An input stream from which the picture to store should be read. The input data
* must be decodeable as either a JPEG, PNG, or GIF image.
* @param executor The {@link Executor} on which to perform the file transfer operation and
@@ -207,12 +206,12 @@
* @hide
*/
@SystemApi
+ @UserHandleAware
@RequiresPermission(allOf = {
Manifest.permission.WRITE_CALL_LOG,
Manifest.permission.INTERACT_ACROSS_USERS
})
- public static void storeCallComposerPictureAsUser(@NonNull Context context,
- @Nullable UserHandle user,
+ public static void storeCallComposerPicture(@NonNull Context context,
@NonNull InputStream input,
@CallbackExecutor @NonNull Executor executor,
@NonNull OutcomeReceiver<Uri, CallComposerLoggingException> callback) {
@@ -246,12 +245,13 @@
byte[] picData = tmpOut.toByteArray();
UserManager userManager = context.getSystemService(UserManager.class);
+ UserHandle user = context.getUser();
// Nasty casework for the shadow calllog begins...
// First see if we're just inserting for one user. If so, insert into the shadow
// based on whether that user is unlocked.
UserHandle realUser = UserHandle.CURRENT.equals(user)
? android.os.Process.myUserHandle() : user;
- if (realUser != null) {
+ if (realUser != UserHandle.ALL) {
Uri baseUri = userManager.isUserUnlocked(realUser) ? CALL_COMPOSER_PICTURE_URI
: SHADOW_CALL_COMPOSER_PICTURE_URI;
Uri pictureInsertionUri = ContentProvider.maybeAddUserId(baseUri,
@@ -625,7 +625,7 @@
}
/**
- * @param pictureUri {@link Uri} returned from {@link #storeCallComposerPictureAsUser}.
+ * @param pictureUri {@link Uri} returned from {@link #storeCallComposerPicture}.
* Associates that stored picture with this call in the log.
*/
public @NonNull AddCallParametersBuilder setPictureUri(@NonNull Uri pictureUri) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9450994..2616a667 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9136,6 +9136,20 @@
"biometric_debug_enabled";
/**
+ * Whether or not biometric is allowed on Keyguard.
+ * @hide
+ */
+ @Readable
+ public static final String BIOMETRIC_KEYGUARD_ENABLED = "biometric_keyguard_enabled";
+
+ /**
+ * Whether or not biometric is allowed for apps (through BiometricPrompt).
+ * @hide
+ */
+ @Readable
+ public static final String BIOMETRIC_APP_ENABLED = "biometric_app_enabled";
+
+ /**
* Whether the assist gesture should be enabled.
*
* @hide
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index 63ec252..7e8acde 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
import android.content.pm.DataLoaderParams;
@@ -48,14 +49,19 @@
*
* @see android.content.pm.DataLoaderParams
* @see android.content.pm.PackageInstaller.SessionParams#setDataLoaderParams
+ *
+ * @hide
*/
+@SystemApi
public abstract class DataLoaderService extends Service {
private static final String TAG = "DataLoaderService";
private final DataLoaderBinderService mBinder = new DataLoaderBinderService();
/**
* DataLoader interface. Each instance corresponds to a single installation session.
+ * @hide
*/
+ @SystemApi
public interface DataLoader {
/**
* A virtual constructor.
@@ -112,7 +118,9 @@
/**
* DataLoader factory method.
* An installation session uses it to create an instance of DataLoader.
+ * @hide
*/
+ @SystemApi
public @Nullable DataLoader onCreateDataLoader(@NonNull DataLoaderParams dataLoaderParams) {
return null;
}
@@ -178,7 +186,10 @@
/**
* Provides access to the installation image.
+ *
+ * @hide
*/
+ @SystemApi
public static final class FileSystemConnector {
/**
* Create a wrapper for a native instance.
diff --git a/core/java/android/service/displayhash/DisplayHashParams.java b/core/java/android/service/displayhash/DisplayHashParams.java
index fcce91a..2ec9d5d 100644
--- a/core/java/android/service/displayhash/DisplayHashParams.java
+++ b/core/java/android/service/displayhash/DisplayHashParams.java
@@ -28,7 +28,7 @@
import com.android.internal.util.DataClass;
/**
- * Information passed from the {@link DisplayHasherService} to system server about how to get the
+ * Information passed from the {@link DisplayHashingService} to system server about how to get the
* display data that will be used to generate the {@link android.view.displayhash.DisplayHash}
*
* @hide
@@ -38,9 +38,9 @@
public final class DisplayHashParams implements Parcelable {
/**
* The size to scale the buffer to so the hash algorithm can properly generate the hash. The
- * buffer given to the {@link DisplayHasherService#onGenerateDisplayHash(byte[], HardwareBuffer,
- * Rect, String, DisplayHashResultCallback)} will be stretched based on the value set here.
- * If {@code null}, the buffer size will not be changed.
+ * buffer given to the {@link DisplayHashingService#onGenerateDisplayHash(byte[],
+ * HardwareBuffer, Rect, String, DisplayHashResultCallback)} will be stretched based on the
+ * value set here. If {@code null}, the buffer size will not be changed.
*/
@Nullable
private final Size mBufferSize;
@@ -48,7 +48,7 @@
/**
* Whether the content will be captured in grayscale or color.
*/
- private final boolean mGrayscaleBuffer;
+ private final boolean mUseGrayscale;
/**
* A builder for {@link DisplayHashParams}
@@ -56,7 +56,7 @@
public static final class Builder {
@Nullable
private Size mBufferSize;
- private boolean mGrayscaleBuffer;
+ private boolean mUseGrayscale;
/**
* Creates a new Builder.
@@ -68,8 +68,8 @@
* The size to scale the buffer to so the hash algorithm can properly generate the hash.
*/
@NonNull
- public Builder setBufferSize(int w, int h) {
- mBufferSize = new Size(w, h);
+ public Builder setBufferSize(int width, int height) {
+ mBufferSize = new Size(width, height);
return this;
}
@@ -77,15 +77,15 @@
* Whether the content will be captured in grayscale or color.
*/
@NonNull
- public Builder setGrayscaleBuffer(boolean value) {
- mGrayscaleBuffer = value;
+ public Builder setUseGrayscale(boolean useGrayscale) {
+ mUseGrayscale = useGrayscale;
return this;
}
/** Builds the instance. This builder should not be touched after calling this! */
@NonNull
public DisplayHashParams build() {
- return new DisplayHashParams(mBufferSize, mGrayscaleBuffer);
+ return new DisplayHashParams(mBufferSize, mUseGrayscale);
}
}
@@ -109,28 +109,28 @@
*
* @param bufferSize
* The size to scale the buffer to so the hash algorithm can properly generate the hash. The
- * buffer given to the {@link DisplayHasherService#onGenerateDisplayHash(byte[], HardwareBuffer,
- * Rect, String, DisplayHashResultCallback)} will be stretched based on the value set here.
- * If {@code null}, the buffer size will not be changed.
- * @param grayscaleBuffer
+ * buffer given to the {@link DisplayHashingService#onGenerateDisplayHash(byte[],
+ * HardwareBuffer, Rect, String, DisplayHashResultCallback)} will be stretched based on the
+ * value set here. If {@code null}, the buffer size will not be changed.
+ * @param useGrayscale
* Whether the content will be captured in grayscale or color.
* @hide
*/
@DataClass.Generated.Member
public DisplayHashParams(
@Nullable Size bufferSize,
- boolean grayscaleBuffer) {
+ boolean useGrayscale) {
this.mBufferSize = bufferSize;
- this.mGrayscaleBuffer = grayscaleBuffer;
+ this.mUseGrayscale = useGrayscale;
// onConstructed(); // You can define this method to get a callback
}
/**
* The size to scale the buffer to so the hash algorithm can properly generate the hash. The
- * buffer given to the {@link DisplayHasherService#onGenerateDisplayHash(byte[], HardwareBuffer,
- * Rect, String, DisplayHashResultCallback)} will be stretched based on the value set here.
- * If {@code null}, the buffer size will not be changed.
+ * buffer given to the {@link DisplayHashingService#onGenerateDisplayHash(byte[],
+ * HardwareBuffer, Rect, String, DisplayHashResultCallback)} will be stretched based on the
+ * value set here. If {@code null}, the buffer size will not be changed.
*/
@DataClass.Generated.Member
public @Nullable Size getBufferSize() {
@@ -141,8 +141,8 @@
* Whether the content will be captured in grayscale or color.
*/
@DataClass.Generated.Member
- public boolean isGrayscaleBuffer() {
- return mGrayscaleBuffer;
+ public boolean isUseGrayscale() {
+ return mUseGrayscale;
}
@Override
@@ -153,7 +153,7 @@
return "DisplayHashParams { " +
"bufferSize = " + mBufferSize + ", " +
- "grayscaleBuffer = " + mGrayscaleBuffer +
+ "useGrayscale = " + mUseGrayscale +
" }";
}
@@ -164,7 +164,7 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
- if (mGrayscaleBuffer) flg |= 0x2;
+ if (mUseGrayscale) flg |= 0x2;
if (mBufferSize != null) flg |= 0x1;
dest.writeByte(flg);
if (mBufferSize != null) dest.writeSize(mBufferSize);
@@ -182,11 +182,11 @@
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
- boolean grayscaleBuffer = (flg & 0x2) != 0;
+ boolean useGrayscale = (flg & 0x2) != 0;
Size bufferSize = (flg & 0x1) == 0 ? null : (Size) in.readSize();
this.mBufferSize = bufferSize;
- this.mGrayscaleBuffer = grayscaleBuffer;
+ this.mUseGrayscale = useGrayscale;
// onConstructed(); // You can define this method to get a callback
}
@@ -206,10 +206,10 @@
};
@DataClass.Generated(
- time = 1617735166254L,
+ time = 1618436855096L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/displayhash/DisplayHashParams.java",
- inputSignatures = "private final @android.annotation.Nullable android.util.Size mBufferSize\nprivate final boolean mGrayscaleBuffer\nclass DisplayHashParams extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.util.Size mBufferSize\nprivate boolean mGrayscaleBuffer\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferSize(int,int)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genAidl=true, genToString=true, genParcelable=true, genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.Nullable android.util.Size mBufferSize\nprivate final boolean mUseGrayscale\nclass DisplayHashParams extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.util.Size mBufferSize\nprivate boolean mUseGrayscale\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferSize(int,int)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setUseGrayscale(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genAidl=true, genToString=true, genParcelable=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/displayhash/DisplayHasherService.java b/core/java/android/service/displayhash/DisplayHashingService.java
similarity index 84%
rename from core/java/android/service/displayhash/DisplayHasherService.java
rename to core/java/android/service/displayhash/DisplayHashingService.java
index d300cf1..e8bb485 100644
--- a/core/java/android/service/displayhash/DisplayHasherService.java
+++ b/core/java/android/service/displayhash/DisplayHashingService.java
@@ -45,21 +45,21 @@
* @hide
*/
@SystemApi
-public abstract class DisplayHasherService extends Service {
+public abstract class DisplayHashingService extends Service {
/** @hide **/
public static final String EXTRA_VERIFIED_DISPLAY_HASH =
"android.service.displayhash.extra.VERIFIED_DISPLAY_HASH";
/**
- * Name under which a DisplayHasherService component publishes information
+ * Name under which a DisplayHashingService component publishes information
* about itself. This meta-data must reference an XML resource containing a
- * {@link com.android.internal.R.styleable#DisplayHasherService} tag.
+ * {@link com.android.internal.R.styleable#DisplayHashingService} tag.
*
* @hide
*/
@SystemApi
- public static final String SERVICE_META_DATA = "android.displayhash.display_hasher_service";
+ public static final String SERVICE_META_DATA = "android.displayhash.display_hashing_service";
/**
* The {@link Intent} action that must be declared as handled by a service in its manifest
@@ -69,18 +69,18 @@
*/
@SystemApi
public static final String SERVICE_INTERFACE =
- "android.service.displayhash.DisplayHasherService";
+ "android.service.displayhash.DisplayHashingService";
- private DisplayHasherServiceWrapper mWrapper;
+ private DisplayHashingServiceWrapper mWrapper;
private Handler mHandler;
- public DisplayHasherService() {
+ public DisplayHashingService() {
}
@Override
public void onCreate() {
super.onCreate();
- mWrapper = new DisplayHasherServiceWrapper();
+ mWrapper = new DisplayHashingServiceWrapper();
mHandler = new Handler(Looper.getMainLooper(), null, true);
}
@@ -105,7 +105,6 @@
* if successfully generated a DisplayHash or {@link
* DisplayHashResultCallback#onDisplayHashError(int)} if failed.
*/
- @Nullable
public abstract void onGenerateDisplayHash(@NonNull byte[] salt,
@NonNull HardwareBuffer buffer, @NonNull Rect bounds,
@NonNull String hashAlgorithm, @NonNull DisplayHashResultCallback callback);
@@ -125,8 +124,8 @@
* HardwareBuffer, Rect, String, DisplayHashResultCallback)} to
* generate the token.
* @param displayHash The token to verify that it was generated by the system.
- * @return a {@link VerifiedDisplayHash} if the token was generated by the system or null
- * if the token cannot be verified.
+ * @return a {@link VerifiedDisplayHash} if the provided display hash was originally generated
+ * by the system or null if the system did not generate the display hash.
*/
@Nullable
public abstract VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[] salt,
@@ -150,13 +149,13 @@
callback.sendResult(data);
}
- private final class DisplayHasherServiceWrapper extends IDisplayHasherService.Stub {
+ private final class DisplayHashingServiceWrapper extends IDisplayHashingService.Stub {
@Override
public void generateDisplayHash(byte[] salt, HardwareBuffer buffer, Rect bounds,
String hashAlgorithm, RemoteCallback callback) {
mHandler.sendMessage(
- obtainMessage(DisplayHasherService::onGenerateDisplayHash,
- DisplayHasherService.this, salt, buffer, bounds,
+ obtainMessage(DisplayHashingService::onGenerateDisplayHash,
+ DisplayHashingService.this, salt, buffer, bounds,
hashAlgorithm, new DisplayHashResultCallback() {
@Override
public void onDisplayHashResult(
@@ -179,14 +178,14 @@
public void verifyDisplayHash(byte[] salt, DisplayHash displayHash,
RemoteCallback callback) {
mHandler.sendMessage(
- obtainMessage(DisplayHasherService::verifyDisplayHash,
- DisplayHasherService.this, salt, displayHash, callback));
+ obtainMessage(DisplayHashingService::verifyDisplayHash,
+ DisplayHashingService.this, salt, displayHash, callback));
}
@Override
public void getDisplayHashAlgorithms(RemoteCallback callback) {
- mHandler.sendMessage(obtainMessage(DisplayHasherService::getDisplayHashAlgorithms,
- DisplayHasherService.this, callback));
+ mHandler.sendMessage(obtainMessage(DisplayHashingService::getDisplayHashAlgorithms,
+ DisplayHashingService.this, callback));
}
}
}
diff --git a/core/java/android/service/displayhash/IDisplayHasherService.aidl b/core/java/android/service/displayhash/IDisplayHashingService.aidl
similarity index 98%
rename from core/java/android/service/displayhash/IDisplayHasherService.aidl
rename to core/java/android/service/displayhash/IDisplayHashingService.aidl
index d9dcdca..56e1e0a 100644
--- a/core/java/android/service/displayhash/IDisplayHasherService.aidl
+++ b/core/java/android/service/displayhash/IDisplayHashingService.aidl
@@ -26,7 +26,7 @@
*
* @hide
*/
-oneway interface IDisplayHasherService {
+oneway interface IDisplayHashingService {
/**
* Generates the DisplayHash that can be used to validate that the system generated the token.
*
diff --git a/core/java/android/service/rotationresolver/RotationResolutionRequest.java b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
index 8c7db40..9b9244bd 100644
--- a/core/java/android/service/rotationresolver/RotationResolutionRequest.java
+++ b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
@@ -42,7 +42,7 @@
)
public final class RotationResolutionRequest implements Parcelable {
/** The Name of the package of the current fore-ground activity. */
- @NonNull private final String mPackageName;
+ @NonNull private final String mForegroundPackageName;
/** The current rotation of the screen. */
@Surface.Rotation private final int mCurrentRotation;
@@ -75,7 +75,7 @@
/**
* Creates a new RotationResolutionRequest.
*
- * @param packageName
+ * @param foregroundPackageName
* The Name of the package of the current fore-ground activity.
* @param currentRotation
* The current rotation of the screen.
@@ -88,14 +88,14 @@
*/
@DataClass.Generated.Member
public RotationResolutionRequest(
- @NonNull String packageName,
+ @NonNull String foregroundPackageName,
@Surface.Rotation int currentRotation,
@Surface.Rotation int proposedRotation,
boolean shouldUseCamera,
@DurationMillisLong long timeoutMillis) {
- this.mPackageName = packageName;
+ this.mForegroundPackageName = foregroundPackageName;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mPackageName);
+ NonNull.class, null, mForegroundPackageName);
this.mCurrentRotation = currentRotation;
com.android.internal.util.AnnotationValidations.validate(
Surface.Rotation.class, null, mCurrentRotation);
@@ -114,8 +114,8 @@
* The Name of the package of the current fore-ground activity.
*/
@DataClass.Generated.Member
- public @NonNull String getPackageName() {
- return mPackageName;
+ public @NonNull String getForegroundPackageName() {
+ return mForegroundPackageName;
}
/**
@@ -157,7 +157,7 @@
// String fieldNameToString() { ... }
return "RotationResolutionRequest { " +
- "packageName = " + mPackageName + ", " +
+ "foregroundPackageName = " + mForegroundPackageName + ", " +
"currentRotation = " + mCurrentRotation + ", " +
"proposedRotation = " + mProposedRotation + ", " +
"shouldUseCamera = " + mShouldUseCamera + ", " +
@@ -174,7 +174,7 @@
byte flg = 0;
if (mShouldUseCamera) flg |= 0x8;
dest.writeByte(flg);
- dest.writeString(mPackageName);
+ dest.writeString(mForegroundPackageName);
dest.writeInt(mCurrentRotation);
dest.writeInt(mProposedRotation);
dest.writeLong(mTimeoutMillis);
@@ -193,14 +193,14 @@
byte flg = in.readByte();
boolean shouldUseCamera = (flg & 0x8) != 0;
- String packageName = in.readString();
+ String foregroundPackageName = in.readString();
int currentRotation = in.readInt();
int proposedRotation = in.readInt();
long timeoutMillis = in.readLong();
- this.mPackageName = packageName;
+ this.mForegroundPackageName = foregroundPackageName;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mPackageName);
+ NonNull.class, null, mForegroundPackageName);
this.mCurrentRotation = currentRotation;
com.android.internal.util.AnnotationValidations.validate(
Surface.Rotation.class, null, mCurrentRotation);
@@ -230,10 +230,10 @@
};
@DataClass.Generated(
- time = 1617213094231L,
+ time = 1618447759819L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/rotationresolver/RotationResolutionRequest.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.view.Surface.Rotation int mCurrentRotation\nprivate final @android.view.Surface.Rotation int mProposedRotation\nprivate final boolean mShouldUseCamera\nprivate final @android.annotation.DurationMillisLong long mTimeoutMillis\nclass RotationResolutionRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genToString=true)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mForegroundPackageName\nprivate final @android.view.Surface.Rotation int mCurrentRotation\nprivate final @android.view.Surface.Rotation int mProposedRotation\nprivate final boolean mShouldUseCamera\nprivate final @android.annotation.DurationMillisLong long mTimeoutMillis\nclass RotationResolutionRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index e492584..45f9743 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -421,7 +421,8 @@
final ArrayList<SentenceWordItem> wordItems = new ArrayList<SentenceWordItem>();
wordIterator.setCharSequence(originalText, 0, originalText.length());
int wordEnd = wordIterator.following(start);
- int wordStart = wordIterator.getBeginning(wordEnd);
+ int wordStart = wordEnd == BreakIterator.DONE ? BreakIterator.DONE
+ : wordIterator.getBeginning(wordEnd);
if (DBG) {
Log.d(TAG, "iterator: break: ---- 1st word start = " + wordStart + ", end = "
+ wordEnd + "\n" + originalText);
diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java
index 7edf2e2..b79e692 100644
--- a/core/java/android/service/translation/TranslationService.java
+++ b/core/java/android/service/translation/TranslationService.java
@@ -32,6 +32,7 @@
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -149,18 +150,15 @@
*/
private final ITranslationDirectManager mClientInterface =
new ITranslationDirectManager.Stub() {
- // TODO: Implement cancellation signal
- @NonNull
- private final CancellationSignal mCancellationSignal = new CancellationSignal();
-
@Override
public void onTranslationRequest(TranslationRequest request, int sessionId,
- ITranslationCallback callback)
+ ICancellationSignal transport, ITranslationCallback callback)
throws RemoteException {
final OnTranslationResultCallback translationResultCallback =
new OnTranslationResultCallbackWrapper(callback);
mHandler.sendMessage(obtainMessage(TranslationService::onTranslationRequest,
- TranslationService.this, request, sessionId, mCancellationSignal,
+ TranslationService.this, request, sessionId,
+ CancellationSignal.fromTransport(transport),
translationResultCallback));
}
@@ -235,7 +233,7 @@
* @param cancellationSignal
*/
public abstract void onTranslationRequest(@NonNull TranslationRequest request, int sessionId,
- @NonNull CancellationSignal cancellationSignal,
+ @Nullable CancellationSignal cancellationSignal,
@NonNull OnTranslationResultCallback callback);
/**
@@ -285,6 +283,5 @@
resultReceiver.send(TranslationManager.STATUS_SYNC_CALL_SUCCESS, bundle);
}
});
-
}
}
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index 23b2103..ea854e8 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -24,8 +24,11 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Service;
+import android.content.ContentCaptureOptions;
+import android.content.Context;
import android.content.Intent;
import android.media.AudioFormat;
import android.os.Bundle;
@@ -38,6 +41,8 @@
import android.os.RemoteException;
import android.os.SharedMemory;
import android.util.Log;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.IContentCaptureManager;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@@ -122,6 +127,9 @@
private Handler mHandler;
+ @Nullable
+ private ContentCaptureManager mContentCaptureManager;
+
private final IHotwordDetectionService mInterface = new IHotwordDetectionService.Stub() {
@Override
public void detectFromDspSource(
@@ -187,6 +195,13 @@
Log.i(TAG, "Unsupported audio source " + audioSource);
}
}
+
+ @Override
+ public void updateContentCaptureManager(IContentCaptureManager manager,
+ ContentCaptureOptions options) {
+ mContentCaptureManager = new ContentCaptureManager(
+ HotwordDetectionService.this, manager, options);
+ }
};
@CallSuper
@@ -207,6 +222,16 @@
return null;
}
+ @Override
+ @SuppressLint("OnNameExpected")
+ public @Nullable Object getSystemService(@ServiceName @NonNull String name) {
+ if (Context.CONTENT_CAPTURE_MANAGER_SERVICE.equals(name)) {
+ return mContentCaptureManager;
+ } else {
+ return super.getSystemService(name);
+ }
+ }
+
/**
* 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}.
diff --git a/core/java/android/service/voice/IHotwordDetectionService.aidl b/core/java/android/service/voice/IHotwordDetectionService.aidl
index d242160..2ffe787 100644
--- a/core/java/android/service/voice/IHotwordDetectionService.aidl
+++ b/core/java/android/service/voice/IHotwordDetectionService.aidl
@@ -22,6 +22,8 @@
import android.os.PersistableBundle;
import android.os.SharedMemory;
import android.service.voice.IDspHotwordDetectionCallback;
+import android.view.contentcapture.IContentCaptureManager;
+import android.content.ContentCaptureOptions;
/**
* Provide the interface to communicate with hotword detection service.
@@ -42,6 +44,12 @@
in PersistableBundle options,
in IDspHotwordDetectionCallback callback);
- void updateState(in PersistableBundle options, in SharedMemory sharedMemory,
- in IRemoteCallback callback);
+ void updateState(
+ in PersistableBundle options,
+ in SharedMemory sharedMemory,
+ in IRemoteCallback callback);
+
+ void updateContentCaptureManager(
+ in IContentCaptureManager contentCaptureManager,
+ in ContentCaptureOptions options);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index d89c29a..87fb611 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -37,6 +37,7 @@
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.TypedArray;
+import android.graphics.BLASTBufferQueue;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
@@ -208,8 +209,8 @@
int mCurHeight;
float mZoom = 0f;
int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
- int mWindowPrivateFlags =
- WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
+ int mWindowPrivateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS
+ | WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
int mCurWindowFlags = mWindowFlags;
int mCurWindowPrivateFlags = mWindowPrivateFlags;
Rect mPreviewSurfacePosition;
@@ -253,6 +254,7 @@
private int mDisplayState;
SurfaceControl mSurfaceControl = new SurfaceControl();
+ BLASTBufferQueue mBlastBufferQueue;
final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
{
@@ -974,7 +976,14 @@
View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl,
mInsetsState, mTempControls, mSurfaceSize);
if (mSurfaceControl.isValid()) {
- mSurfaceHolder.mSurface.copyFrom(mSurfaceControl);
+ Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x,
+ mSurfaceSize.y, mFormat);
+ // If blastSurface == null that means it hasn't changed since the last
+ // time we called. In this situation, avoid calling transferFrom as we
+ // would then inc the generation ID and cause EGL resources to be recreated.
+ if (blastSurface != null) {
+ mSurfaceHolder.mSurface.transferFrom(blastSurface);
+ }
}
if (!mLastSurfaceSize.equals(mSurfaceSize)) {
mLastSurfaceSize.set(mSurfaceSize.x, mSurfaceSize.y);
@@ -1455,13 +1464,12 @@
return;
}
Surface surface = mSurfaceHolder.getSurface();
- boolean widthIsLarger =
- mSurfaceControl.getWidth() > mSurfaceControl.getHeight();
- int smaller = widthIsLarger ? mSurfaceControl.getWidth()
- : mSurfaceControl.getHeight();
+ boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y;
+ int smaller = widthIsLarger ? mSurfaceSize.x
+ : mSurfaceSize.y;
float ratio = (float) MIN_BITMAP_SCREENSHOT_WIDTH / (float) smaller;
- int width = (int) (ratio * mSurfaceControl.getWidth());
- int height = (int) (ratio * mSurfaceControl.getHeight());
+ int width = (int) (ratio * mSurfaceSize.x);
+ int height = (int) (ratio * mSurfaceSize.y);
if (width <= 0 || height <= 0) {
Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height);
return;
@@ -1842,6 +1850,21 @@
public void onDisplayAdded(int displayId) {
}
};
+
+ private Surface getOrCreateBLASTSurface(int width, int height, int format) {
+ Surface ret = null;
+ if (mBlastBufferQueue == null) {
+ mBlastBufferQueue = new BLASTBufferQueue("Wallpaper", mSurfaceControl, width,
+ height, format);
+ // We only return the Surface the first time, as otherwise
+ // it hasn't changed and there is no need to update.
+ ret = mBlastBufferQueue.createSurface();
+ } else {
+ mBlastBufferQueue.update(mSurfaceControl, width, height, format);
+ }
+
+ return ret;
+ }
}
private boolean isValid(RectF area) {
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 340fa40..4b18c5a 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -796,13 +796,14 @@
/**
* Notify {@link PhysicalChannelConfig} has changed for a specific subscription.
*
+ * @param slotIndex for which physical channel configs changed.
* @param subId the subId
* @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel.
*/
- public void notifyPhysicalChannelConfigForSubscriber(
- int subId, List<PhysicalChannelConfig> configs) {
+ public void notifyPhysicalChannelConfigForSubscriber(int slotIndex, int subId,
+ List<PhysicalChannelConfig> configs) {
try {
- sRegistry.notifyPhysicalChannelConfigForSubscriber(subId, configs);
+ sRegistry.notifyPhysicalChannelConfigForSubscriber(slotIndex, subId, configs);
} catch (RemoteException ex) {
// system server crash
}
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 33b7c75..2f7fb2f 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -194,6 +194,7 @@
public static final class Font implements Parcelable {
private final @NonNull File mFile;
private final @Nullable File mOriginalFile;
+ private final @NonNull String mPostScriptName;
private final @NonNull FontStyle mStyle;
private final @IntRange(from = 0) int mIndex;
private final @NonNull String mFontVariationSettings;
@@ -204,11 +205,12 @@
*
* @hide Only system server can create this instance and passed via IPC.
*/
- public Font(@NonNull File file, @Nullable File originalFile, @NonNull FontStyle style,
- @IntRange(from = 0) int index, @NonNull String fontVariationSettings,
- @Nullable String fontFamilyName) {
+ public Font(@NonNull File file, @Nullable File originalFile, @NonNull String postScriptName,
+ @NonNull FontStyle style, @IntRange(from = 0) int index,
+ @NonNull String fontVariationSettings, @Nullable String fontFamilyName) {
mFile = file;
mOriginalFile = originalFile;
+ mPostScriptName = postScriptName;
mStyle = style;
mIndex = index;
mFontVariationSettings = fontVariationSettings;
@@ -224,6 +226,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString8(mFile.getAbsolutePath());
dest.writeString8(mOriginalFile == null ? null : mOriginalFile.getAbsolutePath());
+ dest.writeString8(mPostScriptName);
dest.writeInt(mStyle.getWeight());
dest.writeInt(mStyle.getSlant());
dest.writeInt(mIndex);
@@ -238,14 +241,15 @@
File path = new File(source.readString8());
String originalPathStr = source.readString8();
File originalPath = originalPathStr == null ? null : new File(originalPathStr);
+ String postScriptName = source.readString8();
int weight = source.readInt();
int slant = source.readInt();
int index = source.readInt();
String varSettings = source.readString8();
String fallback = source.readString8();
- return new Font(path, originalPath, new FontStyle(weight, slant), index,
- varSettings, fallback);
+ return new Font(path, originalPath, postScriptName, new FontStyle(weight, slant),
+ index, varSettings, fallback);
}
@Override
@@ -314,6 +318,13 @@
}
/**
+ * Returns the PostScript name of this font.
+ */
+ public @NonNull String getPostScriptName() {
+ return mPostScriptName;
+ }
+
+ /**
* Returns the list of axes associated to this font.
* @deprecated Use getFontVariationSettings
* @hide
diff --git a/core/java/android/text/method/TranslationTransformationMethod.java b/core/java/android/text/method/TranslationTransformationMethod.java
index 7db999a8..54c0ffc 100644
--- a/core/java/android/text/method/TranslationTransformationMethod.java
+++ b/core/java/android/text/method/TranslationTransformationMethod.java
@@ -78,24 +78,11 @@
if (TextUtils.isEmpty(translatedText) || isWhitespace(translatedText.toString())) {
return source;
} else {
- // TODO(b/179693024): Remove this once we have the fix to pad the view text instead.
- translatedText = ellipsize(translatedText, ((TextView) view).getText().length());
// TODO(b/174283799): apply the spans to the text
return translatedText;
}
}
- private static CharSequence ellipsize(CharSequence text, int newLength) {
- if (text.length() <= newLength) {
- return text;
- }
- String ellipsis = String.valueOf('\u2026');
- if (newLength == 1) {
- return ellipsis;
- }
- return TextUtils.concat(TextUtils.trimToSize(text, newLength - 1), ellipsis);
- }
-
@Override
public void onFocusChanged(View view, CharSequence sourceText,
boolean focused, int direction,
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 0a3963d..be172f7 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -693,71 +693,79 @@
ThreadedRenderer.setFPSDivisor(divisor);
}
+ private void traceMessage(String msg) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, msg);
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+
void doFrame(long frameTimeNanos, int frame,
DisplayEventReceiver.VsyncEventData vsyncEventData) {
final long startNanos;
final long frameIntervalNanos = vsyncEventData.frameInterval;
- synchronized (mLock) {
- if (!mFrameScheduled) {
- return; // no work to do
- }
-
- if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
- mDebugPrintNextFrameTimeDelta = false;
- Log.d(TAG, "Frame time delta: "
- + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
- }
-
- long intendedFrameTimeNanos = frameTimeNanos;
- startNanos = System.nanoTime();
- final long jitterNanos = startNanos - frameTimeNanos;
- if (jitterNanos >= frameIntervalNanos) {
- final long skippedFrames = jitterNanos / frameIntervalNanos;
- if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
- Log.i(TAG, "Skipped " + skippedFrames + " frames! "
- + "The application may be doing too much work on its main thread.");
- }
- final long lastFrameOffset = jitterNanos % frameIntervalNanos;
- if (DEBUG_JANK) {
- Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
- + "which is more than the frame interval of "
- + (frameIntervalNanos * 0.000001f) + " ms! "
- + "Skipping " + skippedFrames + " frames and setting frame "
- + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
- }
- frameTimeNanos = startNanos - lastFrameOffset;
- }
-
- if (frameTimeNanos < mLastFrameTimeNanos) {
- if (DEBUG_JANK) {
- Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
- + "previously skipped frame. Waiting for next vsync.");
- }
- scheduleVsyncLocked();
- return;
- }
-
- if (mFPSDivisor > 1) {
- long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
- if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
- scheduleVsyncLocked();
- return;
- }
- }
-
- mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
- vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval);
- mFrameScheduled = false;
- mLastFrameTimeNanos = frameTimeNanos;
- mLastFrameIntervalNanos = frameIntervalNanos;
- mLastVsyncEventData = vsyncEventData;
- }
-
try {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
"Choreographer#doFrame " + vsyncEventData.id);
}
+ synchronized (mLock) {
+ if (!mFrameScheduled) {
+ traceMessage("Frame not scheduled");
+ return; // no work to do
+ }
+
+ if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
+ mDebugPrintNextFrameTimeDelta = false;
+ Log.d(TAG, "Frame time delta: "
+ + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
+ }
+
+ long intendedFrameTimeNanos = frameTimeNanos;
+ startNanos = System.nanoTime();
+ final long jitterNanos = startNanos - frameTimeNanos;
+ if (jitterNanos >= frameIntervalNanos) {
+ final long skippedFrames = jitterNanos / frameIntervalNanos;
+ if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
+ Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ + "The application may be doing too much work on its main thread.");
+ }
+ final long lastFrameOffset = jitterNanos % frameIntervalNanos;
+ if (DEBUG_JANK) {
+ Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
+ + "which is more than the frame interval of "
+ + (frameIntervalNanos * 0.000001f) + " ms! "
+ + "Skipping " + skippedFrames + " frames and setting frame "
+ + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
+ }
+ frameTimeNanos = startNanos - lastFrameOffset;
+ }
+
+ if (frameTimeNanos < mLastFrameTimeNanos) {
+ if (DEBUG_JANK) {
+ Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
+ + "previously skipped frame. Waiting for next vsync.");
+ }
+ traceMessage("Frame time goes backward");
+ scheduleVsyncLocked();
+ return;
+ }
+
+ if (mFPSDivisor > 1) {
+ long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
+ if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
+ traceMessage("Frame skipped due to FPSDivisor");
+ scheduleVsyncLocked();
+ return;
+ }
+ }
+
+ mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
+ vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval);
+ mFrameScheduled = false;
+ mLastFrameTimeNanos = frameTimeNanos;
+ mLastFrameIntervalNanos = frameIntervalNanos;
+ mLastVsyncEventData = vsyncEventData;
+ }
+
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
@@ -870,7 +878,12 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void scheduleVsyncLocked() {
- mDisplayEventReceiver.scheduleVsync();
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#scheduleVsyncLocked");
+ mDisplayEventReceiver.scheduleVsync();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
}
private boolean isRunningOnLooperThreadLocked() {
@@ -967,32 +980,40 @@
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
VsyncEventData vsyncEventData) {
- // Post the vsync event to the Handler.
- // The idea is to prevent incoming vsync events from completely starving
- // the message queue. If there are no messages in the queue with timestamps
- // earlier than the frame time, then the vsync event will be processed immediately.
- // Otherwise, messages that predate the vsync event will be handled first.
- long now = System.nanoTime();
- if (timestampNanos > now) {
- Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
- + " ms in the future! Check that graphics HAL is generating vsync "
- + "timestamps using the correct timebase.");
- timestampNanos = now;
- }
+ try {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW,
+ "Choreographer#onVsync " + vsyncEventData.id);
+ }
+ // Post the vsync event to the Handler.
+ // The idea is to prevent incoming vsync events from completely starving
+ // the message queue. If there are no messages in the queue with timestamps
+ // earlier than the frame time, then the vsync event will be processed immediately.
+ // Otherwise, messages that predate the vsync event will be handled first.
+ long now = System.nanoTime();
+ if (timestampNanos > now) {
+ Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
+ + " ms in the future! Check that graphics HAL is generating vsync "
+ + "timestamps using the correct timebase.");
+ timestampNanos = now;
+ }
- if (mHavePendingVsync) {
- Log.w(TAG, "Already have a pending vsync event. There should only be "
- + "one at a time.");
- } else {
- mHavePendingVsync = true;
- }
+ if (mHavePendingVsync) {
+ Log.w(TAG, "Already have a pending vsync event. There should only be "
+ + "one at a time.");
+ } else {
+ mHavePendingVsync = true;
+ }
- mTimestampNanos = timestampNanos;
- mFrame = frame;
- mLastVsyncEventData = vsyncEventData;
- Message msg = Message.obtain(mHandler, this);
- msg.setAsynchronous(true);
- mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
+ mTimestampNanos = timestampNanos;
+ mFrame = frame;
+ mLastVsyncEventData = vsyncEventData;
+ Message msg = Message.obtain(mHandler, this);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
}
@Override
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 0f032e9..88406ff 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -858,4 +858,6 @@
void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener);
void setForceCrossWindowBlurDisabled(boolean disable);
+
+ boolean isTaskSnapshotSupported();
}
diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java
index a6d786e..5fcb011 100644
--- a/core/java/android/view/ScrollCaptureConnection.java
+++ b/core/java/android/view/ScrollCaptureConnection.java
@@ -185,7 +185,8 @@
}
Log.w(TAG, "close(): capture session still active! Ending now.");
// -> UiThread
- mUiThread.execute(() -> mLocal.onScrollCaptureEnd(() -> { /* ignore */ }));
+ final ScrollCaptureCallback callback = mLocal;
+ mUiThread.execute(() -> callback.onScrollCaptureEnd(() -> { /* ignore */ }));
mActive = false;
}
mActive = false;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 21f75d4..83669fa 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -34,7 +34,9 @@
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
+import android.graphics.BLASTBufferQueue;
import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
@@ -94,6 +96,7 @@
private static native void nativeWriteToParcel(long nativeObject, Parcel out);
private static native void nativeRelease(long nativeObject);
private static native void nativeDisconnect(long nativeObject);
+ private static native void nativeUpdateDefaultBufferSize(long nativeObject, int width, int height);
private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
ScreenCaptureListener captureListener);
private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
@@ -188,6 +191,10 @@
IBinder displayToken, int mode);
private static native void nativeReparent(long transactionObj, long nativeObject,
long newParentNativeObject);
+ private static native void nativeSetBuffer(long transactionObj, long nativeObject,
+ GraphicBuffer buffer);
+ private static native void nativeSetColorSpace(long transactionObj, long nativeObject,
+ int colorSpace);
private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
@@ -1078,6 +1085,11 @@
throw new IllegalStateException(
"Only buffer layers can set a valid buffer size.");
}
+ boolean isBqLayer = isBufferQueueLayer();
+ if (isBqLayer) {
+ setBLASTLayer();
+ }
+
return new SurfaceControl(
mSession, mName, mWidth, mHeight, mFormat, mFlags, mParent, mMetadata,
mLocalOwnerView, mCallsite);
@@ -1134,9 +1146,6 @@
return setFlags(FX_SURFACE_NORMAL, FX_SURFACE_MASK);
}
- /**
- * Set the initial size of the controlled surface's buffers in pixels.
- */
private void unsetBufferSize() {
mWidth = 0;
mHeight = 0;
@@ -1301,11 +1310,14 @@
return (mFlags & FX_SURFACE_EFFECT) == FX_SURFACE_EFFECT;
}
+ private boolean isBufferQueueLayer() {
+ return (mFlags & FX_SURFACE_NORMAL) == FX_SURFACE_NORMAL;
+ }
+
/**
* @hide
*/
public Builder setBLASTLayer() {
- unsetBufferSize();
return setFlags(FX_SURFACE_BLAST, FX_SURFACE_MASK);
}
@@ -2590,6 +2602,16 @@
= sRegistry.registerNativeAllocation(this, mNativeObject);
}
+ /**
+ * Create a transaction object that wraps a native peer.
+ * @hide
+ */
+ Transaction(long nativeObject) {
+ mNativeObject = nativeObject;
+ mFreeNativeResources =
+ sRegistry.registerNativeAllocation(this, mNativeObject);
+ }
+
private Transaction(Parcel in) {
readFromParcel(in);
}
@@ -2644,8 +2666,7 @@
final Point size = mResizedSurfaces.valueAt(i);
final SurfaceControl surfaceControl = mResizedSurfaces.keyAt(i);
synchronized (surfaceControl.mLock) {
- surfaceControl.mWidth = size.x;
- surfaceControl.mHeight = size.y;
+ surfaceControl.resize(size.x, size.y);
}
}
mResizedSurfaces.clear();
@@ -3362,6 +3383,31 @@
return this;
}
+ /**
+ * Set a buffer for a SurfaceControl. This can only be used for SurfaceControls that were
+ * created as type {@link #FX_SURFACE_BLAST}
+ *
+ * @hide
+ */
+ public Transaction setBuffer(SurfaceControl sc, GraphicBuffer buffer) {
+ checkPreconditions(sc);
+ nativeSetBuffer(mNativeObject, sc.mNativeObject, buffer);
+ return this;
+ }
+
+ /**
+ * Set the color space for the SurfaceControl. The supported color spaces are SRGB
+ * and Display P3, other color spaces will be treated as SRGB. This can only be used for
+ * SurfaceControls that were created as type {@link #FX_SURFACE_BLAST}
+ *
+ * @hide
+ */
+ public Transaction setColorSpace(SurfaceControl sc, ColorSpace colorSpace) {
+ checkPreconditions(sc);
+ nativeSetColorSpace(mNativeObject, sc.mNativeObject, colorSpace.getId());
+ return this;
+ }
+
/**
* Merge the other transaction into this transaction, clearing the
* other transaction as if it had been applied.
@@ -3536,4 +3582,13 @@
public static Transaction getGlobalTransaction() {
return sGlobalTransaction;
}
+
+ /**
+ * @hide
+ */
+ public void resize(int w, int h) {
+ mWidth = w;
+ mHeight = h;
+ nativeUpdateDefaultBufferSize(mNativeObject, w, h);
+ }
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 2b96a14..7bdf5cf 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -30,6 +30,7 @@
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.HardwareRenderer;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
@@ -221,8 +222,35 @@
private int mPendingReportDraws;
- private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
- private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
+ /**
+ * Transaction that should be used from the render thread. This transaction is only thread safe
+ * with other calls directly from the render thread.
+ */
+ private final SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
+
+ /**
+ * Transaction that should be used whe
+ * {@link HardwareRenderer.FrameDrawingCallback#onFrameDraw} is invoked. All
+ * frame callbacks can use the same transaction since they will be thread safe
+ */
+ private final SurfaceControl.Transaction mFrameCallbackTransaction =
+ new SurfaceControl.Transaction();
+
+ /**
+ * Transaction that should be used for
+ * {@link RenderNode.PositionUpdateListener#positionChanged(long, int, int, int, int)}
+ * The callback is invoked from a thread pool so it's not thread safe with other render thread
+ * transactions. Keep the transactions for position changed callbacks on its own transaction.
+ */
+ private final SurfaceControl.Transaction mPositionChangedTransaction =
+ new SurfaceControl.Transaction();
+
+ /**
+ * A temporary transaction holder that should only be used when applying right away. There
+ * should be no assumption about thread safety for this transaction.
+ */
+ private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
+
private int mParentSurfaceSequenceId;
private RemoteAccessibilityController mRemoteAccessibilityController =
@@ -432,7 +460,6 @@
* This gets called on a RenderThread worker thread, so members accessed here must
* be protected by a lock.
*/
- final boolean useBLAST = useBLASTSync(viewRoot);
viewRoot.registerRtFrameCallback(frame -> {
try {
synchronized (mSurfaceControlLock) {
@@ -456,8 +483,9 @@
Log.d(TAG, System.identityHashCode(this)
+ " updateSurfaceAlpha RT: set alpha=" + alpha);
}
- mRtTransaction.setAlpha(mSurfaceControl, alpha);
- applyRtTransaction(frame);
+
+ mFrameCallbackTransaction.setAlpha(mSurfaceControl, alpha);
+ applyOrMergeTransaction(mFrameCallbackTransaction, frame);
}
// It's possible that mSurfaceControl is released in the UI thread before
// the transaction completes. If that happens, an exception is thrown, which
@@ -806,7 +834,6 @@
* This gets called on a RenderThread worker thread, so members accessed here must
* be protected by a lock.
*/
- final boolean useBLAST = useBLASTSync(viewRoot);
viewRoot.registerRtFrameCallback(frame -> {
try {
synchronized (mSurfaceControlLock) {
@@ -814,8 +841,8 @@
return;
}
- updateRelativeZ(mRtTransaction);
- applyRtTransaction(frame);
+ updateRelativeZ(mFrameCallbackTransaction);
+ applyOrMergeTransaction(mFrameCallbackTransaction, frame);
}
// It's possible that mSurfaceControl is released in the UI thread before
// the transaction completes. If that happens, an exception is thrown, which
@@ -1380,22 +1407,21 @@
return mRTLastReportedPosition;
}
- private void setParentSpaceRectangle(Rect position, long frameNumber) {
+ private void setParentSpaceRectangle(Rect position, long frameNumber, Transaction t) {
final ViewRootImpl viewRoot = getViewRootImpl();
- applySurfaceTransforms(mSurfaceControl, mRtTransaction, position);
- applyChildSurfaceTransaction_renderWorker(mRtTransaction, viewRoot.mSurface, frameNumber);
- applyRtTransaction(frameNumber);
+ applySurfaceTransforms(mSurfaceControl, t, position);
+ applyChildSurfaceTransaction_renderWorker(t, viewRoot.mSurface, frameNumber);
+ applyOrMergeTransaction(t, frameNumber);
}
- private void applyRtTransaction(long frameNumber) {
+ private void applyOrMergeTransaction(Transaction t, long frameNumber) {
final ViewRootImpl viewRoot = getViewRootImpl();
boolean useBLAST = viewRoot != null && useBLASTSync(viewRoot);
if (useBLAST) {
// If we are using BLAST, merge the transaction with the viewroot buffer transaction.
- viewRoot.mergeWithNextTransaction(mRtTransaction, frameNumber);
- return;
+ viewRoot.mergeWithNextTransaction(t, frameNumber);
} else {
- mRtTransaction.apply();
+ t.apply();
}
}
@@ -1436,7 +1462,8 @@
left, top, right, bottom));
}
mRTLastReportedPosition.set(left, top, right, bottom);
- setParentSpaceRectangle(mRTLastReportedPosition, frameNumber);
+ setParentSpaceRectangle(mRTLastReportedPosition, frameNumber,
+ mPositionChangedTransaction);
// Now overwrite mRTLastReportedPosition with our values
} catch (Exception ex) {
Log.e(TAG, "Exception from repositionChild", ex);
@@ -1448,7 +1475,7 @@
float bottom, float vecX, float vecY, float maxStretch) {
mRtTransaction.setStretchEffect(mSurfaceControl, left, top, right, bottom, vecX, vecY,
maxStretch);
- applyRtTransaction(frameNumber);
+ applyOrMergeTransaction(mRtTransaction, frameNumber);
}
@Override
@@ -1468,14 +1495,12 @@
* need to hold the lock here.
*/
synchronized (mSurfaceControlLock) {
- final ViewRootImpl viewRoot = getViewRootImpl();
-
mRtTransaction.hide(mSurfaceControl);
if (mRtReleaseSurfaces) {
mRtReleaseSurfaces = false;
releaseSurfaces(mRtTransaction);
}
- applyRtTransaction(frameNumber);
+ applyOrMergeTransaction(mRtTransaction, frameNumber);
mRtHandlingPositionUpdates = false;
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d6b5a2c..a757295 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -30927,7 +30927,6 @@
* @param executor The executor that the callback should be invoked on.
* @param callback The callback to handle the results of generating the display hash
*/
- @Nullable
public void generateDisplayHash(@NonNull String hashAlgorithm,
@Nullable Rect bounds, @NonNull Executor executor,
@NonNull DisplayHashResultCallback callback) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 76eb882..3cfda57 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1355,6 +1355,16 @@
}
}
+ private void addASurfaceTransactionCallback() {
+ HardwareRenderer.ASurfaceTransactionCallback callback = (nativeTransactionObj,
+ nativeSurfaceControlObj,
+ frameNr) -> {
+ Transaction t = new Transaction(nativeTransactionObj);
+ mergeWithNextTransaction(t, frameNr);
+ };
+ mAttachInfo.mThreadedRenderer.setASurfaceTransactionCallback(callback);
+ }
+
@UnsupportedAppUsage
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
@@ -1391,6 +1401,7 @@
final boolean translucent = attrs.format != PixelFormat.OPAQUE || hasSurfaceInsets;
mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
attrs.getTitle().toString());
+ addASurfaceTransactionCallback();
mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
updateColorModeIfNeeded(attrs.getColorMode());
updateForceDarkMode();
@@ -9309,11 +9320,11 @@
* Handles an inbound request for scroll capture from the system. A search will be
* dispatched through the view tree to locate scrolling content.
* <p>
- * A call to {@link IScrollCaptureCallbacks#onScrollCaptureResponse(ScrollCaptureResponse)}
- * will follow.
+ * A call to
+ * {@link IScrollCaptureResponseListener#onScrollCaptureResponse} will follow.
*
* @param listener to receive responses
- * @see ScrollCaptureTargetSelector
+ * @see ScrollCaptureSearchResults
*/
public void handleScrollCaptureRequest(@NonNull IScrollCaptureResponseListener listener) {
ScrollCaptureSearchResults results =
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index cca8257..60516eb 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -4568,4 +4568,13 @@
default void holdLock(IBinder token, int durationMs) {
throw new UnsupportedOperationException();
}
+
+ /**
+ * Used for testing to check if the system supports TaskSnapshot mechanism.
+ * @hide
+ */
+ @TestApi
+ default boolean isTaskSnapshotSupported() {
+ return false;
+ }
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 2bed311..07eeb03 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -335,4 +335,13 @@
} catch (RemoteException e) {
}
}
+
+ @Override
+ public boolean isTaskSnapshotSupported() {
+ try {
+ return WindowManagerGlobal.getWindowManagerService().isTaskSnapshotSupported();
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
}
diff --git a/core/java/android/view/displayhash/DisplayHash.java b/core/java/android/view/displayhash/DisplayHash.java
index 4148486..4ec0a59 100644
--- a/core/java/android/view/displayhash/DisplayHash.java
+++ b/core/java/android/view/displayhash/DisplayHash.java
@@ -16,6 +16,7 @@
package android.view.displayhash;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.graphics.Rect;
@@ -31,6 +32,7 @@
/**
* The timestamp when the hash was generated.
*/
+ @CurrentTimeMillisLong
private final long mTimeMillis;
/**
@@ -73,7 +75,7 @@
* @hide
*/
@SystemApi
- public DisplayHash(long timeMillis, @NonNull Rect boundsInWindow,
+ public DisplayHash(@CurrentTimeMillisLong long timeMillis, @NonNull Rect boundsInWindow,
@NonNull String hashAlgorithm, @NonNull byte[] imageHash, @NonNull byte[] hmac) {
mTimeMillis = timeMillis;
mBoundsInWindow = boundsInWindow;
@@ -92,6 +94,7 @@
* @hide
*/
@SystemApi
+ @CurrentTimeMillisLong
public long getTimeMillis() {
return mTimeMillis;
}
diff --git a/core/java/android/view/displayhash/DisplayHashManager.java b/core/java/android/view/displayhash/DisplayHashManager.java
index 69dfc38..4f5fef6 100644
--- a/core/java/android/view/displayhash/DisplayHashManager.java
+++ b/core/java/android/view/displayhash/DisplayHashManager.java
@@ -35,7 +35,8 @@
import java.util.Set;
/**
- * Utility class for DisplayHash requests.
+ * Manages DisplayHash requests. The manager object can be retrieved by calling
+ * {@code Context.getSystemService(Context.DISPLAY_HASH_SERVICE)}
*/
@SystemService(DISPLAY_HASH_SERVICE)
public final class DisplayHashManager {
@@ -75,7 +76,7 @@
return sSupportedHashAlgorithms;
} catch (RemoteException e) {
Log.e(TAG, "Failed to send request getSupportedHashingAlgorithms", e);
- return Collections.emptySet();
+ throw e.rethrowFromSystemServer();
}
}
}
@@ -93,7 +94,7 @@
return WindowManagerGlobal.getWindowManagerService().verifyDisplayHash(displayHash);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send request verifyImpressionToken", e);
- return null;
+ throw e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/view/displayhash/VerifiedDisplayHash.java b/core/java/android/view/displayhash/VerifiedDisplayHash.java
index 16a428e..b17c068 100644
--- a/core/java/android/view/displayhash/VerifiedDisplayHash.java
+++ b/core/java/android/view/displayhash/VerifiedDisplayHash.java
@@ -16,6 +16,7 @@
package android.view.displayhash;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
import android.graphics.Rect;
import android.os.Parcelable;
@@ -30,6 +31,7 @@
/**
* The timestamp when the hash was generated.
*/
+ @CurrentTimeMillisLong
private final long mTimeMillis;
/**
@@ -78,7 +80,7 @@
- // Code below generated by codegen v1.0.22.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -106,11 +108,13 @@
*/
@DataClass.Generated.Member
public VerifiedDisplayHash(
- long timeMillis,
+ @CurrentTimeMillisLong long timeMillis,
@NonNull Rect boundsInWindow,
@NonNull String hashAlgorithm,
@NonNull byte[] imageHash) {
this.mTimeMillis = timeMillis;
+ com.android.internal.util.AnnotationValidations.validate(
+ CurrentTimeMillisLong.class, null, mTimeMillis);
this.mBoundsInWindow = boundsInWindow;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mBoundsInWindow);
@@ -128,7 +132,7 @@
* The timestamp when the hash was generated.
*/
@DataClass.Generated.Member
- public long getTimeMillis() {
+ public @CurrentTimeMillisLong long getTimeMillis() {
return mTimeMillis;
}
@@ -200,6 +204,8 @@
byte[] imageHash = in.createByteArray();
this.mTimeMillis = timeMillis;
+ com.android.internal.util.AnnotationValidations.validate(
+ CurrentTimeMillisLong.class, null, mTimeMillis);
this.mBoundsInWindow = boundsInWindow;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mBoundsInWindow);
@@ -228,10 +234,10 @@
};
@DataClass.Generated(
- time = 1613168749684L,
- codegenVersion = "1.0.22",
+ time = 1617747271440L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/displayhash/VerifiedDisplayHash.java",
- inputSignatures = "private final long mTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate java.lang.String imageHashToString()\nprivate java.lang.String byteArrayToString(byte[])\nclass VerifiedDisplayHash extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
+ inputSignatures = "private final @android.annotation.CurrentTimeMillisLong long mTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate java.lang.String imageHashToString()\nprivate java.lang.String byteArrayToString(byte[])\nclass VerifiedDisplayHash extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 616910a..d6292ca 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -581,7 +581,7 @@
*/
public void reportPerceptible(IBinder windowToken, boolean perceptible) {
try {
- mService.reportPerceptible(windowToken, perceptible);
+ mService.reportPerceptibleAsync(windowToken, perceptible);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/translation/ITranslationDirectManager.aidl b/core/java/android/view/translation/ITranslationDirectManager.aidl
index 525a779..6b00c07 100644
--- a/core/java/android/view/translation/ITranslationDirectManager.aidl
+++ b/core/java/android/view/translation/ITranslationDirectManager.aidl
@@ -16,6 +16,7 @@
package android.view.translation;
+import android.os.ICancellationSignal;
import android.view.translation.TranslationRequest;
import android.service.translation.ITranslationCallback;
@@ -27,6 +28,6 @@
*/
oneway interface ITranslationDirectManager {
void onTranslationRequest(in TranslationRequest request, int sessionId,
- in ITranslationCallback callback);
+ in ICancellationSignal transport, in ITranslationCallback callback);
void onFinishTranslationSession(int sessionId);
}
diff --git a/core/java/android/view/translation/TranslationResponse.java b/core/java/android/view/translation/TranslationResponse.java
index f9b7012..eef283d 100644
--- a/core/java/android/view/translation/TranslationResponse.java
+++ b/core/java/android/view/translation/TranslationResponse.java
@@ -87,7 +87,7 @@
/**
* Adds {@link TranslationResponseValue} to be translated. The input
* TranslationResponseValue format should match those provided by the
- * {@link android.view.translation.Translator}'s destSpec.
+ * {@link android.view.translation.Translator}'s targetSpec.
*
* @param value the translated value.
* @return this Builder.
@@ -109,7 +109,7 @@
/**
* Sets the list of {@link ViewTranslationResponse} to be translated. The input
* ViewTranslationResponse contains {@link TranslationResponseValue}s whose format should
- * match those provided by the {@link android.view.translation.Translator}'s destSpec.
+ * match those provided by the {@link android.view.translation.Translator}'s targetSpec.
*
* @param response the translated response.
* @return this Builder.
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index 6b26e06..188d562 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -26,8 +26,10 @@
import android.content.Context;
import android.os.Binder;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.service.translation.ITranslationCallback;
import android.util.Log;
@@ -227,11 +229,13 @@
*
* @param request {@link TranslationRequest} request to be translate.
*
- * @return {@link TranslationRequest} containing translated request,
- * or null if translation could not be done.
- * @throws IllegalStateException if this TextClassification session was destroyed when calls
+ * @throws IllegalStateException if this Translator session was destroyed when called.
+ *
+ * @deprecated use {@link #translate(TranslationRequest, CancellationSignal,
+ * Executor, Consumer)} instead.
+ * @hide
*/
- //TODO: Add cancellation signal
+ @Deprecated
@Nullable
public void translate(@NonNull TranslationRequest request,
@NonNull @CallbackExecutor Executor executor,
@@ -250,7 +254,52 @@
final ITranslationCallback responseCallback =
new TranslationResponseCallbackImpl(callback, executor);
try {
- mDirectServiceBinder.onTranslationRequest(request, mId, responseCallback);
+ mDirectServiceBinder.onTranslationRequest(request, mId,
+ CancellationSignal.createTransport(), responseCallback);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling requestTranslate(): " + e);
+ }
+ }
+
+ /**
+ * Requests a translation for the provided {@link TranslationRequest} using the Translator's
+ * source spec and destination spec.
+ *
+ * @param request {@link TranslationRequest} request to be translate.
+ * @param cancellationSignal signal to cancel the operation in progress.
+ * @param executor Executor to run callback operations
+ * @param callback {@link Consumer} to receive the translation response. Multiple responses may
+ * be received if {@link TranslationRequest#FLAG_PARTIAL_RESPONSES} is set.
+ *
+ * @throws IllegalStateException if this Translator session was destroyed when called.
+ */
+ @Nullable
+ public void translate(@NonNull TranslationRequest request,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<TranslationResponse> callback) {
+ Objects.requireNonNull(request, "Translation request cannot be null");
+ Objects.requireNonNull(executor, "Executor cannot be null");
+ Objects.requireNonNull(callback, "Callback cannot be null");
+
+ if (isDestroyed()) {
+ // TODO(b/176464808): Disallow multiple Translator now, it will throw
+ // IllegalStateException. Need to discuss if we can allow multiple Translators.
+ throw new IllegalStateException(
+ "This translator has been destroyed");
+ }
+
+ ICancellationSignal transport = null;
+ if (cancellationSignal != null) {
+ transport = CancellationSignal.createTransport();
+ cancellationSignal.setRemote(transport);
+ }
+ final ITranslationCallback responseCallback =
+ new TranslationResponseCallbackImpl(callback, executor);
+
+ try {
+ mDirectServiceBinder.onTranslationRequest(request, mId, transport,
+ responseCallback);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException calling requestTranslate(): " + e);
}
@@ -298,7 +347,8 @@
final ITranslationCallback translationCallback =
new TranslationResponseCallbackImpl(callback, executor);
try {
- mDirectServiceBinder.onTranslationRequest(request, mId, translationCallback);
+ mDirectServiceBinder.onTranslationRequest(request, mId,
+ CancellationSignal.createTransport(), translationCallback);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException calling flushRequest");
}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index c09e8bd..b6d3d09 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -100,7 +100,7 @@
* Update the Ui translation state.
*/
public void updateUiTranslationState(@UiTranslationState int state, TranslationSpec sourceSpec,
- TranslationSpec destSpec, List<AutofillId> views) {
+ TranslationSpec targetSpec, List<AutofillId> views) {
if (!mActivity.isResumed() && (state == STATE_UI_TRANSLATION_STARTED
|| state == STATE_UI_TRANSLATION_RESUMED)) {
return;
@@ -113,11 +113,11 @@
switch (state) {
case STATE_UI_TRANSLATION_STARTED:
final Pair<TranslationSpec, TranslationSpec> specs =
- new Pair<>(sourceSpec, destSpec);
+ new Pair<>(sourceSpec, targetSpec);
if (!mTranslators.containsKey(specs)) {
mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
UiTranslationController::createTranslatorAndStart,
- UiTranslationController.this, sourceSpec, destSpec, views));
+ UiTranslationController.this, sourceSpec, targetSpec, views));
} else {
onUiTranslationStarted(mTranslators.get(specs), views);
}
@@ -362,17 +362,20 @@
continue;
}
mActivity.runOnUiThread(() -> {
- if (view.getViewTranslationCallback() == null) {
+ final ViewTranslationCallback callback = view.getViewTranslationCallback();
+ if (callback == null) {
if (DEBUG) {
Log.d(TAG, view + " doesn't support showing translation because of "
+ "null ViewTranslationCallback.");
}
return;
}
+
+ // TODO: Do this for specific views on request only.
+ callback.enableContentPadding();
+
view.onTranslationResponse(response);
- if (view.getViewTranslationCallback() != null) {
- view.getViewTranslationCallback().onShowTranslation(view);
- }
+ callback.onShowTranslation(view);
});
}
}
@@ -383,12 +386,13 @@
* translation when the Translator is created successfully.
*/
@WorkerThread
- private void createTranslatorAndStart(TranslationSpec sourceSpec, TranslationSpec destSpec,
+ private void createTranslatorAndStart(TranslationSpec sourceSpec, TranslationSpec targetSpec,
List<AutofillId> views) {
- final Translator translator = createTranslatorIfNeeded(sourceSpec, destSpec);
+ // Create Translator
+ final Translator translator = createTranslatorIfNeeded(sourceSpec, targetSpec);
if (translator == null) {
- Log.w(TAG, "Can not create Translator for sourceSpec:" + sourceSpec + " destSpec:"
- + destSpec);
+ Log.w(TAG, "Can not create Translator for sourceSpec:" + sourceSpec + " targetSpec:"
+ + targetSpec);
return;
}
onUiTranslationStarted(translator, views);
diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java
index 62868ac..130e078 100644
--- a/core/java/android/view/translation/UiTranslationManager.java
+++ b/core/java/android/view/translation/UiTranslationManager.java
@@ -124,7 +124,7 @@
* ActivityId)} instead.
*
* @param sourceSpec {@link TranslationSpec} for the data to be translated.
- * @param destSpec {@link TranslationSpec} for the translated data.
+ * @param targetSpec {@link TranslationSpec} for the translated data.
* @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated
* @param taskId the Activity Task id which needs ui translation
* @deprecated Use {@code startTranslation(TranslationSpec, TranslationSpec, List<AutofillId>,
@@ -137,17 +137,17 @@
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
@SystemApi
public void startTranslation(@NonNull TranslationSpec sourceSpec,
- @NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
+ @NonNull TranslationSpec targetSpec, @NonNull List<AutofillId> viewIds,
int taskId) {
Objects.requireNonNull(sourceSpec);
- Objects.requireNonNull(destSpec);
+ Objects.requireNonNull(targetSpec);
Objects.requireNonNull(viewIds);
if (viewIds.size() == 0) {
throw new IllegalArgumentException("Invalid empty views: " + viewIds);
}
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_STARTED, sourceSpec,
- destSpec, viewIds, taskId, mContext.getUserId());
+ targetSpec, viewIds, taskId, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -157,11 +157,11 @@
* Request ui translation for a given Views.
*
* @param sourceSpec {@link TranslationSpec} for the data to be translated.
- * @param destSpec {@link TranslationSpec} for the translated data.
+ * @param targetSpec {@link TranslationSpec} for the translated data.
* @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated
* @param activityId the identifier for the Activity which needs ui translation
* @throws IllegalArgumentException if the no {@link View}'s {@link AutofillId} in the list
- * @throws NullPointerException the sourceSpec, destSpec, viewIds, activityId or
+ * @throws NullPointerException the sourceSpec, targetSpec, viewIds, activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
*
* @hide
@@ -169,11 +169,11 @@
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
@SystemApi
public void startTranslation(@NonNull TranslationSpec sourceSpec,
- @NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
+ @NonNull TranslationSpec targetSpec, @NonNull List<AutofillId> viewIds,
@NonNull ActivityId activityId) {
// TODO(b/177789967): Return result code or find a way to notify the status.
Objects.requireNonNull(sourceSpec);
- Objects.requireNonNull(destSpec);
+ Objects.requireNonNull(targetSpec);
Objects.requireNonNull(viewIds);
Objects.requireNonNull(activityId);
Objects.requireNonNull(activityId.getToken());
@@ -182,7 +182,7 @@
}
try {
mService.updateUiTranslationState(STATE_UI_TRANSLATION_STARTED, sourceSpec,
- destSpec, viewIds, activityId.getToken(), activityId.getTaskId(),
+ targetSpec, viewIds, activityId.getToken(), activityId.getTaskId(),
mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -208,7 +208,7 @@
public void finishTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_FINISHED,
- null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, taskId,
+ null /* sourceSpec */, null /* targetSpec */, null /* viewIds */, taskId,
mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -232,7 +232,7 @@
Objects.requireNonNull(activityId);
Objects.requireNonNull(activityId.getToken());
mService.updateUiTranslationState(STATE_UI_TRANSLATION_FINISHED,
- null /* sourceSpec */, null /* destSpec*/, null /* viewIds */,
+ null /* sourceSpec */, null /* targetSpec */, null /* viewIds */,
activityId.getToken(), activityId.getTaskId(), mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -257,7 +257,7 @@
public void pauseTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_PAUSED,
- null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, taskId,
+ null /* sourceSpec */, null /* targetSpec */, null /* viewIds */, taskId,
mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -281,7 +281,7 @@
Objects.requireNonNull(activityId);
Objects.requireNonNull(activityId.getToken());
mService.updateUiTranslationState(STATE_UI_TRANSLATION_PAUSED,
- null /* sourceSpec */, null /* destSpec*/, null /* viewIds */,
+ null /* sourceSpec */, null /* targetSpec */, null /* viewIds */,
activityId.getToken(), activityId.getTaskId(), mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -306,7 +306,7 @@
public void resumeTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_RESUMED,
- null /* sourceSpec */, null /* destSpec*/, null /* viewIds */,
+ null /* sourceSpec */, null /* targetSpec */, null /* viewIds */,
taskId, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -330,7 +330,7 @@
Objects.requireNonNull(activityId);
Objects.requireNonNull(activityId.getToken());
mService.updateUiTranslationState(STATE_UI_TRANSLATION_RESUMED,
- null /* sourceSpec */, null /* destSpec*/, null /* viewIds */,
+ null /* sourceSpec */, null /* targetSpec */, null /* viewIds */,
activityId.getToken(), activityId.getTaskId(), mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/view/translation/ViewTranslationCallback.java b/core/java/android/view/translation/ViewTranslationCallback.java
index c895984..78b4855 100644
--- a/core/java/android/view/translation/ViewTranslationCallback.java
+++ b/core/java/android/view/translation/ViewTranslationCallback.java
@@ -48,4 +48,16 @@
* @return {@code true} if the View handles clearing the translation.
*/
boolean onClearTranslation(@NonNull View view);
+
+ /**
+ * Enables padding on the view's original content.
+ * <p>
+ * This is useful when we do not modify the content directly, rather use a mechanism like
+ * {@link android.text.method.TransformationMethod}. If the app misbehaves when the displayed
+ * translation and the underlying content have different sizes, the platform intelligence can
+ * request that the original content be padded to make the sizes match.
+ *
+ * @hide
+ */
+ default void enableContentPadding() {}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 40c79cf..1953a76 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -770,6 +770,8 @@
private final boolean mUseInternationalizedInput;
// True if fallback fonts that end up getting used should be allowed to affect line spacing.
/* package */ boolean mUseFallbackLineSpacing;
+ // True if the view text can be padded for compat reasons, when the view is translated.
+ private final boolean mUseTextPaddingForUiTranslation;
@ViewDebug.ExportedProperty(category = "text")
@UnsupportedAppUsage
@@ -1480,6 +1482,8 @@
final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
mUseInternationalizedInput = targetSdkVersion >= VERSION_CODES.O;
mUseFallbackLineSpacing = targetSdkVersion >= VERSION_CODES.P;
+ // TODO(b/179693024): Use a ChangeId instead.
+ mUseTextPaddingForUiTranslation = targetSdkVersion <= Build.VERSION_CODES.R;
if (inputMethod != null) {
Class<?> c;
@@ -2372,6 +2376,12 @@
@ViewDebug.CapturedViewProperty
@InspectableProperty
public CharSequence getText() {
+ if (mUseTextPaddingForUiTranslation
+ && mDefaultTranslationCallback != null
+ && mDefaultTranslationCallback.isTextPaddingEnabled()
+ && mDefaultTranslationCallback.isShowingTranslation()) {
+ return mDefaultTranslationCallback.getPaddedText(mText, mTransformed);
+ }
return mText;
}
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
index a479b8a..73f19a7 100644
--- a/core/java/android/widget/TextViewTranslationCallback.java
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -17,6 +17,7 @@
package android.widget;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Build;
import android.text.method.TranslationTransformationMethod;
import android.util.Log;
@@ -33,7 +34,7 @@
*/
public class TextViewTranslationCallback implements ViewTranslationCallback {
- private static final String TAG = "TextViewTranslationCallback";
+ private static final String TAG = "TextViewTranslationCb";
// TODO(b/182433547): remove Build.IS_DEBUGGABLE before ship. Enable the logging in debug build
// to help the debug during the development phase
@@ -41,6 +42,9 @@
|| Build.IS_DEBUGGABLE;
private TranslationTransformationMethod mTranslationTransformation;
+ private boolean mIsShowingTranslation = false;
+ private boolean mIsTextPaddingEnabled = false;
+ private CharSequence mPaddedText;
/**
* Invoked by the platform when receiving the successful {@link ViewTranslationResponse} for the
@@ -74,6 +78,7 @@
*/
@Override
public boolean onShowTranslation(@NonNull View view) {
+ mIsShowingTranslation = true;
if (mTranslationTransformation != null) {
((TextView) view).setTransformationMethod(mTranslationTransformation);
} else {
@@ -90,6 +95,7 @@
*/
@Override
public boolean onHideTranslation(@NonNull View view) {
+ mIsShowingTranslation = false;
// Restore to original text content.
if (mTranslationTransformation != null) {
((TextView) view).setTransformationMethod(
@@ -110,9 +116,9 @@
public boolean onClearTranslation(@NonNull View view) {
// Restore to original text content and clear TranslationTransformation
if (mTranslationTransformation != null) {
- ((TextView) view).setTransformationMethod(
- mTranslationTransformation.getOriginalTransformationMethod());
+ onHideTranslation(view);
clearTranslationTransformation();
+ mPaddedText = null;
} else {
if (DEBUG) {
// TODO(b/182433547): remove before S release
@@ -121,4 +127,59 @@
}
return true;
}
+
+ boolean isShowingTranslation() {
+ return mIsShowingTranslation;
+ }
+
+ @Override
+ public void enableContentPadding() {
+ mIsTextPaddingEnabled = true;
+ }
+
+ /**
+ * Returns whether readers of the view text should receive padded text for compatibility
+ * reasons. The view's original text will be padded to match the length of the translated text.
+ */
+ boolean isTextPaddingEnabled() {
+ return mIsTextPaddingEnabled;
+ }
+
+ /**
+ * Returns the view's original text with padding added. If the translated text isn't longer than
+ * the original text, returns the original text itself.
+ *
+ * @param text the view's original text
+ * @param translatedText the view's translated text
+ * @see #isTextPaddingEnabled()
+ */
+ @Nullable
+ CharSequence getPaddedText(CharSequence text, CharSequence translatedText) {
+ if (text == null) {
+ return null;
+ }
+ if (mPaddedText == null) {
+ mPaddedText = computePaddedText(text, translatedText);
+ }
+ return mPaddedText;
+ }
+
+ @NonNull
+ private CharSequence computePaddedText(CharSequence text, CharSequence translatedText) {
+ if (translatedText == null) {
+ return text;
+ }
+ int newLength = translatedText.length();
+ if (newLength <= text.length()) {
+ return text;
+ }
+ StringBuilder sb = new StringBuilder(newLength);
+ sb.append(text);
+ for (int i = text.length(); i < newLength; i++) {
+ sb.append(COMPAT_PAD_CHARACTER);
+ }
+ return sb;
+ }
+
+ private static final char COMPAT_PAD_CHARACTER = '\u2002';
}
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 08bb1a9..385d6cff 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -142,6 +142,12 @@
*/
public boolean isKeyguardOccluded = false;
+ /**
+ * TaskSnapshot.
+ * @hide
+ */
+ public TaskSnapshot mTaskSnapshot;
+
public StartingWindowInfo() {
}
@@ -164,6 +170,7 @@
dest.writeTypedObject(mainWindowLayoutParams, flags);
dest.writeInt(splashScreenThemeResId);
dest.writeBoolean(isKeyguardOccluded);
+ dest.writeTypedObject(mTaskSnapshot, flags);
}
void readFromParcel(@NonNull Parcel source) {
@@ -175,6 +182,7 @@
mainWindowLayoutParams = source.readTypedObject(WindowManager.LayoutParams.CREATOR);
splashScreenThemeResId = source.readInt();
isKeyguardOccluded = source.readBoolean();
+ mTaskSnapshot = source.readTypedObject(TaskSnapshot.CREATOR);
}
@Override
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 88584f4..d84f571 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -105,6 +105,7 @@
* a {@link com.android.server.wm.DisplayArea} by
* {@link #attachToDisplayArea(int, int, Bundle)}.
*
+ * @see WindowProviderService#attachToWindowToken(IBinder))
* @see IWindowManager#attachWindowContextToWindowToken(IBinder, IBinder)
*/
public void attachToWindowToken(IBinder windowToken) {
diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java
new file mode 100644
index 0000000..b8619fb
--- /dev/null
+++ b/core/java/android/window/WindowProviderService.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
+import android.annotation.UiContext;
+import android.app.ActivityThread;
+import android.app.LoadedApk;
+import android.app.Service;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.Display;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
+import android.view.WindowManagerImpl;
+
+// TODO(b/159767464): handle #onConfigurationChanged(Configuration)
+/**
+ * A {@link Service} responsible for showing a non-activity window, such as software keyboards or
+ * accessibility overlay windows. This {@link Service} has similar behavior to
+ * {@link WindowContext}, but is represented as {@link Service}.
+ *
+ * @see android.inputmethodservice.InputMethodService
+ * @see android.accessibilityservice.AccessibilityService
+ *
+ * @hide
+ */
+@TestApi
+@UiContext
+public abstract class WindowProviderService extends Service {
+
+ private final WindowTokenClient mWindowToken = new WindowTokenClient();
+ private final WindowContextController mController = new WindowContextController(mWindowToken);
+ private WindowManager mWindowManager;
+
+ /**
+ * Returns the type of this {@link WindowProviderService}.
+ * Each inheriting class must implement this method to provide the type of the window. It is
+ * used similar to {@code type} of {@link Context#createWindowContext(int, Bundle)}
+ *
+ * @see Context#createWindowContext(int, Bundle)
+ *
+ * @hide
+ */
+ @TestApi
+ @SuppressLint("OnNameExpected")
+ // Suppress the lint because it is not a callback and users should provide window type
+ // so we cannot make it final.
+ public abstract @WindowType int getWindowType();
+
+ /**
+ * Returns the option of this {@link WindowProviderService}.
+ * Default is {@code null}. The inheriting class can implement this method to provide the
+ * customization {@code option} of the window. It is used similar to {@code options} of
+ * {@link Context#createWindowContext(int, Bundle)}
+ *
+ * @see Context#createWindowContext(int, Bundle)
+ *
+ * @hide
+ */
+ @TestApi
+ @SuppressLint({"OnNameExpected", "NullableCollection"})
+ // Suppress the lint because it is not a callback and users may override this API to provide
+ // launch option. Also, the return value of this API is null by default.
+ @Nullable
+ public Bundle getWindowContextOptions() {
+ return null;
+ }
+
+ /**
+ * Attaches this WindowProviderService to the {@code windowToken}.
+ *
+ * @hide
+ */
+ @TestApi
+ public final void attachToWindowToken(@NonNull IBinder windowToken) {
+ mController.attachToWindowToken(windowToken);
+ }
+
+ /** @hide */
+ @Override
+ public final Context createServiceBaseContext(ActivityThread mainThread,
+ LoadedApk packageInfo) {
+ final Context context = super.createServiceBaseContext(mainThread, packageInfo);
+ // Always associate with the default display at initialization.
+ final Display defaultDisplay = context.getSystemService(DisplayManager.class)
+ .getDisplay(DEFAULT_DISPLAY);
+ return context.createTokenContext(mWindowToken, defaultDisplay);
+ }
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWindowToken.attachContext(this);
+ mController.attachToDisplayArea(getWindowType(), getDisplayId(), getWindowContextOptions());
+ mWindowManager = WindowManagerImpl.createWindowContextWindowManager(this);
+ }
+
+ @SuppressLint("OnNameExpected")
+ @Override
+ // Suppress the lint because ths is overridden from Context.
+ public @Nullable Object getSystemService(@NonNull String name) {
+ if (WINDOW_SERVICE.equals(name)) {
+ return mWindowManager;
+ }
+ return super.getSystemService(name);
+ }
+
+ @CallSuper
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mController.detachIfNeeded();
+ }
+}
diff --git a/core/java/com/android/internal/app/IAppOpsCallback.aidl b/core/java/com/android/internal/app/IAppOpsCallback.aidl
index 15221b1..024ff66 100644
--- a/core/java/com/android/internal/app/IAppOpsCallback.aidl
+++ b/core/java/com/android/internal/app/IAppOpsCallback.aidl
@@ -17,7 +17,7 @@
package com.android.internal.app;
// This interface is also used by native code, so must
-// be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsCallback.h
+// be kept in sync with frameworks/native/libs/permission/include/binder/IAppOpsCallback.h
oneway interface IAppOpsCallback {
void opChanged(int op, int uid, String packageName);
}
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 8a6856e..281702e 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -33,7 +33,7 @@
interface IAppOpsService {
// These methods are also called by native code, so must
- // be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h
+ // be kept in sync with frameworks/native/libs/permission/include/binder/IAppOpsService.h
// and not be reordered
int checkOperation(int code, int uid, String packageName);
SyncNotedAppOp noteOperation(int code, int uid, String packageName, @nullable String attributionTag,
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index f63ac2e..8b24f64 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -88,7 +88,7 @@
private final int mBindingFlags;
protected I mService;
- private boolean mBinding;
+ private boolean mConnecting;
private boolean mDestroyed;
private boolean mServiceDied;
private boolean mCompleted;
@@ -228,7 +228,7 @@
if (mService != null) {
mService.asBinder().unlinkToDeath(this, 0);
}
- mBinding = false;
+ mConnecting = true;
mService = null;
mServiceDied = true;
cancelScheduledUnbind();
@@ -435,10 +435,10 @@
}
private void handleEnsureBound() {
- if (handleIsBound() || mBinding) return;
+ if (handleIsBound() || mConnecting) return;
if (mVerbose) Slog.v(mTag, "ensureBound()");
- mBinding = true;
+ mConnecting = true;
final int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
| Context.BIND_INCLUDE_CAPABILITIES | mBindingFlags;
@@ -448,7 +448,7 @@
if (!willBind) {
Slog.w(mTag, "could not bind to " + mIntent + " using flags " + flags);
- mBinding = false;
+ mConnecting = false;
if (!mServiceDied) {
handleBinderDied();
@@ -457,10 +457,10 @@
}
private void handleEnsureUnbound() {
- if (!handleIsBound() && !mBinding) return;
+ if (!handleIsBound() && !mConnecting) return;
if (mVerbose) Slog.v(mTag, "ensureUnbound()");
- mBinding = false;
+ mConnecting = false;
if (handleIsBound()) {
handleOnConnectedStateChangedInternal(false);
if (mService != null) {
@@ -476,12 +476,12 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (mVerbose) Slog.v(mTag, "onServiceConnected()");
- if (mDestroyed || !mBinding) {
+ if (mDestroyed || !mConnecting) {
// This is abnormal. Unbinding the connection has been requested already.
Slog.wtf(mTag, "onServiceConnected() was dispatched after unbindService.");
return;
}
- mBinding = false;
+ mConnecting = false;
try {
service.linkToDeath(AbstractRemoteService.this, 0);
} catch (RemoteException re) {
@@ -496,7 +496,7 @@
@Override
public void onServiceDisconnected(ComponentName name) {
if (mVerbose) Slog.v(mTag, "onServiceDisconnected()");
- mBinding = true;
+ mConnecting = true;
mService = null;
}
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index 20e520e..4365966 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -30,7 +30,7 @@
*/
oneway interface IInputMethodPrivilegedOperations {
void setImeWindowStatusAsync(int vis, int backDisposition);
- void reportStartInput(in IBinder startInputToken, in IVoidResultCallback resultCallback);
+ void reportStartInputAsync(in IBinder startInputToken);
void createInputContentUriToken(in Uri contentUri, in String packageName,
in IIInputContentUriTokenResultCallback resultCallback);
void reportFullscreenMode(boolean fullscreen, in IVoidResultCallback resultCallback);
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 1000914..555488d 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -123,21 +123,18 @@
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#reportStartInput(IBinder,
- * IVoidResultCallback)}.
+ * Calls {@link IInputMethodPrivilegedOperations#reportStartInputAsync(IBinder)}.
*
* @param startInputToken {@link IBinder} token to distinguish startInput session
*/
@AnyThread
- public void reportStartInput(IBinder startInputToken) {
+ public void reportStartInputAsync(IBinder startInputToken) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
}
try {
- final Completable.Void value = Completable.createVoid();
- ops.reportStartInput(startInputToken, ResultCallbacks.of(value));
- Completable.getResult(value);
+ ops.reportStartInputAsync(startInputToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index e83f365..b05a9f8 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -6951,9 +6951,10 @@
/**
* Returns the names of custom power components.
*/
- public @Nullable String[] getCustomPowerComponentNames() {
+ @Override
+ public @NonNull String[] getCustomEnergyConsumerNames() {
if (mGlobalMeasuredEnergyStats == null) {
- return null;
+ return new String[0];
}
final String[] names = mGlobalMeasuredEnergyStats.getCustomBucketNames();
for (int i = 0; i < names.length; i++) {
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index c3986c2..9ad7c15 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -21,6 +21,7 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.SystemClock;
import android.os.UidBatteryConsumer;
import android.util.SparseArray;
@@ -36,12 +37,12 @@
*/
public class BatteryUsageStatsProvider {
private final Context mContext;
- private final BatteryStatsImpl mStats;
+ private final BatteryStats mStats;
private final PowerProfile mPowerProfile;
private final Object mLock = new Object();
private List<PowerCalculator> mPowerCalculators;
- public BatteryUsageStatsProvider(Context context, BatteryStatsImpl stats) {
+ public BatteryUsageStatsProvider(Context context, BatteryStats stats) {
mContext = context;
mStats = stats;
mPowerProfile = new PowerProfile(mContext);
@@ -97,7 +98,7 @@
allowableStatsAge = Math.min(allowableStatsAge, query.getMaxStatsAge());
}
- return mStats.mClocks.elapsedRealtime() - lastUpdateTimeStampMs > allowableStatsAge;
+ return elapsedRealtime() - lastUpdateTimeStampMs > allowableStatsAge;
}
/**
@@ -120,10 +121,10 @@
*/
@VisibleForTesting
public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
- final long realtimeUs = mStats.mClocks.elapsedRealtime() * 1000;
- final long uptimeUs = mStats.mClocks.uptimeMillis() * 1000;
+ final long realtimeUs = elapsedRealtime() * 1000;
+ final long uptimeUs = uptimeMillis() * 1000;
- final String[] customPowerComponentNames = mStats.getCustomPowerComponentNames();
+ final String[] customPowerComponentNames = mStats.getCustomEnergyConsumerNames();
// TODO(b/174186358): read extra time component number from configuration
final int customTimeComponentCount = 0;
@@ -154,16 +155,22 @@
if ((query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY) != 0) {
+ if (!(mStats instanceof BatteryStatsImpl)) {
+ throw new UnsupportedOperationException(
+ "History cannot be included for " + getClass().getName());
+ }
+
+ BatteryStatsImpl batteryStatsImpl = (BatteryStatsImpl) mStats;
ArrayList<BatteryStats.HistoryTag> tags = new ArrayList<>(
- mStats.mHistoryTagPool.size());
+ batteryStatsImpl.mHistoryTagPool.size());
for (Map.Entry<BatteryStats.HistoryTag, Integer> entry :
- mStats.mHistoryTagPool.entrySet()) {
+ batteryStatsImpl.mHistoryTagPool.entrySet()) {
final BatteryStats.HistoryTag tag = entry.getKey();
tag.poolIdx = entry.getValue();
tags.add(tag);
}
- batteryUsageStatsBuilder.setBatteryHistory(mStats.mHistoryBuffer, tags);
+ batteryUsageStatsBuilder.setBatteryHistory(batteryStatsImpl.mHistoryBuffer, tags);
}
return batteryUsageStatsBuilder.build();
@@ -199,4 +206,20 @@
return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND, realtimeUs,
BatteryStats.STATS_SINCE_CHARGED) / 1000;
}
+
+ private long elapsedRealtime() {
+ if (mStats instanceof BatteryStatsImpl) {
+ return ((BatteryStatsImpl) mStats).mClocks.elapsedRealtime();
+ } else {
+ return SystemClock.elapsedRealtime();
+ }
+ }
+
+ private long uptimeMillis() {
+ if (mStats instanceof BatteryStatsImpl) {
+ return ((BatteryStatsImpl) mStats).mClocks.uptimeMillis();
+ } else {
+ return SystemClock.uptimeMillis();
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java
index 4ca59be..0079801 100644
--- a/core/java/com/android/internal/os/BinderLatencyObserver.java
+++ b/core/java/com/android/internal/os/BinderLatencyObserver.java
@@ -157,7 +157,10 @@
FrameworkStatsLog.BINDER_LATENCY_REPORTED,
atom.getBytes(),
mPeriodicSamplingInterval,
- 1);
+ 1,
+ mBucketCount,
+ mFirstBucketSize,
+ mBucketScaleFactor);
}
private void noteLatencyDelayed() {
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 1673362..993e4e7 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -149,8 +149,11 @@
return null;
}
- if (parsedArgs.mUsapPoolStatusSpecified) {
- // Handle this once we've released the argBuffer, to avoid opening a second one.
+ if (parsedArgs.mUsapPoolStatusSpecified
+ || parsedArgs.mApiDenylistExemptions != null
+ || parsedArgs.mHiddenApiAccessLogSampleRate != -1
+ || parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) {
+ // Handle these once we've released argBuffer, to avoid opening a second one.
break;
}
@@ -183,18 +186,6 @@
return null;
}
- if (parsedArgs.mApiDenylistExemptions != null) {
- return handleApiDenylistExemptions(zygoteServer,
- parsedArgs.mApiDenylistExemptions);
- }
-
- if (parsedArgs.mHiddenApiAccessLogSampleRate != -1
- || parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) {
- return handleHiddenApiAccessLogSampleRate(zygoteServer,
- parsedArgs.mHiddenApiAccessLogSampleRate,
- parsedArgs.mHiddenApiAccessStatslogSampleRate);
- }
-
if (parsedArgs.mPermittedCapabilities != 0
|| parsedArgs.mEffectiveCapabilities != 0) {
throw new ZygoteSecurityException("Client may not specify capabilities: "
@@ -311,10 +302,20 @@
}
}
}
+ // Handle anything that may need a ZygoteCommandBuffer after we've released ours.
if (parsedArgs.mUsapPoolStatusSpecified) {
- // Now that we've released argBuffer:
return handleUsapPoolStatusChange(zygoteServer, parsedArgs.mUsapPoolEnabled);
}
+ if (parsedArgs.mApiDenylistExemptions != null) {
+ return handleApiDenylistExemptions(zygoteServer,
+ parsedArgs.mApiDenylistExemptions);
+ }
+ if (parsedArgs.mHiddenApiAccessLogSampleRate != -1
+ || parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) {
+ return handleHiddenApiAccessLogSampleRate(zygoteServer,
+ parsedArgs.mHiddenApiAccessLogSampleRate,
+ parsedArgs.mHiddenApiAccessStatslogSampleRate);
+ }
throw new AssertionError("Shouldn't get here");
}
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index e438d39..a0a0f32 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -92,7 +92,7 @@
void notifyRegistrationFailed(int slotIndex, int subId, in CellIdentity cellIdentity,
String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
void notifyBarringInfoChanged(int slotIndex, int subId, in BarringInfo barringInfo);
- void notifyPhysicalChannelConfigForSubscriber(in int subId,
+ void notifyPhysicalChannelConfigForSubscriber(in int phoneId, in int subId,
in List<PhysicalChannelConfig> configs);
void notifyDataEnabled(in int phoneId, int subId, boolean enabled, int reason);
void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in int reason, in long allowedNetworkType);
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 90c7282..61a5014 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -95,6 +95,11 @@
*/
public static final int ACTION_START_RECENTS_ANIMATION = 8;
+ /**
+ * Time it takes the sensor to detect rotation.
+ */
+ public static final int ACTION_ROTATE_SCREEN_SENSOR = 9;
+
private static final int[] ACTIONS_ALL = {
ACTION_EXPAND_PANEL,
ACTION_TOGGLE_RECENTS,
@@ -104,7 +109,8 @@
ACTION_TURN_ON_SCREEN,
ACTION_ROTATE_SCREEN,
ACTION_FACE_WAKE_AND_UNLOCK,
- ACTION_START_RECENTS_ANIMATION
+ ACTION_START_RECENTS_ANIMATION,
+ ACTION_ROTATE_SCREEN_SENSOR
};
/** @hide */
@@ -117,7 +123,8 @@
ACTION_TURN_ON_SCREEN,
ACTION_ROTATE_SCREEN,
ACTION_FACE_WAKE_AND_UNLOCK,
- ACTION_START_RECENTS_ANIMATION
+ ACTION_START_RECENTS_ANIMATION,
+ ACTION_ROTATE_SCREEN_SENSOR
})
@Retention(RetentionPolicy.SOURCE)
public @interface Action {
@@ -133,6 +140,7 @@
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN,
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK,
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION,
+ FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR
};
private static LatencyTracker sLatencyTracker;
@@ -209,6 +217,8 @@
return "ACTION_FACE_WAKE_AND_UNLOCK";
case 9:
return "ACTION_START_RECENTS_ANIMATION";
+ case 11:
+ return "ACTION_ROTATE_SCREEN_SENSOR";
default:
throw new IllegalArgumentException("Invalid action");
}
diff --git a/core/java/com/android/internal/util/State.java b/core/java/com/android/internal/util/State.java
index 4613dad..d5c0f60 100644
--- a/core/java/com/android/internal/util/State.java
+++ b/core/java/com/android/internal/util/State.java
@@ -16,6 +16,7 @@
package com.android.internal.util;
+import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Message;
@@ -25,6 +26,7 @@
*
* The class for implementing states in a StateMachine
*/
+@SuppressLint("AndroidFrameworkRequiresPermission")
public class State implements IState {
/**
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index fd13c26b..93cd4e9 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -85,7 +85,7 @@
oneway void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
in float[] matrixValues, in IVoidResultCallback resultCallback);
- oneway void reportPerceptible(in IBinder windowToken, boolean perceptible);
+ oneway void reportPerceptibleAsync(in IBinder windowToken, boolean perceptible);
/** Remove the IME surface. Requires INTERNAL_SYSTEM_WINDOW permission. */
oneway void removeImeSurface(in IVoidResultCallback resultCallback);
/** Remove the IME surface. Requires passing the currently focused window. */
diff --git a/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
index 934241a..75bf1ce 100644
--- a/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
+++ b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
@@ -105,6 +105,14 @@
int prevAnchorTop = anchor.getTop();
// Note: requestChildRectangleOnScreen may modify rectangle, must pass pass in a copy here
Rect input = new Rect(requestedContentBounds);
+ // Expand input rect to get the requested rect to be in the center
+ int remainingHeight = recyclerView.getHeight() - recyclerView.getPaddingTop()
+ - recyclerView.getPaddingBottom() - input.height();
+ if (remainingHeight > 0) {
+ input.inset(0, -remainingHeight / 2);
+ }
+ Log.d(TAG, "input (post center adjustment) = " + input);
+
if (recyclerView.requestChildRectangleOnScreen(anchor, input, true)) {
int scrolled = prevAnchorTop - anchor.getTop(); // inverse of movement
Log.d(TAG, "RecyclerView scrolled by " + scrolled + " px");
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
index a41511b..8aa2d57 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
@@ -17,6 +17,7 @@
package com.android.internal.view;
import android.annotation.UiThread;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.HardwareRenderer;
@@ -26,6 +27,7 @@
import android.graphics.RectF;
import android.graphics.RenderNode;
import android.os.CancellationSignal;
+import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display.ColorMode;
@@ -53,11 +55,14 @@
private static final String TAG = "ScrollCaptureViewSupport";
- private static final boolean WAIT_FOR_ANIMATION = true;
+ private static final String SETTING_CAPTURE_DELAY = "screenshot.scroll_capture_delay";
+ private static final long SETTING_CAPTURE_DELAY_DEFAULT = 60L; // millis
private final WeakReference<V> mWeakView;
private final ScrollCaptureViewHelper<V> mViewHelper;
private final ViewRenderer mRenderer;
+ private final long mPostScrollDelayMillis;
+
private boolean mStarted;
private boolean mEnded;
@@ -66,6 +71,10 @@
mRenderer = new ViewRenderer();
// TODO(b/177649144): provide access to color space from android.media.Image
mViewHelper = viewHelper;
+ Context context = containingView.getContext();
+ ContentResolver contentResolver = context.getContentResolver();
+ mPostScrollDelayMillis = Settings.Global.getLong(contentResolver,
+ SETTING_CAPTURE_DELAY, SETTING_CAPTURE_DELAY_DEFAULT);
}
/** Based on ViewRootImpl#updateColorModeIfNeeded */
@@ -120,37 +129,41 @@
public final void onScrollCaptureImageRequest(ScrollCaptureSession session,
CancellationSignal signal, Rect requestRect, Consumer<Rect> onComplete) {
if (signal.isCanceled()) {
+ Log.w(TAG, "onScrollCaptureImageRequest: cancelled!");
return;
}
+
V view = mWeakView.get();
if (view == null || !view.isVisibleToUser()) {
// Signal to the controller that we have a problem and can't continue.
onComplete.accept(new Rect());
return;
}
+
// Ask the view to scroll as needed to bring this area into view.
ScrollResult scrollResult = mViewHelper.onScrollRequested(view, session.getScrollBounds(),
requestRect);
+
if (scrollResult.availableArea.isEmpty()) {
onComplete.accept(scrollResult.availableArea);
return;
}
- view.invalidate(); // don't wait for vsync
// For image capture, shift back by scrollDelta to arrive at the location within the view
// where the requested content will be drawn
Rect viewCaptureArea = new Rect(scrollResult.availableArea);
viewCaptureArea.offset(0, -scrollResult.scrollDelta);
- if (WAIT_FOR_ANIMATION) {
- view.postOnAnimation(() -> {
+ Runnable captureAction = () -> {
+ if (signal.isCanceled()) {
+ Log.w(TAG, "onScrollCaptureImageRequest: cancelled! skipping render.");
+ } else {
mRenderer.renderView(view, viewCaptureArea);
onComplete.accept(new Rect(scrollResult.availableArea));
- });
- } else {
- mRenderer.renderView(view, viewCaptureArea);
- onComplete.accept(new Rect(scrollResult.availableArea));
- }
+ }
+ };
+
+ view.postOnAnimationDelayed(captureAction, mPostScrollDelayMillis);
}
@Override
diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java
index 7a8ead6..de3b6a4 100644
--- a/core/java/com/android/internal/widget/NotificationExpandButton.java
+++ b/core/java/com/android/internal/widget/NotificationExpandButton.java
@@ -165,15 +165,23 @@
private void updateColors() {
if (shouldShowNumber() && !mDisallowColor) {
- mPillView.setBackgroundTintList(ColorStateList.valueOf(mHighlightPillColor));
+ if (mHighlightPillColor != 0) {
+ mPillView.setBackgroundTintList(ColorStateList.valueOf(mHighlightPillColor));
+ }
mPillView.setBackgroundTintMode(PorterDuff.Mode.SRC_IN);
mIconView.setColorFilter(mHighlightTextColor, PorterDuff.Mode.SRC_IN);
- mNumberView.setTextColor(mHighlightTextColor);
+ if (mHighlightTextColor != 0) {
+ mNumberView.setTextColor(mHighlightTextColor);
+ }
} else {
- mPillView.setBackgroundTintList(ColorStateList.valueOf(mDefaultPillColor));
+ if (mDefaultPillColor != 0) {
+ mPillView.setBackgroundTintList(ColorStateList.valueOf(mDefaultPillColor));
+ }
mPillView.setBackgroundTintMode(PorterDuff.Mode.SRC_IN);
mIconView.setColorFilter(mDefaultTextColor, PorterDuff.Mode.SRC_IN);
- mNumberView.setTextColor(mDefaultTextColor);
+ if (mDefaultTextColor != 0) {
+ mNumberView.setTextColor(mDefaultTextColor);
+ }
}
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ffba628..27f82f1 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -30,6 +30,7 @@
#include <android/hardware/display/IDeviceProductInfoConstants.h>
#include <android/os/IInputConstants.h>
#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_graphics_GraphicBuffer.h>
#include <android_runtime/android_hardware_HardwareBuffer.h>
#include <android_runtime/android_view_Surface.h>
#include <android_runtime/android_view_SurfaceSession.h>
@@ -253,6 +254,15 @@
}
}
+constexpr ui::Dataspace fromNamedColorSpaceValueToDataspace(const jint colorSpace) {
+ switch (colorSpace) {
+ case JNamedColorSpace::DISPLAY_P3:
+ return ui::Dataspace::DISPLAY_P3;
+ default:
+ return ui::Dataspace::V0_SRGB;
+ }
+}
+
constexpr ui::Dataspace pickDataspaceFromColorMode(const ui::ColorMode colorMode) {
switch (colorMode) {
case ui::ColorMode::DISPLAY_P3:
@@ -384,6 +394,14 @@
}
}
+static void nativeSetDefaultBufferSize(JNIEnv* env, jclass clazz, jlong nativeObject,
+ jint width, jint height) {
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+ if (ctrl != NULL) {
+ ctrl->updateDefaultBufferSize(width, height);
+ }
+}
+
static Rect rectFromObj(JNIEnv* env, jobject rectObj) {
int left = env->GetIntField(rectObj, gRectClassInfo.left);
int top = env->GetIntField(rectObj, gRectClassInfo.top);
@@ -553,6 +571,23 @@
transaction->setGeometry(ctrl, source, dst, orientation);
}
+static void nativeSetBuffer(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+ jobject bufferObject) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ sp<GraphicBuffer> buffer(
+ android_graphics_GraphicBuffer_getNativeGraphicsBuffer(env, bufferObject));
+ transaction->setBuffer(ctrl, buffer);
+}
+
+static void nativeSetColorSpace(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+ jint colorSpace) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ ui::Dataspace dataspace = fromNamedColorSpaceValueToDataspace(colorSpace);
+ transaction->setDataspace(ctrl, dataspace);
+}
+
static void nativeSetBlurRegions(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jobjectArray regions, jint regionsLength) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -1744,6 +1779,8 @@
(void*)nativeRelease },
{"nativeDisconnect", "(J)V",
(void*)nativeDisconnect },
+ {"nativeUpdateDefaultBufferSize", "(JII)V",
+ (void*)nativeSetDefaultBufferSize},
{"nativeCreateTransaction", "()J",
(void*)nativeCreateTransaction },
{"nativeApplyTransaction", "(JZ)V",
@@ -1877,6 +1914,10 @@
(void*)nativeGetDisplayedContentSample },
{"nativeSetGeometry", "(JJLandroid/graphics/Rect;Landroid/graphics/Rect;J)V",
(void*)nativeSetGeometry },
+ {"nativeSetBuffer", "(JJLandroid/graphics/GraphicBuffer;)V",
+ (void*)nativeSetBuffer },
+ {"nativeSetColorSpace", "(JJI)V",
+ (void*)nativeSetColorSpace },
{"nativeSyncInputWindows", "(J)V",
(void*)nativeSyncInputWindows },
{"nativeGetDisplayBrightnessSupport", "(Landroid/os/IBinder;)Z",
diff --git a/core/proto/android/internal/OWNERS b/core/proto/android/internal/OWNERS
new file mode 100644
index 0000000..24e24c2
--- /dev/null
+++ b/core/proto/android/internal/OWNERS
@@ -0,0 +1,2 @@
+# Binder
+per-file binder_latency.proto = file:/core/java/com/android/internal/os/BINDER_OWNERS
\ No newline at end of file
diff --git a/core/proto/android/internal/binder_latency.proto b/core/proto/android/internal/binder_latency.proto
index e32c3e3..8b11f5b 100644
--- a/core/proto/android/internal/binder_latency.proto
+++ b/core/proto/android/internal/binder_latency.proto
@@ -34,6 +34,7 @@
UNKNOWN_PROCESS_SOURCE = 0;
SYSTEM_SERVER = 1;
TELEPHONY = 2;
+ BLUETOOTH = 3;
}
enum ServiceClassName {
@@ -74,4 +75,19 @@
// Stores the count of samples for each bucket. The number of buckets and
// their sizes are controlled server side with a flag.
repeated int32 buckets = 3;
-}
\ No newline at end of file
+
+ // Params for histogram buckets.
+ // The number of buckets in the histogram. Store this value separately
+ // as the tail of empty buckets is truncated when stored in the proto to
+ // conserve space. Thus it is not possible to infer this value from there.
+ optional int32 bucket_count = 4;
+
+ // The size (upper bound) of the first bucket (used to avoid creating an
+ // excessive amount of small buckets). E.g. for first_bucket_size of 5, the
+ // first bucket will be [0, 5) and the second will be [5, 5 * scaleFactor).
+ optional int32 first_bucket_size = 5;
+
+ // The rate in which each consecutive bucket increases (before rounding).
+ // Implemented in: com.android.internal.os.BinderLatencyBuckets.
+ optional float scale_factor = 6;
+}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index a7127ad..b157146 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -308,6 +308,7 @@
optional float minimize_amount = 27;
optional bool created_by_organizer = 28;
optional string affinity = 29;
+ optional bool has_child_pip_activity = 30;
}
/* represents ActivityRecordProto */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 925a212..9d65e71 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4099,6 +4099,15 @@
<permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to use the package installer v2 APIs.
+ <p>The package installer v2 APIs are still a work in progress and we're
+ currently validating they work in all scenarios.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="com.android.permission.USE_INSTALLER_V2"
+ android:protectionLevel="signature|installer" />
+
<!-- Allows an application to use System Data Loaders.
<p>Not for use by third-party applications.
@hide
@@ -5673,12 +5682,12 @@
<permission android:name="android.permission.CONTROL_DEVICE_STATE"
android:protectionLevel="signature" />
- <!-- Must be required by a
- {@link android.service.displayhash.DisplayHasherService}
+ <!-- @hide @SystemApi Must be required by a
+ {@link android.service.displayhash.DisplayHashingService}
to ensure that only the system can bind to it.
- @hide This is not a third-party API (intended for OEMs and system apps).
+ This is not a third-party API (intended for OEMs and system apps).
-->
- <permission android:name="android.permission.BIND_DISPLAY_HASHER_SERVICE"
+ <permission android:name="android.permission.BIND_DISPLAY_HASHING_SERVICE"
android:protectionLevel="signature" />
<!-- @hide @TestApi Allows an application to enable/disable toast rate limiting.
diff --git a/core/res/res/drawable/btn_borderless_material.xml b/core/res/res/drawable/btn_borderless_material.xml
index 1a0912e..08e1060 100644
--- a/core/res/res/drawable/btn_borderless_material.xml
+++ b/core/res/res/drawable/btn_borderless_material.xml
@@ -15,8 +15,7 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?attr/colorControlHighlight"
- android:rippleStyle="?attr/rippleStyle">
+ android:color="?attr/colorControlHighlight">
<item android:id="@id/mask"
android:drawable="@drawable/btn_default_mtrl_shape" />
</ripple>
diff --git a/core/res/res/drawable/btn_colored_material.xml b/core/res/res/drawable/btn_colored_material.xml
index 5274ef2..7ba21e8 100644
--- a/core/res/res/drawable/btn_colored_material.xml
+++ b/core/res/res/drawable/btn_colored_material.xml
@@ -19,8 +19,7 @@
android:insetTop="@dimen/button_inset_vertical_material"
android:insetRight="@dimen/button_inset_horizontal_material"
android:insetBottom="@dimen/button_inset_vertical_material">
- <ripple android:color="?attr/colorControlHighlight"
- android:rippleStyle="?attr/rippleStyle">
+ <ripple android:color="?attr/colorControlHighlight">
<item>
<shape android:shape="rectangle"
android:tint="@color/btn_colored_background_material">
diff --git a/core/res/res/drawable/btn_default_material.xml b/core/res/res/drawable/btn_default_material.xml
index 6a9e621..ed2b5aa 100644
--- a/core/res/res/drawable/btn_default_material.xml
+++ b/core/res/res/drawable/btn_default_material.xml
@@ -15,7 +15,6 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="?attr/colorControlHighlight"
- android:rippleStyle="?attr/rippleStyle">
+ android:color="?attr/colorControlHighlight">
<item android:drawable="@drawable/btn_default_mtrl_shape" />
</ripple>
diff --git a/core/res/res/drawable/btn_toggle_material.xml b/core/res/res/drawable/btn_toggle_material.xml
index 7cee382..8b19e4a 100644
--- a/core/res/res/drawable/btn_toggle_material.xml
+++ b/core/res/res/drawable/btn_toggle_material.xml
@@ -21,8 +21,7 @@
android:insetBottom="@dimen/button_inset_vertical_material">
<layer-list android:paddingMode="stack">
<item>
- <ripple android:color="?attr/colorControlHighlight"
- android:rippleStyle="?attr/rippleStyle">
+ <ripple android:color="?attr/colorControlHighlight">
<item>
<shape android:shape="rectangle"
android:tint="?attr/colorButtonNormal">
diff --git a/core/res/res/layout/notification_expand_button.xml b/core/res/res/layout/notification_expand_button.xml
index b969fa4..e752431 100644
--- a/core/res/res/layout/notification_expand_button.xml
+++ b/core/res/res/layout/notification_expand_button.xml
@@ -22,7 +22,6 @@
android:layout_gravity="top|end"
android:contentDescription="@string/expand_button_content_description_collapsed"
android:padding="16dp"
- android:visibility="gone"
>
<LinearLayout
@@ -40,6 +39,7 @@
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
android:gravity="center_vertical"
android:paddingStart="8dp"
+ android:visibility="gone"
/>
<ImageView
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 8b225b3..445bac5 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1852,10 +1852,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Opgedateer deur jou administrateur"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Uitgevee deur jou administrateur"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Batterybespaarder skakel Donkertema aan en beperk of skakel agtergrondaktiwiteit, sommige visuele effekte en ander kenmerke, soos \"Ok Google\", af\n\n"<annotation id="url">"Kom meer te wete"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Batterybespaarder skakel Donkertema aan en beperk of skakel agtergrondaktiwiteit, sommige visuele effekte en ander kenmerke, soos \"Ok Google\", af."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Databespaarder verhoed sommige programme om data in die agtergrond te stuur of te aanvaar om datagebruik te help verminder. \'n Program wat jy tans gebruik kan by data ingaan, maar sal dit dalk minder gereeld doen. Dit kan byvoorbeeld beteken dat prente nie wys totdat jy op hulle tik nie."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Skakel Databespaarder aan?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Skakel aan"</string>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Maak toe"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Antwoord"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Wys af"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Lui af"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Inkomende oproep"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Hierdie kennisgewing is gedegradeer na Stil. Tik om terugvoer te gee."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Hierdie kennisgewing is hoër gegradeer. Tik om terugvoer te gee."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Hierdie kennisgewing is laer gegradeer. Tik om terugvoer te gee."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Probeer verbeterde kennisgewings"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Skakel verbeterde kennisgewings aan sodat jy aanhou om voorgestelde handelinge, antwoorde en meer te ontvang. Android se aanpasbare kennisgewings word nie meer gesteun nie."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Skakel aan"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Nie nou nie"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Kom meer te wete"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Verbeterde kennisgewings kan alle kennisgewinginhoud lees, insluitend persoonlike inligting soos kontakname en boodskappe. Hierdie kenmerk kan ook kennisgewings toemaak of handelingknoppies in kennisgewings gebruik, soos om foonoproepe te beantwoord.\n\nHierdie kenmerk kan ook Prioriteitmodus aan- of afskakel en soortgelyke instellings verander."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Roetinemodus-inligtingkennisgewing"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Battery kan afloop voordat dit normaalweg gelaai word"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Batterybespaarder is geaktiveer om batterylewe te verleng"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 834eb74..52f0540 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"የባትሪ ኃይል ቆጣቢ የጠቆር ያለ ገጽታን ያበራል እና የጀርባ እንቅስቃሴን፣ አንዳንድ የእይታ ውጤቶችን እና እንደ «Hey Google» ያሉ ባህሪያትን ይገድባል ወይም ያጠፋል።\n\n"<annotation id="url">"የበለጠ ለመረዳት"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"የባትሪ ኃይል ቆጣቢ የጠቆር ያለ ገጽታን ያበራል እና የጀርባ እንቅስቃሴን፣ አንዳንድ የእይታ ውጤቶችን እና እንደ «Hey Google» ያሉ ባህሪያትን ይገድባል ወይም ያጠፋል።"</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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"ዝጋ"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>፦ <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"መልስ"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"አትቀበል"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"ስልኩን ዝጋ"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"ገቢ ጥሪ"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ይህ ማሳወቂያ ወደ ዝምታ ዝቅ ብሏል። ግብረመልስ ለመስጠት መታ ያድርጉ።"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"ይህ ማሳወቂያ ከፍተኛ ደረጃ ተሰጥቶታል። ግብረመልስ ለመስጠት መታ ያድርጉ።"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"ይህ ማሳወቂያ ዝቅተኛ ደረጃ ተሰጥቶታል። ግብረመልስ ለመስጠት መታ ያድርጉ።"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"የተሻሻሉ ማሳወቂያዎችን ይሞክሩ"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"የተጠቆሙ እርምጃዎችን፣ ምላሾችን እና ሌሎችን ማግኘትን ለመቀጠል የተሻሻሉ ማሳወቂያዎችን ያብሩ። የAndroid አስማሚ ማሳወቂያዎች ከአሁን በኋላ አይደገፉም።"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"አብራ"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"አሁን አይደለም"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"የበለጠ ለመረዳት"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"የተሻሻሉ ማሳወቂያዎች እንደ የእውቂያ ስሞች እና መልዕክቶች ያሉ የግል መረጃዎችን ጨምሮ ሁሉንም የማሳወቂያ ይዘቶችን ማንበብ ይችላሉ። ይህ ባህሪ ማሳወቂያዎችን ማሰናከል ወይም እንደ የስልክ ጥሪዎችን ማንሳት በመሳሰሉ ማሳወቂያዎች ውስጥ ባሉ አዝራሮች ላይ እርምጃዎችንም አዝራሮች ላይ እርምጃዎችንም መውሰድ ይችላል።\n\nይህ ባህሪ የቅድሚያ ሁነታን ማብራት ወይም ማጥፋት እና ተዛማጅ ቅንብሮችን መለወጥ ይችላል።"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"የዕለት ተዕለት ሁነታ መረጃ ማሳወቂያዎች"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ባትሪ ከተለመደው ኃይል መሙላት በፊት ሊያልቅ ይችላል"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"የባትሪ ቆጣቢ የባትሪ ዕድሜን ለማራዘም ገብሯል።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 4623057..a78c0e3 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -550,10 +550,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"يسمح هذا الإذن للتطبيق باكتشاف الأجهزة القريبة التي تتضمّن بلوتوث والاقتران بها."</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"تسمح بالربط الأجهزة المقترنة التي تتضمّن بلوتوث."</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"يسمح هذا الإذن للتطبيق بالارتباط بالأجهزة المقترنة التي تتضمّن بلوتوث."</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"عرض الإعلانات على الأجهزة القريبة التي تتضمن بلوتوث"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"للسماح للتطبيق بعرض إعلانات على الأجهزة القريبة التي تتضمّن بلوتوث."</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"تحديد الموضع النسبي بين الأجهزة المجاورة التي تستخدم النطاق الواسع جدًا"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"يسمح هذا الإذن للتطبيق بتحديد الموضع النسبي بين الأجهزة المجاورة التي تستخدم النطاق الواسع جدًا."</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"معلومات الخدمات المدفوعة باستخدام الاتصال قصير المدى NFC المفضّل"</string>
@@ -1946,10 +1944,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"يؤدي استخدام خيار \"توفير شحن البطارية\" إلى تفعيل \"المظهر الداكن\" وتقييد أو إيقاف النشاط في الخلفية وبعض التأثيرات المرئية والميزات الأخرى، مثلاً \"Ok Google\".\n\n"<annotation id="url">"مزيد من المعلومات"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"يؤدي استخدام خيار \"توفير شحن البطارية\" إلى تفعيل \"المظهر الداكن\" وتقييد أو إيقاف النشاط في الخلفية وبعض التأثيرات المرئية والميزات الأخرى، مثلاً \"Ok Google\"."</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>
@@ -2057,6 +2053,8 @@
<string name="close_button_text" msgid="10603510034455258">"إغلاق"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"ردّ"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"رفض"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"قطع الاتصال"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"مكالمة واردة"</string>
@@ -2208,18 +2206,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"تم خفض ترتيب هذا الإشعار إلى الوضع \"صامت\". انقر لإرسال ملاحظات وآراء."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"تمت زيادة ترتيب هذا الإشعار. انقر لإرسال ملاحظات وآراء."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"تم خفض ترتيب هذا الإشعار. انقر لإرسال ملاحظات وآراء."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"تجربة الإشعارات المحسّنة"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"لمواصلة الحصول على الردود والإجراءات المقترحة والمزيد، عليك تفعيل الإشعارات المحسّنة. لم تعد الإشعارات التكيُّفية لنظام التشغيل Android متاحة."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"تفعيل"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"لاحقًا"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"مزيد من المعلومات"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"يمكن للإشعارات المحسّنة قراءة كل محتوى الإشعارات، بما في ذلك المعلومات الشخصية، مثلاً أسماء جهات الاتصال والرسائل. يمكن لهذه الميزة أيضًا إغلاق الإشعارات أو اتخاذ إجراءات من خلال الأزرار في الإشعارات، مثلاً الردّ على مكالمات الهاتف.\n\nويمكن لهذه الميزة أيضًا تفعيل وضع \"الأولوية\" أو إيقافه وتغيير الإعدادات ذات الصلة."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"إشعار معلومات \"وضع سلسلة الإجراءات\""</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"قد تنفد طاقة البطارية قبل الشحن المعتاد"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"تم تفعيل \"توفير شحن البطارية\" لإطالة عمرها."</string>
@@ -2256,7 +2248,7 @@
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> و<xliff:g id="COUNT_3">%d</xliff:g> ملف</item>
<item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> وملف (<xliff:g id="COUNT_1">%d</xliff:g>)</item>
</plurals>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ليس هناك أشخاص مقترحون للمشاركة معهم"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ليس هناك أشخاص مقترحون للمشاركة معهم."</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"قائمة التطبيقات"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"لم يتم منح هذا التطبيق إذن تسجيل، ولكن يمكنه تسجيل الصوت من خلال جهاز USB هذا."</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"الشاشة الرئيسية"</string>
@@ -2411,14 +2403,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"يمكنك الآن تكبير جزء من الشاشة."</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"التفعيل من خلال \"الإعدادات\""</string>
<string name="dismiss_action" msgid="1728820550388704784">"إغلاق"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"إزالة حظر ميكروفون الجهاز"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"إزالة حظر كاميرا الجهاز"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"للتطبيق <b><xliff:g id="APP">%s</xliff:g></b> وكل التطبيقات والخدمات"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"إزالة الحظر"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"الخصوصية في جهاز الاستشعار"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"رمز التطبيق"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"الصورة الذهنية للعلامة التجارية للتطبيق"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index a815db7..4d6b0b7 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -319,10 +319,8 @@
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"শাৰীৰিক কাৰ্যকলাপ এক্সেছ কৰা"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"কেমেৰা"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"ফট\' তুলিব আৰু ভিডিঅ\' ৰেকৰ্ড কৰিব পাৰে"</string>
- <!-- no translation found for permgrouplab_nearby_devices (5529147543651181991) -->
- <skip />
- <!-- no translation found for permgroupdesc_nearby_devices (3213561597116913508) -->
- <skip />
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"নিকটৱৰ্তী ডিভাইচ"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"নিকটৱৰ্তী ডিভাইচসমূহ বিচাৰি পাওক আৰু সেইবোৰৰ সৈতে সংযোগ কৰক"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"কল লগসমূহ"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"ফ\'নৰ কল লগ পঢ়ক আৰু লিখক"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"ফ’ন"</string>
@@ -540,14 +538,10 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"এপ্টোক নিকটৱৰ্তী ব্লুটুথ ডিভাইচ বিচাৰি উলিয়াবলৈ আৰু সেইসমূহৰ সৈতে পেয়াৰ কৰিবলৈ অনুমতি দিয়ে"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"পেয়াৰ কৰা ব্লুটুথ ডিভাইচৰ সৈতে সংযোগ কৰক"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"এপ্টোক পেয়াৰ কৰা ব্লুটুথ ডিভাইচৰ সৈতে সংযোগ কৰিবলৈ অনুমতি দিয়ে"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
- <!-- no translation found for permlab_uwb_ranging (8141915781475770665) -->
- <skip />
- <!-- no translation found for permdesc_uwb_ranging (2519723069604307055) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"নিকটৱৰ্তী ব্লুটুথ ডিভাইচত বিজ্ঞাপন প্ৰচাৰ কৰা"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"এপ্টোক নিকটৱৰ্তী ব্লুটুথ ডিভাইচত বিজ্ঞাপন প্ৰচাৰ কৰিবলৈ দিয়ে"</string>
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"নিকটৱৰ্তী আল্ট্ৰা-ৱাইডবেণ্ড ডিভাইচৰ মাজৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰক"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"এপ্টোক নিকটৱৰ্তী আল্ট্ৰা-ৱাইডবেণ্ড ডিভাইচসমূহৰ মাজৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ক"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"অগ্ৰাধিকাৰ দিয়া NFC পৰিশোধ সেৱাৰ তথ্য"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"এপ্টোক অগ্ৰাধিকাৰ দিয়া nfc পৰিশোধ সেৱাৰ পঞ্জীকৃত সহায়কসমূহ আৰু পৰিশোধ কৰিব লগা লক্ষ্যস্থান দৰে তথ্য পাবলৈ অনুমতি দিয়ে।"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"নিয়েৰ ফিল্ড কমিউনিকেশ্বন নিয়ন্ত্ৰণ কৰক"</string>
@@ -1858,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"বেটাৰী সঞ্চয়কাৰীয়ে গাঢ় থীম অন কৰে আৰু নেপথ্যৰ কাৰ্যকলাপ, কিছুমান ভিজুৱেল ইফেক্ট আৰু “Hey Google”ৰ দৰে সুবিধাসমূহ অফ কৰে অথবা সীমাবদ্ধ কৰে\n\n"<annotation id="url">"অধিক জানক"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"বেটাৰী সঞ্চয়কাৰীয়ে গাঢ় থীম অন কৰে আৰু নেপথ্যৰ কাৰ্যকলাপ, কিছুমান ভিজুৱেল ইফেক্ট আৰু “Hey Google”ৰ দৰে সুবিধাসমূহ অফ কৰে অথবা সীমাবদ্ধ কৰে।"</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>
@@ -1937,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"বন্ধ কৰক"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"উত্তৰ দিয়ক"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"প্ৰত্যাখ্যান কৰক"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"কল কাটি দিয়ক"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"অন্তৰ্গামী কল"</string>
@@ -2080,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"এই জাননীটোৰ গুৰুত্ব নীৰৱলৈ হ্ৰাস কৰা হৈছে। মতামত দিবলৈ টিপক।"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"এই জাননীটোৰ স্থান ওপৰলৈ কৰা হৈছে। মতামত দিবলৈ টিপক।"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"এই জাননীটোৰ স্থান তললৈ কৰা হৈছে। মতামত দিবলৈ টিপক।"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"উন্নত জাননী ব্যৱহাৰ কৰি চাওক"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"পৰামৰ্শ দিয়া কাৰ্য, প্ৰত্যুত্তৰ আৰু বহুতো সুবিধা পাই থাকিবলৈ উন্নত জাননী অন কৰক। Androidৰ অভিযোজিত জাননী আৰু সমৰ্থিত নহয়।"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"অন কৰক"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"এতিয়া নহয়"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"অধিক জানক"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"উন্নত প্ৰত্যুত্তৰে সম্পৰ্কৰ নাম আৰু বাৰ্তাৰ দৰে ব্যক্তিগত তথ্যৰ লগতে আটাইবোৰ জাননীৰ সমল পঢ়িব পাৰে। এই সুবিধাটোৱে জাননী অগ্ৰাহ্য কৰাৰ লগতে জাননীত থকা ফ’ন কলৰ উত্তৰ দিয়াৰ দৰে কাৰ্য বুটামৰ ওপৰত কাৰ্যব্যৱস্থা ল’ব পাৰে।\n\nলগতে, এই সুবিধাটোৱে অগ্ৰাধিকাৰ দিয়া ম’ড অন অথবা অফ কৰিব পাৰে আৰু সম্পৰ্কিত ছেটিং সলনি কৰিব পাৰে।"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ৰুটিন ম’ডৰ তথ্য জাননী"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"চ্চাৰ্জ কৰাৰ সচৰাচৰ সময়ৰ আগতেই বেটাৰি শেষ হ’ব পাৰে"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"বেটাৰিৰ খৰচ কমাবলৈ বেটাৰি সঞ্চয়কাৰী অন কৰা হৈছে"</string>
@@ -2279,14 +2267,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"আপুনি এতিয়া আপোনাৰ স্ক্ৰীনখনৰ কিছু অংশ বিবৰ্ধন কৰিব পাৰে"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ছেটিঙত অন কৰক"</string>
<string name="dismiss_action" msgid="1728820550388704784">"অগ্ৰাহ্য কৰক"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"ডিভাইচৰ মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাওক"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"ডিভাইচৰ কেমেৰ অৱৰোধৰ পৰা আঁতৰাওক"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> আৰু আটাইবোৰ এপ আৰু সেৱাৰ বাবে"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"অৱৰোধৰ পৰা আঁতৰাওক"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ছেন্সৰ সম্পৰ্কীয় গোপনীয়তাৰ নীতি"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"এপ্লিকেশ্বনৰ চিহ্ন"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"এপ্লিকেশ্বনৰ ব্ৰেণ্ডৰ প্ৰতিচ্ছবি"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 70c4534..69e0400 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Tətbiqə yaxınlıqdakı Bluetooth cihazlarını aşkarlamaq və birləşdirməyə icazə verir"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"birləşdirilmiş Bluetooth cihazlarına qoşulmaq"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Tətbiqə birləşdirilmiş Bluetooth cihazlarına qoşulmağa icazə verir"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"yaxınlıqdakı Bluetooth cihazlarında reklam etmək"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Tətbiqə yaxınlıqdakı Bluetooth cihazlarında reklam etmək imkanı verir"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"yaxınlıqdakı Ultra Genişzolaqlı cihazları arasında nisbi mövqeyi təyin etmək"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Tətbiqə yaxınlıqdakı Ultra Genişzolaqlı cihazları arasında nisbi mövqeyi təyin etməyə icazə verin"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Tərcih edilən NFC ödəniş xidməti məlumatı"</string>
@@ -1244,7 +1242,7 @@
<string name="unsupported_display_size_show" msgid="980129850974919375">"Həmişə göstərin"</string>
<string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> Android OS sisteminin uyğunsuz versiyası üçün hazırlandı və gözlənilməz şəkildə davrana bilər. Tətbiqin güncəllənmiş versiyası əlçatan ola bilər."</string>
<string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Həmişə göstərin"</string>
- <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Güncəlləməni yoxlayın"</string>
+ <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Güncəllənmə olmasını yoxlayın"</string>
<string name="smv_application" msgid="3775183542777792638">"Tətbiq <xliff:g id="APPLICATION">%1$s</xliff:g> (proses <xliff:g id="PROCESS">%2$s</xliff:g>) StrictMode siyasətini pozdu."</string>
<string name="smv_process" msgid="1398801497130695446">"<xliff:g id="PROCESS">%1$s</xliff:g> prosesi StrictMode siyasətini pozdu."</string>
<string name="android_upgrading_title" product="default" msgid="7279077384220829683">"Telefon yenilənir…"</string>
@@ -1575,7 +1573,7 @@
<string name="storage_usb_drive_label" msgid="6631740655876540521">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB drayv"</string>
<string name="storage_usb" msgid="2391213347883616886">"USB yaddaş"</string>
<string name="extract_edit_menu_button" msgid="63954536535863040">"Düzəliş edin"</string>
- <string name="data_usage_warning_title" msgid="9034893717078325845">"Data xəbərdarlığı"</string>
+ <string name="data_usage_warning_title" msgid="9034893717078325845">"Trafik xəbərdarlığı"</string>
<string name="data_usage_warning_body" msgid="1669325367188029454">"<xliff:g id="APP">%s</xliff:g> data istifadə etdiniz"</string>
<string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"Mobil data limitinə çatdı"</string>
<string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Wi-Fi data limitinə çatdı"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Enerjiyə Qənaət funksiyası Qaranlıq temanı aktiv edir və arxa fondakı fəaliyyəti, bəzi vizual effektləri və “Hey Google” kimi digər funksiyaları məhdudlaşdırır və ya deaktiv edir\n\n"<annotation id="url">"Ətraflı məlumat"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Enerjiyə Qənaət funksiyası Qaranlıq temanı aktiv edir və arxa fondakı fəaliyyəti, bəzi vizual effektləri və “Hey Google” kimi digər funksiyaları məhdudlaşdırır və ya deaktiv edir."</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>
@@ -1933,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Qapadın"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Cavab verin"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"İmtina edin"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Dəstəyi asın"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Gələn zəng"</string>
@@ -1965,7 +1962,7 @@
<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>
<string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Bu tətbiq köhnə Android versiyası üçün hazırlanıb və düzgün işləməyə bilər. Güncəlləməni yoxlayın və ya developer ilə əlaqə saxlayın."</string>
- <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Güncəlləməni yoxlayın"</string>
+ <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Güncəllənmə olmasını yoxlayın"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Yeni mesajlarınız var"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"Baxmaq üçün SMS tətbiqini açın"</string>
<string name="profile_encrypted_title" msgid="9001208667521266472">"Bəzi funksiyalar məhdudlaşdırıla bilər"</string>
@@ -2076,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Bu bildiriş Səssiz rejimə keçirilib. Rəy bildirmək üçün toxunun."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Bu bildiriş yuxarı sıraya keçirilib. Rəy bildirmək üçün toxunun."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Bu bildiriş aşağı sıraya keçirilib. Rəy bildirmək üçün toxunun."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Genişləndirilmiş bildirişləri sınayın"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Təklif olunan əməliyyatlar, cavablar və daha çoxunu almağa davam etmək üçün genişləndirilmiş bildirişləri aktiv edin. Android Adaptiv Bildirişləri artıq dəstəklənmir."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Aktiv edin"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"İndi yox"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Ətraflı məlumat"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Genişləndirilmiş bildirişlər, kontakt adları və mesajlar kimi şəxsi məlumatlar daxil olmaqla bütün bildiriş məzmununu oxuya bilər. Bu funksiya həmçinin bildirişləri qapada və ya telefon zənglərinə cavab vermək kimi bildirişlərdəki düymələr üzərində əməliyyatlar edə bilər.\n\nBu funksiya həmçinin Prioritet rejimini aktiv və ya deaktiv edə və əlaqəli ayarları dəyişdirə bilər."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Rejim üçün məlumat bildirişi"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Batareya həmişəki vaxtdan əvvəl bitə bilər"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Enerjiyə Qənaət rejimi batareya istifadəsinin müddətini artırmaq üçün aktiv edilir"</string>
@@ -2275,14 +2266,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"İndi ekranınızın bir hissəsini böyüdə bilərsiniz"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Ayarlarda aktiv edin"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Qapadın"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Cihaz mikrofonunu blokdan çıxarın"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Cihaz kamerasını blokdan çıxarın"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> və bütün tətbiqlər və cihazlar üçün"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Blokdan çıxarın"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensor Məxfiliyi"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Tətbiq ikonası"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Tətbiqin brend şəkli"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 1cd4c12..5a7e890 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1875,10 +1875,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizuelne efekte i funkcije, na primer, „Hej Google“.\n\n"<annotation id="url">"Saznajte više"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizuelne efekte i funkcije, na primer, „Hej Google“."</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>
@@ -1962,6 +1960,7 @@
<string name="close_button_text" msgid="10603510034455258">"Zatvori"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Odgovori"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Odbij"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Prekini vezu"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Dolazni poziv"</string>
@@ -2107,18 +2106,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Ovo obaveštenje je degradirano u Nečujno. Dodirnite da biste naveli povratne informacije."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Ovo obaveštenje je rangirano više. Dodirnite da biste naveli povratne informacije."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Ovo obaveštenje je rangirano niže. Dodirnite da biste naveli povratne informacije."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Probajte poboljšana obaveštenja"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Uključite poboljšana obaveštenja da biste i dalje dobijali preporučene radnje, odgovore i drugo. Prilagodljiva obaveštenja za Android više nisu podržana."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Uključi"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Ne sada"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saznajte više"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Poboljšana obaveštenja mogu da čitaju sadržaj svih obaveštenja, uključujući lične podatke, poput imena kontakata i poruka. Ova funkcija može i da odbacuje obaveštenja ili aktivira dugmad u obaveštenjima, poput javljanja na telefonske pozive.\n\nOva funkcija može i da uključi ili isključi Prioritetni režim i da menja povezana podešavanja."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Obaveštenje o informacijama Rutinskog režima"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Baterija će se možda isprazniti pre uobičajenog punjenja"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Ušteda baterije je aktivirana da bi se produžilo trajanje baterije"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index c1abeca..bdfab40 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -544,10 +544,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Дазваляе праграме выяўляць прылады з Bluetooth і спалучацца з імі"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"падключацца да спалучаных прылад з Bluetooth"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Дазваляе праграме падключацца да спалучаных прылад з Bluetooth"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"адпраўляць рэкламу на прылады з Bluetooth паблізу"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Дазволіць праграме адпраўляць рэкламу на прылады з Bluetooth, якія знаходзяцца паблізу"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"вызначаць адлегласць паміж прыладамі з звышшырокапалоснай сувяззю"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Дазволіць праграме вызначаць адлегласць паміж прыладамі паблізу, якія выкарыстоўваюць звышшырокапалосную сувязь"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Інфармацыя пра прыярытэтны сэрвіс аплаты NFC"</string>
@@ -1753,8 +1751,8 @@
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Гатова"</string>
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Дэактываваць камбінацыю хуткага доступу"</string>
<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>
+ <string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія колераў"</string>
+ <string name="color_correction_feature_name" msgid="3655077237805422597">"Карэкцыя колераў"</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>
@@ -1900,10 +1898,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"У рэжыме эканоміі зараду ўключаецца цёмная тэма і абмяжоўваюцца ці выключаюцца дзеянні ў фонавым рэжыме, некаторыя візуальныя эфекты і функцыі, напрыклад \"Ok Google\"\n\n"<annotation id="url">"Даведацца больш"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"У рэжыме эканоміі зараду ўключаецца цёмная тэма і абмяжоўваюцца ці выключаюцца дзеянні ў фонавым рэжыме, некаторыя візуальныя эфекты і функцыі, напрыклад \"Ok Google\"."</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>
@@ -1995,6 +1991,8 @@
<string name="close_button_text" msgid="10603510034455258">"Закрыць"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Адказаць"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Адхіліць"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Завяршыць"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Уваходны выклік"</string>
@@ -2142,18 +2140,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Гэта апавяшчэнне пераведзена ў рэжым \"Без гуку\". Націсніце тут і дайце водгук."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Гэта апавяшчэнне ацэнена як важнае. Націсніце тут і дайце водгук."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Гэта апавяшчэнне ацэнена як няважнае. Націсніце тут і дайце водгук."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Палепшаныя апавяшчэнні"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Каб і далей атрымліваць прапановы дзеянняў, адказаў і іншага змесціва, уключыце палепшаныя апавяшчэнні. Адаптыўныя апавяшчэнні Android больш не падтрымліваюцца."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Уключыць"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Не зараз"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Даведацца больш"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Палепшаныя апавяшчэнні маюць доступ да змесціва ўсіх апавяшчэнняў, у тым ліку да асабістай інфармацыі – імён кантактаў і паведамленняў. Гэта функцыя таксама можа адхіляць апавяшчэнні ці актываваць у іх кнопкі дзеянняў, у тым ліку адказваць на тэлефонныя выклікі.\n\nГэта функцыя можа ўключаць і выключаць прыярытэтны рэжым, а таксама змяняць звязаныя налады."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Апавяшчэнне з інфармацыяй пра ўсталяваны рэжым"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Акумулятар можа разрадзіцца хутчэй, чым прыйдзе час звычайнай зарадкі"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Каб павялічыць тэрмін работы акумулятара, уключаны рэжым эканоміі зараду"</string>
@@ -2343,14 +2335,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Цяпер можна павялічваць частку экрана"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Уключыць у Наладах"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Адхіліць"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Разблакіруйце мікрафон прылады"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Разблакіруйце камеру прылады"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Для праграмы <b><xliff:g id="APP">%s</xliff:g></b> і ўсіх праграм і сэрвісаў"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Разблакіраваць"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Прыватнасць інфармацыі з датчыка"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Значок праграмы"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Відарыс брэнда праграмы"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 52e686c92..5b8a81c 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Режимът за запазване на батерията включва тъмната тема и ограничава или изключва активността на заден план, някои визуални ефекти и различни функции, като например „Ok Google“.\n\n"<annotation id="url">"Научете повече"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Режимът за запазване на батерията включва тъмната тема и ограничава или изключва активността на заден план, някои визуални ефекти и различни функции, като например „Ok Google“."</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>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Затваряне"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"„<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>“: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Отговор"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Видеообаждане"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Отхвърляне"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Затваряне"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Входящо обаждане"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Това известие бе понижено до беззвучно. Докоснете, за да изпратите отзиви."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Това известие бе класирано по-високо. Докоснете, за да изпратите отзиви."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Това известие бе класирано по-ниско. Докоснете, за да изпратите отзиви."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Изпробвайте подобрен. известия"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"За да продължите да получавате предложени действия, отговори и др., включете функцията за подобрени известия. Адаптивните известия за Android вече не се поддържат."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Включване"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Не сега"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Научете повече"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Функцията за подобрени известия може да чете цялото съдържание в дадено известие, включително личната информация, като например имената на контактите и текстовите съобщения. Тя има възможност да отхвърля известията или да предприема действия по бутоните в тях, като например приемане на телефонни обаждания.\n\nСъщо така функцията може да включва или изключва приоритетния режим и да променя сродни настройки."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Известие с информация за режима на поредица"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батерията може да се изтощи преди обичайното зареждане"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Режимът за запазване на батерията е активиран с цел удължаване на живота на батерията"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 17be781..57f2157 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -319,10 +319,8 @@
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"শারীরিক অ্যাক্টিভিটি অ্যাক্সেস করা"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"ক্যামেরা"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"ছবি তোলা এবং ভিডিও রেকর্ড"</string>
- <!-- no translation found for permgrouplab_nearby_devices (5529147543651181991) -->
- <skip />
- <!-- no translation found for permgroupdesc_nearby_devices (3213561597116913508) -->
- <skip />
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"আশেপাশের ডিভাইস"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"আশেপাশের ডিভাইস খুঁজে দেখুন ও তার সাথে কানেক্ট করুন"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"কল লগ"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"ফোন কল লগ পড়ে এবং দেখে"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"ফোন"</string>
@@ -540,14 +538,10 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"অ্যাপটিকে কাছাকাছি ব্লুটুথ ডিভাইস খুঁজে দেখতে এবং তার সাথে পেয়ার করার অনুমতি দেয়"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"পেয়ার করা ব্লুটুথ ডিভাইসের সাথে কানেক্ট করুন"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"অ্যাপটিকে পেয়ার করা ব্লুটুথ ডিভাইসের সাথে কানেক্ট করতে অনুমতি দেয়"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
- <!-- no translation found for permlab_uwb_ranging (8141915781475770665) -->
- <skip />
- <!-- no translation found for permdesc_uwb_ranging (2519723069604307055) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"আশেপাশের ব্লুটুথ ডিভাইস বিজ্ঞাপন দেখান"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"আশেপাশের ব্লুটুথ ডিভাইস বিজ্ঞাপন দেওয়ার জন্য অ্যাপকে অনুমতি দেয়"</string>
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"আশেপাশের Ultra-Wideband ডিভাইসগুলির আপেক্ষিক অবস্থান নির্ণয় করুন"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"অ্যাপকে আশেপাশের Ultra-Wideband ডিভাইসগুলির আপেক্ষিক অবস্থান নির্ণয় করার অনুমতি দিন"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"পছন্দের NFC পেমেন্ট পরিষেবার তথ্য"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"অ্যাপের মাধ্যমে পছন্দসই এনএফসি পেমেন্ট পরিষেবার তথ্য, যেমন রেজিস্ট্রার করার সহায়তা এবং রুট ডেস্টিনেশন সম্পর্কিত তথ্য অ্যাক্সেস করার অনুমতি দেয়।"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"নিয়ার ফিল্ড কমিউনিকেশন নিয়ন্ত্রণ করে"</string>
@@ -1858,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ব্যাটারি সেভার ডার্ক থিম চালু করে এবং ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজ্যুয়াল এফেক্ট এবং “Ok Google”-এর মতো ফিচার সীমিত করে বা বন্ধ করে দেয়\n\n"<annotation id="url">"আরও জানুন"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"ব্যাটারি সেভার ডার্ক থিম চালু করে এবং ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজ্যুয়াল এফেক্ট এবং “Ok Google”-এর মতো ফিচার সীমিত করে বা বন্ধ করে দেয়।"</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>
@@ -1937,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"বন্ধ করুন"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"উত্তর"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"বাতিল করুন"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"কল কেটে দেওয়া"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"ইনকামিং কল"</string>
@@ -2080,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"এই বিজ্ঞপ্তির গুরুত্ব কমিয়ে মিউট হিসেবে সেট করা হয়েছে। মতামত জানাতে ট্যাপ করুন।"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"এই বিজ্ঞপ্তির গুরুত্ব বাড়ানো হয়েছে। মতামত জানাতে ট্যাপ করুন।"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"এই বিজ্ঞপ্তির গুরুত্ব কমানো হয়েছে। মতামত জানাতে ট্যাপ করুন।"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"উন্নত নোটিফিকেশন ব্যবহার করুন"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"সাজেস্ট করা অ্যাকশন, উত্তর এবং আরও অনেক কিছু পাওয়া চালিয়ে যেতে, উন্নত নোটিফিকেশন পাওয়ার সুবিধা চালু করুন। Android অ্যাডাপ্টিভ নোটিফিকেশন আর কাজ করবে না।"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"চালু করুন"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"এখন নয়"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"আরও জানুন"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"উন্নত নোটিফিকেশন পরিচিতির নাম এবং মেসেজের মতো ব্যক্তিগত তথ্য ছাড়াও নোটিফিকেশনের সবকটি কন্টেন্ট পড়তে পারবে। এছাড়াও, এই ফিচার নোটিফিকেশন বাতিল করতে পারবে এবং নোটিফিকেশনে থাকা বোতামের সাহায্যে অ্যাকশন নিতে পারবে, যেমন ফোন কলের উত্তর দেওয়া।\n\nএই ফিচার প্রায়োরিটি মোড চালু বা বন্ধ করতে এবং সেই সম্পর্কিত সেটিংস পরিবর্তনও করতে পারবে।"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"রুটিন মোডের তথ্য সংক্রান্ত বিজ্ঞপ্তি"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"সাধারণত যখন চার্জ দেন, তার আগে চার্জ শেষ হয়ে যেতে পারে"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ডিভাইস বেশিক্ষণ চালু রাখতে ব্যাটারি সেভার চালু করা হয়েছে"</string>
@@ -2279,14 +2267,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"আপনার স্ক্রিনের অংশ এখন আপনি বড় করে দেখতে পারবেন"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"সেটিংস থেকে চালু করুন"</string>
<string name="dismiss_action" msgid="1728820550388704784">"বাতিল করুন"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"ডিভাইসের মাইক্রোফোন আনব্লক করুন"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"ডিভাইসের ক্যামেরা আনব্লক করুন"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> এবং সবকটি অ্যাপ ও পরিষেবার জন্য"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"আনব্লক করুন"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"সেন্সর গোপনীয়তা"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"অ্যাপের আইকন"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"অ্যাপের ব্র্যান্ড ছবি"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index b034223..195b8f7 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1875,10 +1875,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnosti u pozadini, određene vizuelne efekte i funkcije kao što je \"Ok Google\"\n\n"<annotation id="url">"Saznajte više"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Ušteda baterije uključuje Tamnu temu i ograničava ili isključuje aktivnosti u pozadini, određene vizuelne efekte i funkcije kao što je \"Ok Google\"."</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>
@@ -1962,6 +1960,7 @@
<string name="close_button_text" msgid="10603510034455258">"Zatvori"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Odgovori"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Videozapis"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Odbaci"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Prekini vezu"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Dolazni poziv"</string>
@@ -2107,18 +2106,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Značaj ovog obavještenja je umanjen na Nečujno. Dodirnite da pošaljete povratne informacije."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Značaj ovog obavještenja je povećan. Dodirnite da pošaljete povratne informacije."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Značaj ovog obavještenja je umanjen. Dodirnite da pošaljete povratne informacije."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Probajte poboljšana obavještenja"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Da nastavite primati prijedloge radnji, odgovore i još mnogo toga, uključite poboljšana obavještenja. Prilagodljiva obavještenja Androida više nisu podržana."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Uključi"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Ne sada"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saznajte više"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Poboljšana obavještenja mogu čitati sav sadržaj obavještenja, uključujući lične informacije kao što su imena kontakata i poruke. Ova funkcija također može odbaciti obavještenja ili poduzeti radnje vezane za dugmad u obavještenjima, kao što je javljanje na telefonske pozive.\n\nOva funkcija također može uključiti ili isključiti način rada Prioriteti i promijeniti srodne postavke."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Obavještenje za informacije Rutinskog načina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Moguće je da će se baterija isprazniti prije uobičajenog punjenja"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Ušteda baterije je aktivirana da bi se produžio vijek trajanja baterije"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 96f0b7c..7fd2ede 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals i funcions com \"Hey Google\".\n\n"<annotation id="url">"Més informació"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals i funcions com \"Hey Google\"."</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>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Tanca"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Respon"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Vídeo"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Rebutja"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Penja"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Trucada entrant"</string>
@@ -1948,7 +1947,7 @@
<string name="user_creation_adding" msgid="7305185499667958364">"Concedeixes permís a <xliff:g id="APP">%1$s</xliff:g> per crear un usuari amb el compte <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="52674936078683285">"Afegeix un idioma"</string>
<string name="country_selection_title" msgid="5221495687299014379">"Preferència de regió"</string>
- <string name="search_language_hint" msgid="7004225294308793583">"Nom de l\'idioma"</string>
+ <string name="search_language_hint" msgid="7004225294308793583">"Escriu el nom de l\'idioma"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Suggerits"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Tots els idiomes"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"Totes les regions"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"El nivell d\'aquesta notificació s\'ha disminuït a Silenci. Toca per proporcionar suggeriments."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Aquesta notificació s\'ha classificat amb un nivell superior. Toca per proporcionar suggeriments."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Aquesta notificació s\'ha classificat amb un nivell inferior. Toca per proporcionar suggeriments."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Prova notificacions millorades"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Per continuar rebent accions suggerides, respostes i més, activa les notificacions millorades. Les notificacions adaptatives d\'Android ja no s\'admeten."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Activa"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Ara no"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Més informació"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Les notificacions millorades poden llegir tot el contingut de les notificacions, inclosa la informació personal com els noms dels contactes i els missatges. Aquesta funció també pot ignorar les notificacions o fer accions amb els botons de les notificacions, com ara contestar a trucades.\n\nA més, pot activar i desactivar el mode Prioritat i canviar-ne la configuració."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificació d\'informació del mode de rutina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"És possible que la bateria s\'esgoti abans de la càrrega habitual"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"S\'ha activat l\'estalvi de bateria per prolongar-ne la durada"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 0dd472f..0448954 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1406,10 +1406,10 @@
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Bylo zjištěno analogové zvukové příslušenství"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Připojené zařízení není s tímto telefonem kompatibilní. Klepnutím zobrazíte další informace."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"Ladění přes USB připojeno"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"Klepnutím vypnete ladění přes USB"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"Klepnutím ladění přes USB vypnete"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Vyberte, chcete-li zakázat ladění přes USB."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Bezdrátové ladění je připojeno"</string>
- <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Klepnutím vypnete bezdrátové ladění"</string>
+ <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Klepnutím bezdrátové ladění vypnete"</string>
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Vyberte, chcete-li zakázat bezdrátové ladění."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Režim správce testů je aktivní"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Chcete-li deaktivovat režim správce testů, restartujte zařízení do továrního nastavení."</string>
@@ -1734,13 +1734,13 @@
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nezapínat"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"ZAP"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"VYP"</string>
- <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Chcete službě <xliff:g id="SERVICE">%1$s</xliff:g> povolit, aby nad vaším zařízením měla plnou kontrolu?"</string>
+ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Chcete službě <xliff:g id="SERVICE">%1$s</xliff:g> povolit plnou kontrolu nad vaším zařízením?"</string>
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"Pokud zapnete službu <xliff:g id="SERVICE">%1$s</xliff:g>, zařízení nebude používat zámek obrazovky k vylepšení šifrování dat."</string>
- <string name="accessibility_service_warning_description" msgid="291674995220940133">"Plná kontrola je vhodná u aplikací, které vám pomáhají s usnadněním přístupu, nikoli u většiny aplikací."</string>
+ <string name="accessibility_service_warning_description" msgid="291674995220940133">"Plná kontrola je vhodná u aplikací, které vám pomáhají s usnadněním přístupu. U většiny aplikací však vhodná není."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Zobrazení a ovládání obrazovky"</string>
- <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Může číst veškerý obsah obrazovky a zobrazovat obsah přes ostatní aplikace."</string>
+ <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Služba může číst veškerý obsah obrazovky a zobrazovat ho přes ostatní aplikace."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Zobrazení a provádění akcí"</string>
- <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Může sledovat vaše interakce s aplikací nebo hardwarovým senzorem a komunikovat s aplikacemi namísto vás."</string>
+ <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Služba může sledovat vaše interakce s aplikací nebo hardwarovým senzorem a komunikovat s aplikacemi namísto vás."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Povolit"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Zakázat"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Chcete-li některou funkci začít používat, klepněte na ni:"</string>
@@ -1898,10 +1898,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Spořič baterie zapne tmavý motiv a omezí nebo vypne aktivitu na pozadí, některé vizuální efekty a funkce jako „Ok Google“\n\n"<annotation id="url">"Další informace"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Spořič baterie zapne tmavý motiv a omezí nebo vypne aktivitu na pozadí, některé vizuální efekty a funkce jako „Ok Google“."</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>
@@ -1993,6 +1991,8 @@
<string name="close_button_text" msgid="10603510034455258">"Zavřít"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Přijmout"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Odmítnout"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Zavěsit"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Příchozí hovor"</string>
@@ -2140,18 +2140,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Toto oznámení bylo ztlumeno. Po klepnutí můžete zadat zpětnou vazbu."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"U tohoto oznámení byla zvýšena priorita. Po klepnutí můžete zadat zpětnou vazbu."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"U tohoto oznámení byla snížena priorita. Po klepnutí můžete zadat zpětnou vazbu."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Vyzkoušejte vylepšená oznámení"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Pokud chcete dál dostávat návrhy akcí, odpovědí a další, zapněte vylepšená oznámení. Adaptivní oznámení systému Android už nejsou podporována."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Zapnout"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Teď ne"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Další informace"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Vylepšená oznámení mohou číst veškerý obsah oznámení včetně osobních údajů, jako jsou jména kontaktů a obsah zpráv. Tato funkce také může zavírat oznámení nebo aktivovat tlačítka v oznámeních, například přijímat telefonické hovory.\n\nMůže také zapnout nebo vypnout prioritní režim a změnit související nastavení."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Informační oznámení režimu sledu činností"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Baterie se možná vybije před obvyklým časem nabití"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Byl aktivován spořič baterie za účelem prodloužení výdrže"</string>
@@ -2349,5 +2343,5 @@
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikona aplikace"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Image značky aplikace"</string>
<string name="view_and_control_notification_title" msgid="4300765399209912240">"Zkontrolujte nastavení přístupu"</string>
- <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> může zobrazit a ovládat tuto obrazovku. Klepnutím to zkontrolujete."</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"Tuto obrazovku může zobrazit a ovládat služba <xliff:g id="SERVICE_NAME">%s</xliff:g>. Klepnutím to zkontrolujete."</string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 94b9a92..55fcea3 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -566,8 +566,8 @@
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Tillader, at appen kan ændre din billedsamling."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"læse placeringer fra din mediesamling"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Tillader, at appen kan læse placeringer fra din mediesamling."</string>
- <string name="biometric_app_setting_name" msgid="3339209978734534457">"Brug biometriske systemer"</string>
- <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Brug biometriske systemer eller skærmlås"</string>
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Brug biometri"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Brug biometri eller skærmlås"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Bekræft, at det er dig"</string>
<string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Brug dine biometriske data for at fortsætte"</string>
<string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Brug dine biometriske data eller din skærmlås for at fortsætte"</string>
@@ -1854,10 +1854,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Opdateret af din administrator"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Slettet af din administrator"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Batterisparefunktionen aktiverer Mørkt tema og deaktiverer eller begrænser aktivitet i baggrunden, visse visuelle effekter og funktioner som f.eks. \"Hey Google\"\n\n"<annotation id="url">"Få flere oplysninger"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Batterisparefunktionen aktiverer Mørkt tema og deaktiverer eller begrænser aktivitet i baggrunden, visse visuelle effekter og funktioner som f.eks. \"Hey Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Datasparefunktionen forhindrer nogle apps i at sende eller modtage data i baggrunden for at reducere dataforbruget. En app, der er i brug, kan få adgang til data, men gør det måske ikke så ofte. Dette kan f.eks. betyde, at billeder ikke vises, før du trykker på dem."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Vil du aktivere Datasparefunktion?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivér"</string>
@@ -1933,6 +1931,8 @@
<string name="close_button_text" msgid="10603510034455258">"Luk"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Besvar"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Afvis"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Læg på"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Indgående opkald"</string>
@@ -2076,18 +2076,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Denne notifikation blev angivet som Lydløs. Tryk for at give feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Denne notifikation blev placeret højere. Tryk for at give feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Denne notifikation blev placeret lavere. Tryk for at give feedback."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Prøv forbedrede notifikationer"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Aktivér forbedrede notifikationer for at fortsætte med at få forslag til handlinger, svar og meget mere. Tilpassede Android-notifikationer understøttes ikke længere."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Aktivér"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Ikke nu"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Få flere oplysninger"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Forbedrede notifikationer kan læse alt indhold i notifikationer, bl.a. personlige oplysninger som f.eks. beskeder og navne på kontakter. Denne funktion kan også afvise notifikationer eller aktivere knapper i notifikationer, herunder besvare opkald.\n\nDenne funktion kan også aktivere og deaktivere tilstanden Prioritet samt ændre relaterede indstillinger."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notifikation med oplysninger om rutinetilstand"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Enheden løber muligvis tør for batteri, inden du normalt oplader den"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Batterisparefunktion er aktiveret for at forlænge batteritiden"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 6e8ab72..7d42e1b 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -319,10 +319,8 @@
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"Zugriff auf körperliche Aktivität"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"Bilder und Videos aufnehmen"</string>
- <!-- no translation found for permgrouplab_nearby_devices (5529147543651181991) -->
- <skip />
- <!-- no translation found for permgroupdesc_nearby_devices (3213561597116913508) -->
- <skip />
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Geräte in der Nähe"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"Geräte in der Nähe finden und eine Verbindung herstellen"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"Anrufliste"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"Schreib- und Lesezugriff auf Anrufliste"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"Telefon"</string>
@@ -540,14 +538,10 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Erlaubt der App, Bluetooth-Geräte in der Nähe zu finden und zu koppeln"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"Mit gekoppelten Bluetooth-Geräten verbinden"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Erlaubt der App, sich mit gekoppelten Bluetooth-Geräten zu verbinden"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
- <!-- no translation found for permlab_uwb_ranging (8141915781475770665) -->
- <skip />
- <!-- no translation found for permdesc_uwb_ranging (2519723069604307055) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"Inhalte an Bluetooth-Geräte in der Nähe senden"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Erlaubt der App, Inhalte an Bluetooth-Geräte in der Nähe zu senden"</string>
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"Relative Distanz zwischen Ultrabreitband-Geräten in der Nähe bestimmen"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Ermöglicht der App, die relative Distanz zwischen Ultrabreitband-Geräten in der Nähe zu bestimmen"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informationen zum bevorzugten NFC-Zahlungsdienst"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Ermöglicht der App, Informationen zum bevorzugten NFC-Zahlungsdienst abzurufen, etwa registrierte Hilfsmittel oder das Routenziel."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"Nahfeldkommunikation steuern"</string>
@@ -1937,6 +1931,8 @@
<string name="close_button_text" msgid="10603510034455258">"Schließen"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Annehmen"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Ablehnen"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Auflegen"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Eingehender Anruf"</string>
@@ -2007,7 +2003,7 @@
<string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"In den Texteingabemodus wechseln, um die Uhrzeit einzugeben."</string>
<string name="time_picker_radial_mode_description" msgid="1222342577115016953">"In den Uhrzeitmodus wechseln, um die Uhrzeit einzugeben."</string>
<string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Optionen für automatisches Ausfüllen"</string>
- <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Für \"Automatisches Ausfüllen\" speichern"</string>
+ <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Für „Automatisches Ausfüllen“ speichern"</string>
<string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Inhalte können nicht automatisch ausgefüllt werden"</string>
<string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Keine Vorschläge für automatisches Ausfüllen"</string>
<plurals name="autofill_picker_some_suggestions" formatted="false" msgid="6651883186966959978">
@@ -2279,14 +2275,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Du kannst das Display teilweise vergrößern"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"In den Einstellungen aktivieren"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Schließen"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Blockierung des Gerätemikrofons aufheben"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Blockierung der Gerätekamera aufheben"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Für <b><xliff:g id="APP">%s</xliff:g></b> und alle Apps und Dienste"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Blockierung aufheben"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Datenschutz für Sensoren"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"App-Symbol"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"App-Branding-Hintergrundbild"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 517ba84..1612a7b 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Η Εξοικονόμηση μπαταρίας ενεργοποιεί το Σκούρο θέμα και περιορίζει ή απενεργοποιεί τη δραστηριότητα στο παρασκήνιο, ορισμένα οπτικά εφέ και λειτουργίες όπως την εντολή \"Ok Google\"\n\n"<annotation id="url">"Μάθετε περισσότερα"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Η Εξοικονόμηση μπαταρίας ενεργοποιεί το Σκούρο θέμα και περιορίζει ή απενεργοποιεί τη δραστηριότητα στο παρασκήνιο, ορισμένα οπτικά εφέ και λειτουργίες όπως την εντολή \"Ok Google\"."</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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Κλείσιμο"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Απάντηση"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Απόρριψη"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Τερματισμός"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Εισερχόμενη κλήση"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Αυτή η ειδοποίηση υποβιβάστηκε στις Αθόρυβες. Πατήστε για να υποβάλετε σχόλια."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Αυτή η ειδοποίηση κατατάχθηκε ψηλότερα. Πατήστε για να υποβάλετε σχόλια."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Αυτή η ειδοποίηση κατατάχθηκε χαμηλότερα. Πατήστε για να υποβάλετε σχόλια."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Δοκιμή βελτιωμ. ειδοποιήσεων"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Για να συνεχίσετε να λαμβάνετε προτεινόμενες ενέργειες, απαντήσεις και άλλα, ενεργοποιήστε τις βελτιωμένες ειδοποιήσεις. Δεν υποστηρίζονται πλέον οι Προσαρμοστικές ειδοποιήσεις Android."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Ενεργοποίηση"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Όχι τώρα"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Μάθετε περισσότερα"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Οι Βελτιωμένες ειδοποιήσεις έχουν τη δυνατότητα ανάγνωσης όλου του περιεχομένου ειδοποιήσεων, συμπεριλαμβανομένων των προσωπικών πληροφοριών, όπως ονομάτων επαφών και μηνυμάτων. Αυτή η λειτουργία έχει επίσης τη δυνατότητα παράβλεψης ειδοποιήσεων ή λήψης ενεργειών σε κουμπιά στις ειδοποιήσεις, όπως η απάντηση τηλεφωνικών κλήσεων.\n\nΕπίσης, έχει τη δυνατότητα ενεργοποίησης ή απενεργοποίησης της λειτουργίας Προτεραιότητας, καθώς και αλλαγής των σχετικών ρυθμίσεων."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Ειδοποίηση πληροφοριών λειτουργίας Ρουτίνας"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Η μπαταρία μπορεί να εξαντληθεί πριν από τη συνηθισμένη φόρτιση"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Η Εξοικονόμηση μπαταρίας ενεργοποιήθηκε για την επέκταση της διάρκειας ζωής της μπαταρίας"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index e841e27..9692781 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1852,10 +1852,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'\n\n"<annotation id="url">"Learn more"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'."</string>
<string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you\'re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Turn on Data Saver?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Turn on"</string>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Close"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang up"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"This notification was demoted to silent. Tap to provide feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"This notification was ranked higher. Tap to provide feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"This notification was ranked lower. Tap to provide feedback."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Try enhanced notifications"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"To keep getting suggested actions, replies and more, turn on enhanced notifications. Android Adaptive Notifications are no longer supported."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Turn on"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Not now"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Learn more"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Enhanced notifications can read all notification content, including personal information like contact names and messages. This feature can also dismiss notifications or take actions on buttons in notifications, such as answering phone calls.\n\nThis feature can also turn Priority mode on or off and change related settings."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Routine Mode info notification"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Battery may run out before usual charge"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Battery Saver activated to extend battery life"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 52d6dc5..f41ab42 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1852,10 +1852,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'\n\n"<annotation id="url">"Learn more"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'."</string>
<string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you\'re currently using can access data, but may do so less frequently. This may mean, for example, that images don\'t display until you tap them."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Turn on Data Saver?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Turn on"</string>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Close"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang up"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"This notification was demoted to silent. Tap to provide feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"This notification was ranked higher. Tap to provide feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"This notification was ranked lower. Tap to provide feedback."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Try enhanced notifications"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"To keep getting suggested actions, replies and more, turn on enhanced notifications. Android Adaptive Notifications are no longer supported."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Turn on"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Not now"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Learn more"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Enhanced notifications can read all notification content, including personal information like contact names and messages. This feature can also dismiss notifications or take actions on buttons in notifications, such as answering phone calls.\n\nThis feature can also turn Priority mode on or off and change related settings."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Routine Mode info notification"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Battery may run out before usual charge"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Battery Saver activated to extend battery life"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index a25f29c..098a936 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1852,10 +1852,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'\n\n"<annotation id="url">"Learn more"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'."</string>
<string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app that you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Turn on Data Saver?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Turn on"</string>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Close"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang up"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"This notification was demoted to silent. Tap to provide feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"This notification was ranked higher. Tap to provide feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"This notification was ranked lower. Tap to provide feedback."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Try enhanced notifications"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"To keep getting suggested actions, replies and more, turn on enhanced notifications. Android Adaptive Notifications are no longer supported."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Turn on"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Not now"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Learn more"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Enhanced notifications can read all notification content, including personal information like contact names and messages. This feature can also dismiss notifications or take actions on buttons in notifications, such as answering phone calls.\n\nThis feature can also turn Priority mode on or off and change related settings."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Routine Mode info notification"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Battery may run out before usual charge"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Battery Saver activated to extend battery life"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index dbb72e9..a7e1f5f 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1852,10 +1852,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Updated by your admin"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Deleted by your admin"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'\n\n"<annotation id="url">"Learn more"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects and features like \'Hey Google\'."</string>
<string name="data_saver_description" msgid="4995164271550590517">"To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app that you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Turn on Data Saver?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Turn on"</string>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Close"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang up"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"This notification was demoted to silent. Tap to provide feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"This notification was ranked higher. Tap to provide feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"This notification was ranked lower. Tap to provide feedback."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Try enhanced notifications"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"To keep getting suggested actions, replies and more, turn on enhanced notifications. Android Adaptive Notifications are no longer supported."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Turn on"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Not now"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Learn more"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Enhanced notifications can read all notification content, including personal information like contact names and messages. This feature can also dismiss notifications or take actions on buttons in notifications, such as answering phone calls.\n\nThis feature can also turn Priority mode on or off and change related settings."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Routine Mode info notification"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Battery may run out before usual charge"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Battery Saver activated to extend battery life"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 0c50419..2cdf5ba 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1929,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Close"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang Up"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 128e2fb..6a1cddc 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -321,7 +321,7 @@
<string name="permgroupdesc_camera" msgid="7585150538459320326">"tomar fotografías y grabar videos"</string>
<string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Dispositivos cercanos"</string>
<string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"descubrir y conectarse a dispositivos cercanos"</string>
- <string name="permgrouplab_calllog" msgid="7926834372073550288">"Llamadas"</string>
+ <string name="permgrouplab_calllog" msgid="7926834372073550288">"Registros de llamadas"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"leer y escribir el registro de llamadas telefónicas"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"Teléfono"</string>
<string name="permgroupdesc_phone" msgid="270048070781478204">"hacer y administrar llamadas telefónicas"</string>
@@ -1852,10 +1852,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Tu administrador actualizó este paquete"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Tu administrador borró este paquete"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"El Ahorro de batería activa el Tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales y otras funciones, como \"Hey Google\"\n\n"<annotation id="url">"Más información"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"El Ahorro de batería activa el Tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales y otras funciones, como \"Hey Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Para reducir el uso de datos, el modo Ahorro de datos evita que algunas apps envíen y reciban datos en segundo plano. La app que estés usando podrá acceder a los datos, pero con menor frecuencia. De esta forma, por ejemplo, las imágenes no se mostrarán hasta que las presiones."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"¿Deseas activar Ahorro de datos?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Cerrar"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Responder"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Rechazar"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Colgar"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Llamada entrante"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Esta notificación descendió de a Silenciada. Presiona para enviar comentarios."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Esta notificación recibió una clasificación superior. Presiona para enviar comentarios."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Esta notificación recibió una clasificación inferior. Presiona para enviar comentarios."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Prueba las notif. mejoradas"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Activa las notificaciones mejoradas para seguir recibiendo acciones sugeridas, respuestas y mucho más. Ya no se admiten las notificaciones adaptables de Android."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Activar"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Ahora no"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Más información"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Las notificaciones mejoradas pueden leer todo el contenido de notificaciones, incluido el que contenga información personal, como nombres de contactos y mensajes. Esta función también podrá descartar notificaciones o realizar acciones en botones de notificaciones, como responder llamadas.\n\nTambién puede activar o desactivar el Modo prioridad y cambiar la configuración relacionada."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificación de información del modo de Rutinas"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Es posible que la batería se agote antes de la carga habitual"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Se activó el Ahorro de batería para extender la duración de la batería"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 7bed3e6..62ecf65 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -34,7 +34,7 @@
<string name="defaultMsisdnAlphaTag" msgid="2285034592902077488">"MSISDN1"</string>
<string name="mmiError" msgid="2862759606579822246">"Se ha producido un problema de conexión o el código MMI no es válido."</string>
<string name="mmiFdnError" msgid="3975490266767565852">"La operación solo es válida para números de marcación fija."</string>
- <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"No se puede cambiar la configuración de desvío de llamadas desde tu teléfono mientras estás en itinerancia."</string>
+ <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"No se puede cambiar la configuración de desvío de llamadas desde tu teléfono mientras estás en roaming."</string>
<string name="serviceEnabled" msgid="7549025003394765639">"El servicio se ha habilitado."</string>
<string name="serviceEnabledFor" msgid="1463104778656711613">"Se ha habilitado el servicio para:"</string>
<string name="serviceDisabled" msgid="641878791205871379">"El servicio se ha inhabilitado."</string>
@@ -108,19 +108,19 @@
<string name="serviceClassDataSync" msgid="7895071363569133704">"Sincronización"</string>
<string name="serviceClassPacket" msgid="1430642951399303804">"Paquete"</string>
<string name="serviceClassPAD" msgid="6850244583416306321">"PAD"</string>
- <string name="roamingText0" msgid="7793257871609854208">"Indicador de itinerancia activado"</string>
- <string name="roamingText1" msgid="5073028598334616445">"Indicador de itinerancia desactivado"</string>
- <string name="roamingText2" msgid="2834048284153110598">"Indicador de itinerancia parpadeante"</string>
+ <string name="roamingText0" msgid="7793257871609854208">"Indicador de roaming activado"</string>
+ <string name="roamingText1" msgid="5073028598334616445">"Indicador de roaming desactivado"</string>
+ <string name="roamingText2" msgid="2834048284153110598">"Indicador de roaming parpadeante"</string>
<string name="roamingText3" msgid="831690234035748988">"Fuera del vecindario"</string>
<string name="roamingText4" msgid="2171252529065590728">"Fuera del edificio"</string>
- <string name="roamingText5" msgid="4294671587635796641">"Itinerancia: sistema preferido"</string>
- <string name="roamingText6" msgid="5536156746637992029">"Itinerancia: sistema disponible"</string>
- <string name="roamingText7" msgid="1783303085512907706">"Itinerancia: partner de alianza"</string>
- <string name="roamingText8" msgid="7774800704373721973">"Itinerancia: partner de gran calidad"</string>
- <string name="roamingText9" msgid="1933460020190244004">"Itinerancia: funcionalidad de servicio completa"</string>
- <string name="roamingText10" msgid="7434767033595769499">"Itinerancia: funcionalidad de servicio parcial"</string>
- <string name="roamingText11" msgid="5245687407203281407">"Banner de itinerancia activado"</string>
- <string name="roamingText12" msgid="673537506362152640">"Banner de itinerancia desactivado"</string>
+ <string name="roamingText5" msgid="4294671587635796641">"Roaming: sistema preferido"</string>
+ <string name="roamingText6" msgid="5536156746637992029">"Roaming: sistema disponible"</string>
+ <string name="roamingText7" msgid="1783303085512907706">"Roaming: partner de alianza"</string>
+ <string name="roamingText8" msgid="7774800704373721973">"Roaming: partner de gran calidad"</string>
+ <string name="roamingText9" msgid="1933460020190244004">"Roaming: funcionalidad de servicio completa"</string>
+ <string name="roamingText10" msgid="7434767033595769499">"Roaming: funcionalidad de servicio parcial"</string>
+ <string name="roamingText11" msgid="5245687407203281407">"Banner de roaming activado"</string>
+ <string name="roamingText12" msgid="673537506362152640">"Banner de roaming desactivado"</string>
<string name="roamingTextSearching" msgid="5323235489657753486">"Buscando servicio"</string>
<string name="wfcRegErrorTitle" msgid="3193072971584858020">"No se ha podido configurar la llamada por Wi‑Fi"</string>
<string-array name="wfcOperatorErrorAlertMessages">
@@ -412,13 +412,13 @@
<string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"Permite que la aplicación envíe emisiones que permanecen en el dispositivo una vez finalizadas. Si esta función se utiliza en exceso, podría ralentizar tu dispositivo Android TV o volverlo inestable al hacer que se ocupe demasiada memoria."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"Permite que la aplicación envíe emisiones que permanecen en el dispositivo una vez que la emisión ha finalizado. Un uso excesivo podría ralentizar el teléfono o volverlo inestable al hacer que use demasiada memoria."</string>
<string name="permlab_readContacts" msgid="8776395111787429099">"consultar tus contactos"</string>
- <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"Permite que la aplicación lea datos de los contactos almacenados en tu tablet. Las aplicaciones también podrán acceder a las cuentas de tu tablet que hayan creado contactos, y quizá tengan acceso a las cuentas creadas por aplicaciones que hayas descargado. Las aplicaciones con este permiso este permiso pueden guardar los datos de tus contactos, y las aplicaciones maliciosas puede que compartan estos datos sin que lo sepas."</string>
- <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"Permite que la aplicación lea datos de los contactos almacenados en tu dispositivo Android TV. Las aplicaciones también podrán acceder a las cuentas de tu dispositivo Android TV que hayan creado contactos, y quizá tengan acceso a las cuentas creadas por aplicaciones que hayas descargado. Las aplicaciones con este permiso este permiso pueden guardar los datos de tus contactos, y las aplicaciones maliciosas puede que compartan estos datos sin que lo sepas."</string>
- <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"Permite que la aplicación lea datos de los contactos almacenados en tu teléfono. Las aplicaciones también podrán acceder a las cuentas de tu teléfono que hayan creado contactos, y quizá tengan acceso a las cuentas creadas por aplicaciones que hayas descargado. Las aplicaciones con este permiso este permiso pueden guardar los datos de tus contactos, y las aplicaciones maliciosas puede que compartan estos datos sin que lo sepas."</string>
+ <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"Permite que la aplicación lea datos de los contactos almacenados en tu tablet. Las aplicaciones también podrán acceder a las cuentas de tu tablet que hayan creado contactos, y quizá tengan acceso a las cuentas creadas por aplicaciones que hayas descargado. Las aplicaciones con este permiso pueden guardar los datos de tus contactos, y las aplicaciones maliciosas puede que compartan estos datos sin que lo sepas."</string>
+ <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"Permite que la aplicación lea datos de los contactos almacenados en tu dispositivo Android TV. Las aplicaciones también podrán acceder a las cuentas de tu dispositivo Android TV que hayan creado contactos, y quizá tengan acceso a las cuentas creadas por aplicaciones que hayas descargado. Las aplicaciones con este permiso pueden guardar los datos de tus contactos, y las aplicaciones maliciosas puede que compartan estos datos sin que lo sepas."</string>
+ <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"Permite que la aplicación lea datos de los contactos almacenados en tu teléfono. Las aplicaciones también podrán acceder a las cuentas de tu teléfono que hayan creado contactos, y quizá tengan acceso a las cuentas creadas por aplicaciones que hayas descargado. Las aplicaciones con este permiso pueden guardar los datos de tus contactos, y las aplicaciones maliciosas puede que compartan estos datos sin que lo sepas."</string>
<string name="permlab_writeContacts" msgid="8919430536404830430">"modificar tus contactos"</string>
- <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"Permite que la aplicación cambie los datos de los contactos almacenados en tu tablet. Las aplicaciones con este permiso este permiso pueden eliminar datos de contactos."</string>
- <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"Permite que la aplicación cambie los datos de los contactos almacenados en tu dispositivo Android TV. Las aplicaciones con este permiso este permiso pueden eliminar datos de contactos."</string>
- <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"Permite que la aplicación cambie los datos de los contactos almacenados en tu teléfono. Las aplicaciones con este permiso este permiso pueden eliminar datos de contactos."</string>
+ <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"Permite que la aplicación cambie los datos de los contactos almacenados en tu tablet. Las aplicaciones con este permiso pueden eliminar datos de contactos."</string>
+ <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"Permite que la aplicación cambie los datos de los contactos almacenados en tu dispositivo Android TV. Las aplicaciones con este permiso pueden eliminar datos de contactos."</string>
+ <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"Permite que la aplicación cambie los datos de los contactos almacenados en tu teléfono. Las aplicaciones con este permiso pueden eliminar datos de contactos."</string>
<string name="permlab_readCallLog" msgid="1739990210293505948">"leer el registro de llamadas"</string>
<string name="permdesc_readCallLog" msgid="8964770895425873433">"Esta aplicación puede leer tu historial de llamadas."</string>
<string name="permlab_writeCallLog" msgid="670292975137658895">"escribir en el registro de llamadas"</string>
@@ -521,25 +521,25 @@
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Permite que la aplicación reciba paquetes enviados a través de una red Wi-Fi y mediante direcciones de multidifusión a todos los dispositivos, no solo a tu dispositivo Android TV. Consume más batería que el modo sin multidifusión."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Permite que la aplicación reciba paquetes enviados a todos los dispositivos de una red Wi-Fi que utilicen direcciones de multidifusión, no solo al teléfono. Utiliza más batería que el modo de no multidifusión."</string>
<string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"acceder a los ajustes de Bluetooth"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Permite que la aplicación configure el tablet Bluetooth local y que detecte dispositivos remotos y se vincule con ellos."</string>
- <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Permite que la aplicación configure el Bluetooth en tu dispositivo Android TV, detecte dispositivos remotos y se vincule con ellos."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Permite que la aplicación configure el teléfono Bluetooth local y que detecte dispositivos remotos y se vincule con ellos."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Permite que la aplicación configure el tablet Bluetooth local y que detecte dispositivos remotos y se empareje con ellos."</string>
+ <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Permite que la aplicación configure el Bluetooth en tu dispositivo Android TV, detecte dispositivos remotos y se empareje con ellos."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Permite que la aplicación configure el teléfono Bluetooth local y que detecte dispositivos remotos y se empareje con ellos."</string>
<string name="permlab_accessWimaxState" msgid="7029563339012437434">"conectarse a WiMAX y desconectarse de esta red"</string>
<string name="permdesc_accessWimaxState" msgid="5372734776802067708">"Permite que la aplicación determine si está habilitada la conexión WiMAX y obtenga información sobre las redes WiMAX que están conectadas."</string>
<string name="permlab_changeWimaxState" msgid="6223305780806267462">"cambiar estado de WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"Permite que la aplicación conecte el tablet a redes WiMAX y lo desconecte de ellas."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"Permite que la aplicación active y desactive la conexión entre tu dispositivo Android TV y las redes WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"Permite que la aplicación conecte el teléfono a redes WiMAX y lo desconecte de ellas."</string>
- <string name="permlab_bluetooth" msgid="586333280736937209">"vincular con dispositivos Bluetooth"</string>
+ <string name="permlab_bluetooth" msgid="586333280736937209">"emparejar con dispositivos Bluetooth"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Permite que la aplicación acceda a la configuración de Bluetooth del tablet y que establezca y acepte conexiones con los dispositivos sincronizados."</string>
<string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Permite que la aplicación vea la configuración de Bluetooth de tu dispositivo Android TV y que cree y acepte conexiones con los dispositivos vinculados."</string>
<string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Permite que la aplicación acceda a la configuración de Bluetooth del teléfono y que establezca y acepte conexiones con los dispositivos sincronizados."</string>
- <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"detectar y vincular dispositivos Bluetooth cercanos"</string>
- <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Permite que la aplicación detecte y vincule dispositivos Bluetooth cercanos"</string>
+ <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"detectar y emparejar dispositivos Bluetooth cercanos"</string>
+ <string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Permite que la aplicación detecte y empareje dispositivos Bluetooth cercanos"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"conectarse a dispositivos Bluetooth vinculados"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Permite que la aplicación se conecte a dispositivos Bluetooth vinculados"</string>
- <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"mostrar anuncios a dispositivos Bluetooth cercanos"</string>
- <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite que la aplicación muestre anuncios a dispositivos Bluetooth cercanos"</string>
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"emitir a dispositivos Bluetooth cercanos"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite que la aplicación emita a dispositivos Bluetooth cercanos"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"calcular posición de dispositivos de banda ultraancha cercanos"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite que la aplicación determine la posición relativa de los dispositivos de banda ultraancha cercanos"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Información sobre el servicio de pago por NFC preferido"</string>
@@ -1335,7 +1335,7 @@
<string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"No permitir nunca"</string>
<string name="sim_removed_title" msgid="5387212933992546283">"Tarjeta SIM retirada"</string>
<string name="sim_removed_message" msgid="9051174064474904617">"La red móvil no estará disponible hasta que reinicies el dispositivo con una tarjeta SIM válida."</string>
- <string name="sim_done_button" msgid="6464250841528410598">"Listo"</string>
+ <string name="sim_done_button" msgid="6464250841528410598">"Hecho"</string>
<string name="sim_added_title" msgid="7930779986759414595">"Tarjeta SIM añadida"</string>
<string name="sim_added_message" msgid="6602906609509958680">"Reinicia el dispositivo para acceder a la red móvil."</string>
<string name="sim_restart_button" msgid="8481803851341190038">"Reiniciar"</string>
@@ -1348,7 +1348,7 @@
<string name="time_picker_dialog_title" msgid="9053376764985220821">"Establecer hora"</string>
<string name="date_picker_dialog_title" msgid="5030520449243071926">"Establecer fecha"</string>
<string name="date_time_set" msgid="4603445265164486816">"Establecer"</string>
- <string name="date_time_done" msgid="8363155889402873463">"Listo"</string>
+ <string name="date_time_done" msgid="8363155889402873463">"Hecho"</string>
<string name="perms_new_perm_prefix" msgid="6984556020395757087"><font size="12" fgcolor="#ff33b5e5">"NUEVO:"</font></string>
<string name="perms_description_app" msgid="2747752389870161996">"Proporcionado por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="no_permissions" msgid="5729199278862516390">"No es necesario ningún permiso"</string>
@@ -1366,7 +1366,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Se ha detectado un accesorio de audio analógico"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"El dispositivo adjunto no es compatible con este teléfono. Toca para obtener más información."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"Depuración USB habilitada"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"Toca aquí para desactivar la depuración USB"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"Toca para desactivar la depuración USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Seleccionar para inhabilitar la depuración USB"</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Depuración inalámbrica conectada"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Toca para desactivar la depuración inalámbrica"</string>
@@ -1438,7 +1438,7 @@
<string name="ext_media_status_removed" msgid="241223931135751691">"Extraído"</string>
<string name="ext_media_status_unmounted" msgid="8145812017295835941">"Expulsado"</string>
<string name="ext_media_status_checking" msgid="159013362442090347">"Comprobando..."</string>
- <string name="ext_media_status_mounted" msgid="3459448555811203459">"Listo"</string>
+ <string name="ext_media_status_mounted" msgid="3459448555811203459">"Hecho"</string>
<string name="ext_media_status_mounted_ro" msgid="1974809199760086956">"Solo lectura"</string>
<string name="ext_media_status_bad_removal" msgid="508448566481406245">"Extraído de forma no segura"</string>
<string name="ext_media_status_unmountable" msgid="7043574843541087748">"Dañado"</string>
@@ -1463,7 +1463,7 @@
<string name="ime_action_search" msgid="4501435960587287668">"Buscar"</string>
<string name="ime_action_send" msgid="8456843745664334138">"Enviar"</string>
<string name="ime_action_next" msgid="4169702997635728543">"Siguiente"</string>
- <string name="ime_action_done" msgid="6299921014822891569">"Listo"</string>
+ <string name="ime_action_done" msgid="6299921014822891569">"Hecho"</string>
<string name="ime_action_previous" msgid="6548799326860401611">"Anterior"</string>
<string name="ime_action_default" msgid="8265027027659800121">"Ejecutar"</string>
<string name="dial_number_using" msgid="6060769078933953531">"Marcar número\ncon <xliff:g id="NUMBER">%s</xliff:g>"</string>
@@ -1510,7 +1510,7 @@
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> de <xliff:g id="TOTAL">%d</xliff:g></item>
<item quantity="one">1 coincidencia</item>
</plurals>
- <string name="action_mode_done" msgid="2536182504764803222">"Listo"</string>
+ <string name="action_mode_done" msgid="2536182504764803222">"Hecho"</string>
<string name="progress_erasing" msgid="6891435992721028004">"Borrando almacenamiento compartido…"</string>
<string name="share" msgid="4157615043345227321">"Compartir"</string>
<string name="find" msgid="5015737188624767706">"Buscar"</string>
@@ -1551,7 +1551,7 @@
<string name="keyboardview_keycode_alt" msgid="8997420058584292385">"Alt"</string>
<string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"Cancelar"</string>
<string name="keyboardview_keycode_delete" msgid="2661117313730098650">"Eliminar"</string>
- <string name="keyboardview_keycode_done" msgid="2524518019001653851">"Listo"</string>
+ <string name="keyboardview_keycode_done" msgid="2524518019001653851">"Hecho"</string>
<string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"Cambio de modo"</string>
<string name="keyboardview_keycode_shift" msgid="3026509237043975573">"Mayús"</string>
<string name="keyboardview_keycode_enter" msgid="168054869339091055">"Intro"</string>
@@ -1704,7 +1704,7 @@
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Selecciona qué funciones usar con la tecla de volumen"</string>
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Se ha desactivado <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar accesos directos"</string>
- <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Listo"</string>
+ <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Hecho"</string>
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Desactivar acceso directo"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar acceso directo"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de color"</string>
@@ -1834,7 +1834,7 @@
<string name="immersive_cling_title" msgid="2307034298721541791">"Modo de pantalla completa"</string>
<string name="immersive_cling_description" msgid="7092737175345204832">"Para salir, desliza el dedo de arriba abajo."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Entendido"</string>
- <string name="done_label" msgid="7283767013231718521">"Listo"</string>
+ <string name="done_label" msgid="7283767013231718521">"Hecho"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Control deslizante circular de horas"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Control deslizante circular de minutos"</string>
<string name="select_hours" msgid="5982889657313147347">"Seleccionar horas"</string>
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales y funciones como \"Hey Google\"\n\n"<annotation id="url">"Más información"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales y funciones como \"Hey Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"El modo Ahorro de datos evita que algunas aplicaciones envíen o reciban datos en segundo plano, lo que puede reducir el uso de datos. Una aplicación activa puede acceder a los datos, aunque con menos frecuencia. Esto significa que es posible que, por ejemplo, algunas imágenes no se muestren hasta que las toques."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"¿Activar Ahorro de datos?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Cerrar"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Responder"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Rechazar"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Colgar"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Llamada entrante"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"La importancia de esta notificación ha disminuido a Silencio. Toca para enviar comentarios."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Esta notificación aparecerá en una posición más alta. Toca para enviar comentarios."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Esta notificación aparecerá en una posición más baja. Toca para enviar comentarios."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Probar notificaciones mejoradas"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Para seguir recibiendo sugerencias de acciones, respuestas y más, activa las notificaciones mejoradas. Las notificaciones adaptativas de Android ya no están disponibles."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Activar"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Ahora no"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Más información"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Las notificaciones mejoradas pueden leer todo el contenido de las notificaciones, incluidas las relacionadas con información personal, como nombres y mensajes de tus contactos. Esta función también puede cerrar notificaciones o utilizar los botones de las notificaciones, por ejemplo, para responder llamadas telefónicas.\n\nAdemás, puede activar o desactivar el modo Prioridad y cambiar ajustes relacionados con él."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificación sobre el modo rutina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Quizás se agote la batería antes de lo habitual"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Se ha activado el modo Ahorro de batería para aumentar la duración de la batería"</string>
@@ -2275,7 +2269,7 @@
<string name="dismiss_action" msgid="1728820550388704784">"Cerrar"</string>
<string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Desbloquea el micrófono del dispositivo"</string>
<string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Desbloquea la cámara del dispositivo"</string>
- <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Para que <b><xliff:g id="APP">%s</xliff:g></b> y todos los servicios y las aplicaciones puedan acceder"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Para <b><xliff:g id="APP">%s</xliff:g></b> y todos los servicios y las aplicaciones"</string>
<string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Desbloquear"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacidad del sensor"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Icono de aplicación"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 9672e84..1e58aed 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Akusäästja lülitab sisse tumeda teema ning piirab taustategevusi, teatud visuaalseid efekte ja funktsioone, nagu „Ok Google“ (või lülitab need välja)\n\n"<annotation id="url">"Lisateave"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Akusäästja lülitab sisse tumeda teema ning piirab taustategevusi, teatud visuaalseid efekte ja funktsioone, nagu „Ok Google“ (või lülitab need välja)."</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>
@@ -1896,7 +1894,7 @@
<string name="zen_mode_alarm" msgid="7046911727540499275">"Kuni <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (järgmine äratus)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Kuni välja lülitate"</string>
<string name="zen_mode_forever_dnd" msgid="3423201955704180067">"Kuni lülitate välja valiku Mitte segada"</string>
- <string name="zen_mode_rule_name_combination" msgid="7174598364351313725">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
+ <string name="zen_mode_rule_name_combination" msgid="7174598364351313725">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
<string name="toolbar_collapse_description" msgid="8009920446193610996">"Ahendamine"</string>
<string name="zen_mode_feature_name" msgid="3785547207263754500">"Mitte segada"</string>
<string name="zen_mode_downtime_feature_name" msgid="5886005761431427128">"Puhkeaeg"</string>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Sule"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Vasta"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Keeldu"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Lõpeta kõne"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Sissetulev kõne"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Sellele märguandele määrati prioriteet Vaikne. Puudutage tagasiside andmiseks."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Sellele märguandele määrati kõrgem prioriteet. Puudutage tagasiside andmiseks."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Sellele märguandele määrati madalam prioriteet. Puudutage tagasiside andmiseks."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Proovige täiustatud märguandeid"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Soovitatud toimingute, vastuste ja muu nägemiseks ka edaspidi lülitage sisse täiustatud märguanded. Androidi kohanduvaid märguandeid enam ei toetata."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Lülita sisse"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Mitte praegu"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Lisateave"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Täiustatud märguanded saavad lugeda kogu märguande sisu, sh isiklikku teavet, nagu kontaktide nimed ja sõnumid. Samuti saab selle funktsiooniga märguannetest loobuda või märguannetes nuppude abil toiminguid teha (nt vastata telefonikõnedele).\n\nLisaks saab selle funktsiooniga sisse või välja lülitada režiimi Prioriteetne ja muuta seotud seadeid."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Rutiinirežiimi teabe märguanne"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Aku võib enne tavapärast laadimist tühjaks saada"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Akusäästja aktiveeriti aku tööea pikendamiseks"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 08c4c06..3d2eda9 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -315,7 +315,7 @@
<string name="permgroupdesc_storage" msgid="6351503740613026600">"atzitu gailuko argazkiak, multimedia-edukia eta fitxategiak"</string>
<string name="permgrouplab_microphone" msgid="2480597427667420076">"Mikrofonoa"</string>
<string name="permgroupdesc_microphone" msgid="1047786732792487722">"grabatu audioa"</string>
- <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Jarduera fisiko"</string>
+ <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Jarduera fisikoa"</string>
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"jarduera fisikoa atzitu"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"atera argazkiak eta grabatu bideoak"</string>
@@ -362,11 +362,11 @@
<string name="permlab_receiveMms" msgid="4000650116674380275">"jaso testu-mezuak (MMSak)"</string>
<string name="permdesc_receiveMms" msgid="958102423732219710">"MMS mezuak jasotzeko eta prozesatzeko baimena ematen die aplikazioei. Horrela, aplikazioak gailura bidalitako mezuak kontrola eta ezaba ditzake zuri erakutsi gabe."</string>
<string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"desbideratu sare mugikor bidezko igorpen-mezuak"</string>
- <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Sare mugikor bidezko igorpen-modulura lotzeko baimena ematen die aplikazioei, sare mugikor bidezko igorpen-mezuak jaso ahala desbideratu ahal izateko. Sare mugikor bidezko igorpen-alertak kokapen batzuetan entregatzen dira larrialdi-egoeren berri emateko. Sare mugikor bidezko larrialdi-igorpenak jasotzean, asmo txarreko aplikazioek gailuaren errendimenduari edota funtzionamenduari eragin diezaiokete."</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Sare mugikor bidezko iragarpen-modulura lotzeko baimena ematen die aplikazioei, sare mugikor bidezko iragarpen-mezuak jaso ahala desbideratu ahal izateko. Sare mugikor bidezko iragarpen-alertak kokapen batzuetan entregatzen dira larrialdi-egoeren berri emateko. Sare mugikor bidezko larrialdi-iragarpenak jasotzean, asmo txarreko aplikazioek gailuaren errendimenduari edota funtzionamenduari eragin diezaiokete."</string>
<string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Kudeatu abian dauden deiak"</string>
<string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Gailuak jasotzen dituen deiei buruzko xehetasunak ikusteko eta dei horiek kontrolatzeko baimena ematen die aplikazioei."</string>
<string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"irakurri sare mugikor bidezko igorpen-mezuak"</string>
- <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Gailuak jasotako sare mugikor bidezko igorpenen mezuak irakurtzeko baimena ematen die aplikazioei. Sare mugikor bidezko igorpen-alertak kokapen batzuetan ematen dira larrialdi-egoeren berri emateko. Asmo txarreko aplikazioek gailuaren errendimendua edo funtzionamendua oztopa dezakete larrialdi-igorpen horietako bat jasotzen denean."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Gailuak jasotako sare mugikor bidezko igorpen-mezuak irakurtzeko baimena ematen die aplikazioei. Sare mugikor bidezko igorpen-alertak kokapen batzuetan ematen dira larrialdi-egoeren berri emateko. Asmo txarreko aplikazioek gailuaren errendimendua edo funtzionamendua oztopa dezakete larrialdi-igorpen horietako bat jasotzen denean."</string>
<string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"irakurri harpidetutako jarioak"</string>
<string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Une horretan sinkronizatutako jarioei buruzko xehetasunak lortzeko baimena ematen die aplikazioei."</string>
<string name="permlab_sendSms" msgid="7757368721742014252">"bidali eta ikusi SMS mezuak"</string>
@@ -451,8 +451,8 @@
<string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Aplikazioak edonoiz erabil dezake mikrofonoa audioa grabatzeko."</string>
<string name="permlab_sim_communication" msgid="176788115994050692">"bidali aginduak SIM txartelera"</string>
<string name="permdesc_sim_communication" msgid="4179799296415957960">"SIM txartelera aginduak bidaltzeko aukera ematen die aplikazioei. Oso arriskutsua da."</string>
- <string name="permlab_activityRecognition" msgid="1782303296053990884">"hauteman jarduera fisiko"</string>
- <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Aplikazioak jarduera fisiko hauteman dezake."</string>
+ <string name="permlab_activityRecognition" msgid="1782303296053990884">"hauteman jarduera fisikoa"</string>
+ <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Aplikazioak jarduera fisikoa hauteman dezake."</string>
<string name="permlab_camera" msgid="6320282492904119413">"atera argazkiak eta grabatu bideoak"</string>
<string name="permdesc_camera" msgid="5240801376168647151">"Aplikazioak abian den bitartean erabil dezake kamera argazkiak ateratzeko eta bideoak grabatzeko."</string>
<string name="permlab_backgroundCamera" msgid="7549917926079731681">"Argazkiak atera eta bideoak grabatu atzeko planoan."</string>
@@ -538,10 +538,10 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Inguruko Bluetooth bidezko gailuak hautemateko eta haiekin parekatzeko baimena ematen die aplikazioei"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"parekatutako Bluetooth bidezko gailuetara konektatu"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Parekatutako Bluetooth bidezko gailuetara konektatzeko baimena ematen die aplikazioei"</string>
- <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"inguruko Bluetooth bidezko gailuei publizitatea erakutsi"</string>
- <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Inguruko Bluetooth bidezko gailuei publizitatea erakusteko baimena ematen die aplikazioei"</string>
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"inguruko Bluetooth bidezko gailuetan informazioa iragarri"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Inguruko Bluetooth bidezko gailuetan informazioa iragartzeko baimena ematen die aplikazioei"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"banda ultrazabala darabilten inguruko gailuen arteko distantzia erlatiboa zehaztu"</string>
- <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Banda ultrazabala darabilten inguruko gailuen arteko distantzia erlatiboa zehazteko baimena ematen die aplikazioei"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Banda ultrazabala darabilten inguruko gailuen arteko distantzia erlatiboa zehazteko baimena ematen dio aplikazioari"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"NFC bidezko ordainketa-zerbitzu lehenetsiari buruzko informazioa"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Aplikazioari baimena ematen dio NFC bidezko ordainketa-zerbitzu lehenetsiari buruzko informazioa jasotzeko, hala nola erregistratutako laguntzaileak eta ibilbidearen helmuga."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kontrolatu Near Field Communication komunikazioa"</string>
@@ -1852,11 +1852,9 @@
<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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
- <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 aplikazioak atzitu egin ahal izango ditu datuak, baina baliteke maiztasun txikiagoarekin atzitzea. Horrela, adibidez, baliteke irudiak ez erakustea haiek sakatu arte."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Bateria-aurrezleak gai iluna aktibatzen du, eta murriztu edo desaktibatu egiten ditu atzeko planoko jarduerak, zenbait efektu bisual eta beste eginbide batzuk, hala nola \"Ok Google\".\n\n"<annotation id="url">"Lortu informazio gehiago"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Bateria-aurrezleak gai iluna aktibatzen du, eta murriztu edo desaktibatu egiten ditu atzeko planoko jarduerak, zenbait efektu bisual eta beste eginbide batzuk, hala nola \"Ok Google\"."</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>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="2877101784123058273">
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Itxi"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Erantzun"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Baztertu"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Amaitu deia"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Jasotako deia"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Isilarazi da jakinarazpena. Sakatu hau oharrak bidaltzeko."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Mailaz igo da jakinarazpena. Sakatu hau oharrak bidaltzeko."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Mailaz jaitsi da jakinarazpena. Sakatu hau oharrak bidaltzeko."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Probatu jakinarazpen hobetuak"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Iradokitako ekintzak, erantzunak eta abar jasotzen jarraitzeko, aktibatu jakinarazpen hobetuak. Android-en jakinarazpen egokituak ez dira onartzen jada."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Aktibatu"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Orain ez"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Lortu informazio gehiago"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Jakinarazpen hobetuek jakinarazpenen eduki osoa irakur dezakete, informazio pertsonala barne (esaterako, kontaktuen izenak eta mezuak). Halaber, jakinarazpenak baztertu edo jakinarazpenetako botoiak erabil ditzake eginbideak; adibidez, telefono-deiak erantzun.\n\nHorretaz gain, lehentasunezko modua aktibatu edo desaktiba dezake, eta erlazionatutako ezarpenak aldatu."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Ohitura moduaren informazio-jakinarazpena"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Baliteke bateria ohi baino lehenago agortzea"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Bateria-aurrezlea aktibatuta dago bateriaren iraupena luzatzeko"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index fc9162a..ae3f79a 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -84,7 +84,7 @@
<string name="RestrictedStateContent" msgid="7693575344608618926">"شرکت مخابراتی شما موقتاً آن را خاموش کرده است"</string>
<string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"شرکت مخابراتیتان موقتاً آن را برای سیمکارت <xliff:g id="SIMNUMBER">%d</xliff:g> خاموش کرده است"</string>
<string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"شبکه تلفن همراه دردسترس نیست"</string>
- <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"تغییر شبکه برگزیده را امتحان کنید. برای تغییر ضربه بزنید."</string>
+ <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"تغییر شبکه ترجیحی را امتحان کنید. برای تغییر، ضربه بزنید."</string>
<string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"تماس اضطراری امکانپذیر نیست"</string>
<string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"تماس اضطراری ازطریق Wi‑Fi امکانپذیر نیست"</string>
<string name="notification_channel_network_alert" msgid="4788053066033851841">"هشدارها"</string>
@@ -113,7 +113,7 @@
<string name="roamingText2" msgid="2834048284153110598">"نشانگر چشمک زن فراگردی"</string>
<string name="roamingText3" msgid="831690234035748988">"خارج از محله"</string>
<string name="roamingText4" msgid="2171252529065590728">"خارج از ساختمان"</string>
- <string name="roamingText5" msgid="4294671587635796641">"فراگردی - سیستم برگزیده"</string>
+ <string name="roamingText5" msgid="4294671587635796641">"فراگردی - سیستم ترجیحی"</string>
<string name="roamingText6" msgid="5536156746637992029">"فراگردی - سیستم موجود"</string>
<string name="roamingText7" msgid="1783303085512907706">"فراگردی - شریک"</string>
<string name="roamingText8" msgid="7774800704373721973">"فراگردی - شریک ویژه"</string>
@@ -1322,7 +1322,7 @@
<string name="select_character" msgid="3352797107930786979">"درج نویسه"</string>
<string name="sms_control_title" msgid="4748684259903148341">"درحال ارسال پیامکها"</string>
<string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> درحال ارسال تعداد زیادی پیامک است. آیا اجازه میدهید این برنامه همچنان پیامک ارسال کند؟"</string>
- <string name="sms_control_yes" msgid="4858845109269524622">"مجاز است"</string>
+ <string name="sms_control_yes" msgid="4858845109269524622">"مجاز بودن"</string>
<string name="sms_control_no" msgid="4845717880040355570">"مجاز نبودن"</string>
<string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> مایل است پیامی به <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b> ارسال کند."</string>
<string name="sms_short_code_details" msgid="2723725738333388351">"این مورد "<b>"شاید هزینهای"</b>" را به حساب دستگاه همراهتان بگذارد."</string>
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"«بهینهسازی باتری» «طرح زمینه تیره» را روشن میکند و فعالیت پسزمینه، برخی از جلوههای بصری، و ویژگیهایی مثل «Ok Google» را محدود یا خاموش میکند.\n\n"<annotation id="url">"بیشتر بدانید"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"«بهینهسازی باتری» «طرح زمینه تیره» را روشن میکند و فعالیت پسزمینه، برخی از جلوههای بصری، و ویژگیهایی مثل «Ok Google» را محدود یا خاموش میکند."</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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"بستن"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"پاسخ"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"رد کردن"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"قطع تماس"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"تماس ورودی"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"این اعلان به «بیصدا» تنزل داده شد. برای ارائه بازخورد، ضربه بزنید."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"این اعلان در رتبه بالاتری قرار گرفت. برای ارائه بازخورد، ضربه بزنید."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"این اعلان در رتبه پایینتری قرار گرفت. برای ارائه بازخورد، ضربه بزنید."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"امتحان کردن اعلانهای بهبودیافته"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"برای اینکه پاسخها و کنشهای پیشنهادی و موارد دیگر را همچنان دریافت کنید، اعلانهای بهبودیافته را روشن کنید. از «اعلانهای تطبیقی Android» دیگر پشتیبانی نمیشود."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"روشن کردن"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"الآن نه"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"بیشتر بدانید"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"اعلانهای بهبودیافته میتواند محتوای همه اعلانها، ازجمله اطلاعات شخصی مثل نام مخاطبین و پیامها را بخواند. این ویژگی همچنین میتواند اعلانها را ببندد یا بااستفاده از دکمههای موجود در اعلانها اقداماتی انجام دهد، مثلاً به تماسهای تلفنی پاسخ دهد.\n\nبهعلاوه، این ویژگی میتواند «حالت اولویت» را روشن یا خاموش کند و تنظیمات مربوطه را تغییر دهد."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"اعلان اطلاعات حالت روال معمول"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ممکن است شارژ باتری قبل از شارژ معمول تمام شود"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"جهت افزایش عمر باتری، «بهینهسازی باتری» فعال شد"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 4ff7593..7e6cb56 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Sallii sovelluksen löytää lähellä olevia Bluetooth-laitteita ja muodostaa niistä laitepareja"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"yhdistää pariliitettyihin Bluetooth-laitteisiin"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Sallii sovelluksen muodostaa yhteyden pariliitettyihin Bluetooth-laitteisiin"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"mainostaa lähellä oleville Bluetooth-laitteille"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Sallii sovelluksen mainostaa lähellä oleville Bluetooth-laitteille"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"määrittää UVB:ta käyttävien laitteiden suhteellisen sijainnin"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Sallii sovelluksen määrittää UVB-taajuutta käyttävien laitteiden sijainnin suhteessa toisiinsa"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Ensisijaiset NFC-maksupalvelutiedot"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Virransäästö laittaa tumman teeman päälle ja rajoittaa tai laittaa pois päältä taustatoimintoja, joitakin visuaalisia tehosteita ja muita ominaisuuksia (esim. Ok Google).\n\n"<annotation id="url">"Lue lisää"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Virransäästö laittaa tumman teeman päälle ja rajoittaa tai laittaa pois päältä taustatoimintoja, joitakin visuaalisia tehosteita ja muita ominaisuuksia (esim. Ok Google)."</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>
@@ -1933,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Sulje"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Vastaa"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Hylkää"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Lopeta puhelu"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Saapuva puhelu"</string>
@@ -2076,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Tämä ilmoitus hiljennettiin. Lähetä palautetta napauttamalla."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Tämän ilmoituksen tasoa nostettiin. Lähetä palautetta napauttamalla."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Tämän ilmoituksen tasoa laskettiin. Lähetä palautetta napauttamalla."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Kokeile parann. ilmoituksia"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Jos haluat jatkossakin saada ehdotuksia toiminnoista, vastauksista ja muista, laita parannetut ilmoitukset päälle. Androidin mukautuvia ilmoituksia ei enää tueta."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Laita päälle"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Ei nyt"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Lue lisää"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Parannetut ilmoitukset voivat lukea kaikkea ilmoitussisältöä, myös henkilökohtaisia tietoja (esim. kontaktien nimet ja viestit). Ominaisuus voi myös ohittaa ilmoituksia tai käyttää niiden toimintopainikkeita, esim. vastata puheluihin.\n\nLisäksi ominaisuus voi laittaa Tärkeät-tilan päälle tai pois päältä ja muuttaa siihen liittyviä asetuksia."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Ohjelmatilan tietoilmoitus"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Akku saattaa loppua ennen normaalia latausaikaa"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Virransäästö otettu käyttöön akunkeston pidentämiseksi"</string>
@@ -2275,14 +2267,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Voit nyt suurentaa näytön osan"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Laita päälle asetuksista"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Hylkää"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Kumoa laitteen mikrofonin esto"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Kumoa laitteen kameran esto"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> sekä kaikki sovellukset ja palvelut"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Kumoa esto"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Anturin tietosuoja"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Sovelluskuvake"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Sovelluksen tuotemerkkikuva"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 20365d0..3d22748 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"La fonctionnalité Économiseur de pile active le mode sombre et limite ou désactive l\'activité en arrière-plan, certains effets visuels et certaines fonctionnalités, comme « Ok Google »\n\n"<annotation id="url">"En savoir plus"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"La fonctionnalité Économiseur de pile active le mode sombre et limite ou désactive l\'activité en arrière-plan, certains effets visuels et certaines fonctionnalités, comme « Ok Google »."</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>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Fermer"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g> : <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Répondre"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Vidéo"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Refuser"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Raccrocher"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Appel entrant"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Cette notification a été rétrogradée à Silencieuse. Touchez pour envoyer des commentaires."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Cette notification a été élevée d\'un niveau. Touchez pour envoyer des commentaires."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Cette notification a été abaissée d\'un niveau. Touchez pour envoyer des commentaires."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Essayer les notif. améliorées"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Pour continuer de recevoir des suggestions d\'actions, de réponses et plus encore, activez les notifications améliorées. Les notifications adaptatives Android ne sont plus prises en charge."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Activer"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Plus tard"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"En savoir plus"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Les notifications améliorées peuvent lire le contenu de toutes les notifications, y compris les renseignements personnels comme le nom des contacts et les messages. Cette fonctionnalité peut aussi fermer des notifications ou effectuer des actions sur les boutons dans les notifications, comme répondre aux appels entrants.\n\nElle peut aussi activer et désactiver le mode Prioritaire et modifier les paramètres connexes."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notification d\'information du mode Routine"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"La pile pourrait s\'épuiser avant la charge habituelle"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Le mode Économiseur de pile est activé afin de prolonger l\'autonomie"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 7afe476..5f762ff 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -196,7 +196,7 @@
<string name="network_logging_notification_text" msgid="1327373071132562512">"Votre organisation gère cet appareil et peut surveiller le trafic réseau. Appuyez ici pour obtenir plus d\'informations."</string>
<string name="location_changed_notification_title" msgid="3620158742816699316">"Des application peuvent accéder à votre position"</string>
<string name="location_changed_notification_text" msgid="7158423339982706912">"Contactez votre administrateur pour en savoir plus"</string>
- <string name="geofencing_service" msgid="3826902410740315456">"Service de gardiennage virtuel"</string>
+ <string name="geofencing_service" msgid="3826902410740315456">"Service de géorepérage"</string>
<string name="country_detector" msgid="7023275114706088854">"Détecteur de pays"</string>
<string name="location_service" msgid="2439187616018455546">"Service de localisation"</string>
<string name="gnss_service" msgid="8907781262179951385">"Service GNSS"</string>
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Autorise l\'appli à détecter et à associer les appareils Bluetooth à proximité"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"se connecter aux appareils Bluetooth associés"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Autorise l\'appli à se connecter à des appareils Bluetooth associés"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"diffuser sur les appareils Bluetooth à proximité"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permet à l\'appli de diffuser du contenu sur les appareils Bluetooth à proximité"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"déterminer position relative entre appareils ultra-wideband à proximité"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Autoriser l\'appli à déterminer la position relative entre des appareils ultra-wideband à proximité"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informations sur le service de paiement NFC préféré"</string>
@@ -1106,8 +1104,8 @@
<item quantity="other">dans <xliff:g id="COUNT_1">%d</xliff:g> a</item>
</plurals>
<plurals name="duration_minutes_relative" formatted="false" msgid="6569851308583028344">
- <item quantity="one">il y a <xliff:g id="COUNT_1">%d</xliff:g> minute</item>
- <item quantity="other">il y a <xliff:g id="COUNT_1">%d</xliff:g> minutes</item>
+ <item quantity="one">Il y a <xliff:g id="COUNT_1">%d</xliff:g> minute</item>
+ <item quantity="other">Il y a <xliff:g id="COUNT_1">%d</xliff:g> minutes</item>
</plurals>
<plurals name="duration_hours_relative" formatted="false" msgid="420434788589102019">
<item quantity="one">il y a <xliff:g id="COUNT_1">%d</xliff:g> heure</item>
@@ -1692,13 +1690,13 @@
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne pas activer"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"ACTIVÉE"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"DÉSACTIVÉE"</string>
- <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Accorder le contrôle total de votre appareil au service <xliff:g id="SERVICE">%1$s</xliff:g> ?"</string>
+ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Accorder le contrôle total de votre appareil à <xliff:g id="SERVICE">%1$s</xliff:g> ?"</string>
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"Si vous activez <xliff:g id="SERVICE">%1$s</xliff:g>, votre appareil n\'utilisera pas le verrouillage de l\'écran pour améliorer le chiffrement des données."</string>
- <string name="accessibility_service_warning_description" msgid="291674995220940133">"Le contrôle total convient aux applications qui répondent à vos besoins d\'accessibilité. Il ne convient pas à la plupart des applications."</string>
+ <string name="accessibility_service_warning_description" msgid="291674995220940133">"Le contrôle total convient aux applications qui répondent à vos besoins d\'accessibilité. Il ne convient pas à la plupart des autres applications."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Afficher et contrôler l\'écran"</string>
- <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Cette fonctionnalité peut lire l\'intégralité du contenu à l\'écran et afficher du contenu par-dessus d\'autres applications."</string>
+ <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Le service peut lire l\'intégralité du contenu à l\'écran et afficher du contenu par-dessus d\'autres applications."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Afficher et effectuer des actions"</string>
- <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Cette fonctionnalité peut effectuer le suivi de vos interactions avec une application ou un capteur matériel, et interagir avec les applications en votre nom."</string>
+ <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Le service peut suivre vos interactions avec une application ou un capteur matériel, et interagir avec des applications de votre part."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Autoriser"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Refuser"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Appuyez sur une fonctionnalité pour commencer à l\'utiliser :"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"L\'économiseur de batterie active le thème sombre et limite ou désactive les activités en arrière-plan, certains effets visuels et d\'autres fonctionnalités comme \"Hey Google\"\n\n"<annotation id="url">"En savoir plus"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"L\'économiseur de batterie active le thème sombre et limite ou désactive les activités en arrière-plan, certains effets visuels et d\'autres fonctionnalités comme \"Hey Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Pour réduire la consommation de données, l\'économiseur de données empêche certaines applications d\'envoyer ou de recevoir des données en arrière-plan. Ainsi, les applications que vous utilisez peuvent toujours accéder aux données, mais pas en permanence. Par exemple, il se peut que les images ne s\'affichent pas tant que vous n\'appuyez 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>
@@ -1933,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Fermer"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g> : <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Répondre"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Refuser"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Raccrocher"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Appel entrant"</string>
@@ -2076,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Cette notification a été abaissée à la catégorie \"Silencieux\". Appuyez ici pour donner votre avis."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Cette notification a été élevée d\'un niveau. Appuyez ici pour donner votre avis."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Cette notification a été abaissée d\'un niveau. Appuyez ici pour donner votre avis."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Testez les notifications améliorées"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Activez les notifications améliorées pour continuer à recevoir des suggestions d\'actions, de réponses et plus encore. Les notifications intelligentes Android ne sont plus disponibles."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Activer"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Pas maintenant"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"En savoir plus"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Les notifications améliorées peuvent lire tout le contenu des notifications, y compris des informations personnelles comme le nom des contacts et les messages. Cette fonctionnalité peut aussi fermer des notifications ou effectuer des actions grâce aux boutons figurant dans ces notifications, par exemple répondre aux appels téléphoniques.\n\nElle peut aussi activer ou désactiver le mode Prioritaire et modifier les paramètres associés."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notification d\'information du mode Routine"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Vous risquez d\'être à court de batterie plus tôt que prévu"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Économiseur de batterie activé pour prolonger l\'autonomie"</string>
@@ -2275,14 +2267,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Vous pouvez désormais agrandir une partie de l\'écran"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Activer dans les paramètres"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Fermer"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Débloquer le micro de l\'appareil"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Débloquer l\'appareil photo de l\'appareil"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Pour <b><xliff:g id="APP">%s</xliff:g></b> et tous les services et applis"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Débloquer"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Confidentialité du capteur"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Icône de l\'application"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Image de branding de l\'application"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 123419b..12bb42d 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -342,7 +342,7 @@
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Facer captura de pantalla"</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Pode facer capturas de pantalla."</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"desactivar ou modificar a barra de estado"</string>
- <string name="permdesc_statusBar" msgid="5809162768651019642">"Permite á aplicación desactivar a barra de estado ou engadir e eliminar as iconas do sistema."</string>
+ <string name="permdesc_statusBar" msgid="5809162768651019642">"Permite á aplicación desactivar a barra de estado ou engadir e quitar as iconas do sistema."</string>
<string name="permlab_statusBarService" msgid="2523421018081437981">"actuar como a barra de estado"</string>
<string name="permdesc_statusBarService" msgid="6652917399085712557">"Permite á aplicación ser a barra de estado."</string>
<string name="permlab_expandStatusBar" msgid="1184232794782141698">"ampliar/contraer a barra de estado"</string>
@@ -352,7 +352,7 @@
<string name="permlab_install_shortcut" msgid="7451554307502256221">"instalar atallos"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Permite a unha aplicación engadir atallos na pantalla de inicio sen intervención do usuario."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"desinstalar atallos"</string>
- <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Permite á aplicación eliminar atallos da pantalla de inicio sen a intervención do usuario."</string>
+ <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Permite á aplicación quitar atallos da pantalla de inicio sen a intervención do usuario."</string>
<string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"redirixir as chamadas saíntes"</string>
<string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Permite á aplicación ver o número que se está marcando durante unha chamada saínte coa opción de redirixir a chamada a un número diferente ou abortar a chamada."</string>
<string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"responder chamadas telefónicas"</string>
@@ -709,8 +709,8 @@
<string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Permite a unha aplicación fornecer e utilizar certificados DRM. Non se deberían precisar nunca para as aplicacións normais."</string>
<string name="permlab_handoverStatus" msgid="7620438488137057281">"recibir o estado das transferencias de Android Beam"</string>
<string name="permdesc_handoverStatus" msgid="3842269451732571070">"Permite a esta aplicación recibir información acerca das transferencias actuais de Android Beam"</string>
- <string name="permlab_removeDrmCertificates" msgid="710576248717404416">"eliminar certificados DRM"</string>
- <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Permite a unha aplicación eliminar os certificados DRM. As aplicacións normais non o deberían precisar nunca."</string>
+ <string name="permlab_removeDrmCertificates" msgid="710576248717404416">"quitar certificados DRM"</string>
+ <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"Permite a unha aplicación quitar os certificados DRM. As aplicacións normais non o deberían precisar nunca."</string>
<string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"vincular a un servizo de mensaxaría"</string>
<string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"Permite ao propietario vincularse á interface de nivel superior dun servizo de mensaxaría. As aplicacións normais non deberían necesitar este permiso."</string>
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"vincular aos servizos do operador"</string>
@@ -1582,7 +1582,7 @@
<string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"Límite de datos wifi superado"</string>
<string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Pasácheste <xliff:g id="SIZE">%s</xliff:g> do límite establecido"</string>
<string name="data_usage_restricted_title" msgid="126711424380051268">"Datos en segundo plano limitados"</string>
- <string name="data_usage_restricted_body" msgid="5338694433686077733">"Toca para eliminar a restrición."</string>
+ <string name="data_usage_restricted_body" msgid="5338694433686077733">"Toca para quitar a restrición."</string>
<string name="data_usage_rapid_title" msgid="2950192123248740375">"Uso elevado de datos móbiles"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"As aplicacións utilizaron máis datos do normal"</string>
<string name="data_usage_rapid_app_body" msgid="5425779218506513861">"A aplicación <xliff:g id="APP">%s</xliff:g> utilizou máis datos do normal"</string>
@@ -1677,7 +1677,7 @@
<string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Debuxaches incorrectamente o padrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Se realizas <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos máis, terás que desbloquear o dispositivo Android TV a través dunha conta de correo electrónico.\n\n Téntao de novo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Debuxaches o padrón de desbloqueo incorrectamente <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Se realizas <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos máis, terás que desbloquear o teléfono a través dunha conta de correo electrónico.\n\n Téntao de novo dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
- <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Eliminar"</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Quitar"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Queres subir o volume máis do nivel recomendado?\n\nA reprodución de son a un volume elevado durante moito tempo pode provocar danos nos oídos."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Queres utilizar o atallo de accesibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Cando o atallo está activado, podes premer os dous botóns de volume durante 3 segundos para iniciar unha función de accesibilidade."</string>
@@ -1833,7 +1833,7 @@
<string name="restr_pin_try_later" msgid="5897719962541636727">"Téntao de novo máis tarde"</string>
<string name="immersive_cling_title" msgid="2307034298721541791">"Vendo pantalla completa"</string>
<string name="immersive_cling_description" msgid="7092737175345204832">"Para saír, pasa o dedo cara abaixo desde a parte superior."</string>
- <string name="immersive_cling_positive" msgid="7047498036346489883">"De acordo"</string>
+ <string name="immersive_cling_positive" msgid="7047498036346489883">"Entendido"</string>
<string name="done_label" msgid="7283767013231718521">"Feito"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Control desprazable circular das horas"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Control desprazable circular dos minutos"</string>
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"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 outras funcións, como “Hey Google”\n\n"<annotation id="url">"Máis información"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"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 outras funcións, como “Hey Google”."</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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Pechar"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Resposta"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Rexeitar"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Colgar"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada entrante"</string>
@@ -1948,7 +1948,7 @@
<string name="user_creation_adding" msgid="7305185499667958364">"Queres permitir que <xliff:g id="APP">%1$s</xliff:g> cree un usuario novo con <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="52674936078683285">"Engadir un idioma"</string>
<string name="country_selection_title" msgid="5221495687299014379">"Preferencia de rexión"</string>
- <string name="search_language_hint" msgid="7004225294308793583">"Nome do idioma"</string>
+ <string name="search_language_hint" msgid="7004225294308793583">"Escribe o nome do idioma"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Suxeridos"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Todos os idiomas"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"Todas as rexións"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"O nivel desta notificación diminuíu a Silenciosa. Toca para compartir a túa opinión."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Esta notificación clasificouse nun nivel superior. Toca para compartir a túa opinión."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Esta notificación clasificouse nun nivel inferior. Toca para compartir a túa opinión."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Proba notificacións melloradas"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Para seguir recibindo accións suxeridas, respostas e moito máis, activa as notificacións melloradas. As notificacións intelixentes de Android xa non son compatibles."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Activar"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Agora non"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Máis información"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Coas notificacións melloradas pódese ler todo o contido das notificacións, mesmo a información persoal (por exemplo, os nomes dos contactos e as mensaxes). Con esta función tamén se poden ignorar as notificacións ou realizar accións nos botóns que aparecen nelas, como responder chamadas telefónicas.\n\nAdemais, permite activar e desactivar o modo de prioridade e cambiar a configuración relacionada."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificación da información do modo de rutina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A batería pode esgotarse antes do habitual"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Para ampliar a duración da batería activouse a función Aforro de batería"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index f0e3f77..2f2df47 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -87,7 +87,7 @@
<string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"પસંદગીનું નેટવર્ક બદલવાનો પ્રયાસ કરો. બદલવા માટે ટૅપ કરો."</string>
<string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"કટોકટીની કૉલિંગ સેવા અનુપલબ્ધ"</string>
<string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"વાઇ-ફાઇ પરથી કટોકટીના કૉલ કરી શકાતા નથી"</string>
- <string name="notification_channel_network_alert" msgid="4788053066033851841">"ચેતવણીઓ"</string>
+ <string name="notification_channel_network_alert" msgid="4788053066033851841">"અલર્ટ"</string>
<string name="notification_channel_call_forward" msgid="8230490317314272406">"કૉલ ફૉર્વર્ડિંગ"</string>
<string name="notification_channel_emergency_callback" msgid="54074839059123159">"કટોકટી કૉલબૅક મોડ"</string>
<string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"મોબાઇલ ડેટાની સ્થિતિ"</string>
@@ -288,7 +288,7 @@
<string name="notification_channel_network_available" msgid="6083697929214165169">"નેટવર્ક ઉપલબ્ધ છે"</string>
<string name="notification_channel_vpn" msgid="1628529026203808999">"VPN સ્થિતિ"</string>
<string name="notification_channel_device_admin" msgid="6384932669406095506">"તમારા IT વ્યવસ્થાપક તરફથી અલર્ટ"</string>
- <string name="notification_channel_alerts" msgid="5070241039583668427">"ચેતવણીઓ"</string>
+ <string name="notification_channel_alerts" msgid="5070241039583668427">"અલર્ટ"</string>
<string name="notification_channel_retail_mode" msgid="3732239154256431213">"રિટેલ ડેમો"</string>
<string name="notification_channel_usb" msgid="1528280969406244896">"USB કનેક્શન"</string>
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"ઍપ ચાલી રહ્યું છે"</string>
@@ -303,7 +303,7 @@
<string name="android_system_label" msgid="5974767339591067210">"Android સિસ્ટમ"</string>
<string name="user_owner_label" msgid="8628726904184471211">"વ્યક્તિગત પ્રોફાઇલ પર સ્વિચ કરો"</string>
<string name="managed_profile_label" msgid="7316778766973512382">"કાર્યાલયની પ્રોફાઇલ પર સ્વિચ કરો"</string>
- <string name="permgrouplab_contacts" msgid="4254143639307316920">"Contacts"</string>
+ <string name="permgrouplab_contacts" msgid="4254143639307316920">"સંપર્કો"</string>
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"તમારા સંપર્કોને ઍક્સેસ કરવાની"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"સ્થાન"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"આ ઉપકરણના સ્થાનને ઍક્સેસ કરવાની"</string>
@@ -319,10 +319,8 @@
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"તમારી શારીરિક પ્રવૃત્તિને ઍક્સેસ કરવી"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"કૅમેરા"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"ચિત્રો લેવાની અને વીડિઓ રેકોર્ડ કરવાની"</string>
- <!-- no translation found for permgrouplab_nearby_devices (5529147543651181991) -->
- <skip />
- <!-- no translation found for permgroupdesc_nearby_devices (3213561597116913508) -->
- <skip />
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"નજીકના ડિવાઇસ"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"નજીકના ડિવાઇસ શોધો અને કનેક્ટ કરો"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"કૉલ લૉગ"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"ફોન કૉલ લૉગ વાંચો અને લખો"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"ફોન"</string>
@@ -540,14 +538,10 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"ઍપને આસપાસના બ્લૂટૂથ ડિવાઇસ શોધવાની અને તેની સાથે જોડી કરવાની મંજૂરી આપે છે"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"જોડી કરેલા બ્લૂટૂથ ડિવાઇસ સાથે કનેક્ટ કરો"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"ઍપને જોડી કરેલા બ્લૂટૂથ ડિવાઇસ સાથે કનેક્ટ કરવાની મંજૂરી આપે છે"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
- <!-- no translation found for permlab_uwb_ranging (8141915781475770665) -->
- <skip />
- <!-- no translation found for permdesc_uwb_ranging (2519723069604307055) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"નજીકના બ્લૂટૂથ ડિવાઇસ પર જાહેરાત કરો"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"નજીકના બ્લૂટૂથ ડિવાઇસ પર ઍપને જાહેરાત કરવાની મંજૂરી આપે છે"</string>
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"અલ્ટ્રા-વાઇડબૅન્ડ ડિવાઇસની વચ્ચેનું અંતર નક્કી કરો"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ઍપને નજીકના અલ્ટ્રા-વાઇડબૅન્ડ ડિવાઇસની વચ્ચેનું સંબંધિત અંતર નક્કી કરવાની મંજૂરી આપો"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"પસંદગીની NFC ચુકવણીની સેવા વિશે માહિતી"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"આ મંજૂરીને આપવાથી, ઍપ તમારી પસંદગીની NFC ચુકવણીની સેવા વિશે માહિતી મેળવી શકે છે, જેમ કે રજિસ્ટર થયેલી સહાય અને નિર્ધારિત સ્થાન."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"નિઅર ફીલ્ડ કમ્યુનિકેશન નિયંત્રિત કરો"</string>
@@ -1683,7 +1677,7 @@
<string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"તમે તમારી અનલૉક પૅટર્ન <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે દોરી છે. વધુ <xliff:g id="NUMBER_1">%2$d</xliff:g> અસફળ પ્રયાસ પછી, તમને ઇમેઇલ એકાઉન્ટનો ઉપયોગ કરીને તમારા Android TV ડિવાઇસને અનલૉક કરવાનું કહેવામાં આવશે.\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g> સેકન્ડમાં ફરીથી પ્રયાસ કરો."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"તમે તમારી અનલૉક પૅટર્ન <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે દોરી. હજી <xliff:g id="NUMBER_1">%2$d</xliff:g> અસફળ પ્રયાસ પછી, તમને ઇમેઇલ એકાઉન્ટનો ઉપયોગ કરીને ફોનને અનલૉક કરવાનું કહેવામાં આવશે.\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g> સેકન્ડમાં ફરીથી પ્રયાસ કરો."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
- <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"દૂર કરો"</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"કાઢી નાખો"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ભલામણ કરેલ સ્તરની ઉપર વૉલ્યૂમ વધાર્યો?\n\nલાંબા સમય સુધી ઊંચા અવાજે સાંભળવું તમારી શ્રવણક્ષમતાને નુકસાન પહોંચાડી શકે છે."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ઍક્સેસિબિલિટી શૉર્ટકટનો ઉપયોગ કરીએ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"જ્યારે શૉર્ટકટ ચાલુ હોય, ત્યારે બન્ને વૉલ્યૂમ બટનને 3 સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધા શરૂ થઈ જશે."</string>
@@ -1725,7 +1719,7 @@
<string name="accessibility_button_instructional_text" msgid="8853928358872550500">"કોઈ એક સુવિધાથી બીજી સુવિધા પર સ્વિચ કરવા માટે, ઍક્સેસિબિલિટી બટનને ટચ કરીને થોડીવાર દબાવી રાખો."</string>
<string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"કોઈ એક સુવિધાથી બીજી સુવિધા પર સ્વિચ કરવા માટે, બે આંગળીઓ વડે સ્ક્રીનની ઉપરની તરફ સ્વાઇપ કરીને દબાવી રાખો."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"કોઈ એક સુવિધાથી બીજી સુવિધા પર સ્વિચ કરવા માટે, ત્રણ આંગળીઓ વડે સ્ક્રીનની ઉપરની તરફ સ્વાઇપ કરીને દબાવી રાખો."</string>
- <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"વિસ્તૃતીકરણ"</string>
+ <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"મોટું કરવું"</string>
<string name="user_switched" msgid="7249833311585228097">"વર્તમાન વપરાશકર્તા <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> પર સ્વિચ કરી રહ્યાં છે…"</string>
<string name="user_logging_out_message" msgid="7216437629179710359">"<xliff:g id="NAME">%1$s</xliff:g> લોગ આઉટ થઈ રહ્યાં છે…"</string>
@@ -1858,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"બૅટરી સેવર ઘેરી થીમની સુવિધા ચાલુ કરે છે અને બૅકગ્રાઉન્ડમાં થતી પ્રવૃત્તિ, કેટલીક વિઝ્યુઅલ ઇફેક્ટ અને “Ok Google” જેવી સુવિધાઓને મર્યાદિત કે બંધ કરે છે\n\n"<annotation id="url">"વધુ જાણો"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"બૅટરી સેવર ઘેરી થીમની સુવિધા ચાલુ કરે છે અને બૅકગ્રાઉન્ડમાં થતી પ્રવૃત્તિ, કેટલીક વિઝ્યુઅલ ઇફેક્ટ અને “Ok Google” જેવી સુવિધાઓને મર્યાદિત કે બંધ કરે છે."</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>
@@ -1937,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"બંધ કરો"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"જવાબ"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"નકારો"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"સમાપ્ત કરો"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"ઇનકમિંગ કૉલ"</string>
@@ -2034,7 +2028,7 @@
<string name="autofill_save_type_debit_card" msgid="3169397504133097468">"ડેબિટ કાર્ડ"</string>
<string name="autofill_save_type_payment_card" msgid="6555012156728690856">"ચુકવણી કાર્ડ"</string>
<string name="autofill_save_type_generic_card" msgid="1019367283921448608">"કાર્ડ"</string>
- <string name="autofill_save_type_username" msgid="1018816929884640882">"વપરાશકર્તાનામ"</string>
+ <string name="autofill_save_type_username" msgid="1018816929884640882">"વપરાશકર્તાનું નામ"</string>
<string name="autofill_save_type_email_address" msgid="1303262336895591924">"ઇમેઇલ સરનામું"</string>
<string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"શાંત રહો અને નજીકમાં આશ્રય લો."</string>
<string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"દરિયાકિનારાના પ્રદેશો તથા નદીકાંઠાના વિસ્તારો ખાલી કરીને તાત્કાલિક સુરક્ષિત ઊંચા સ્થાન પર જાઓ."</string>
@@ -2080,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"નોટિફિકેશનને સાઇલન્ટ પર અવનત કરવામાં આવ્યું. પ્રતિસાદ આપવા માટે ટૅપ કરો."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"આ નોટિફિકેશનને ઉપલી રેંક આપવામાં આવી. પ્રતિસાદ આપવા માટે ટૅપ કરો."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"આ નોટિફિકેશનને નીચલી રેંક આપવામાં આવી. પ્રતિસાદ આપવા માટે ટૅપ કરો."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"વધારાના નોટિફિકેશન અજમાવી જુઓ"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"સૂચવેલી ક્રિયાઓ, જવાબો અને બીજું ઘણું મેળવવા માટે, વધારાના નોટિફિકેશન ચાલુ કરો. Android માટે અનુકૂળ નોટિફિકેશનને હવેથી સપોર્ટ કરવામાં આવતો નથી."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"ચાલુ કરો"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"હમણાં નહીં"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"વધુ જાણો"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"વધારાના નોટિફિકેશન સંપર્કનું નામ અને સંદેશા જેવી વ્યક્તિગત માહિતી સહિત નોટિફિકેશનનું બધું કન્ટેન્ટ વાંચી શકે છે. આ સુવિધા નોટિફિકેશન છોડી પણ શકે છે અથવા નોટિફિકેશનમાં બટન પર ફોન કૉલનો જવાબ આપવા જેવી ક્રિયાઓ પણ કરી શકે છે.\n\nઆ સુવિધા પ્રાધાન્યતા મોડ ચાલુ કે બંધ પણ કરી શકે છે અને સંબંધિત સેટિંગ બદલી પણ શકે છે."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"રૂટિન મોડની માહિતીનું નોટિફિકેશન"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"સામાન્ય રીતે ચાર્જ કરવાના સમય પહેલાં બૅટરી સમાપ્ત થઈ શકે છે"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"બૅટરી આવરદા વધારવા માટે બૅટરી સેવર ચાલુ કર્યું"</string>
@@ -2279,14 +2267,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"હવે તમે તમારી સ્ક્રીનનો અમુક ભાગ મોટો કરી શકો છો"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"સેટિંગમાં ચાલુ કરો"</string>
<string name="dismiss_action" msgid="1728820550388704784">"છોડી દો"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"ડિવાઇસના માઇક્રોફોનને અનબ્લૉક કરો"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"ડિવાઇસના કૅમેરાને અનબ્લૉક કરો"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> અને બધી ઍપ અને સેવાઓ માટે"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"અનબ્લૉક કરો"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"સેન્સર પ્રાઇવસી સંબંધિત નોટિફિકેશન"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ઍપ્લિકેશનનું આઇકન"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ઍપ્લિકેશનની બ્રાંડિંગ છબી"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 8def350..190d561 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -103,7 +103,7 @@
<string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
<string name="serviceClassData" msgid="4148080018967300248">"डेटा"</string>
<string name="serviceClassFAX" msgid="2561653371698904118">"फ़ैक्स"</string>
- <string name="serviceClassSMS" msgid="1547664561704509004">"मैसेज (एसएमएस)"</string>
+ <string name="serviceClassSMS" msgid="1547664561704509004">"एसएमएस"</string>
<string name="serviceClassDataAsync" msgid="2029856900898545984">"Async"</string>
<string name="serviceClassDataSync" msgid="7895071363569133704">"समन्वयन"</string>
<string name="serviceClassPacket" msgid="1430642951399303804">"पैकेट"</string>
@@ -309,7 +309,7 @@
<string name="permgroupdesc_location" msgid="1995955142118450685">"इस डिवाइस की जगह तक पहुंचने दें"</string>
<string name="permgrouplab_calendar" msgid="6426860926123033230">"कैलेंडर"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"अपने कैलेंडर को ऐक्सेस करने"</string>
- <string name="permgrouplab_sms" msgid="795737735126084874">"मैसेज (एसएमएस)"</string>
+ <string name="permgrouplab_sms" msgid="795737735126084874">"एसएमएस"</string>
<string name="permgroupdesc_sms" msgid="5726462398070064542">"मैसेज (एसएमएस) भेजें और देखें"</string>
<string name="permgrouplab_storage" msgid="1938416135375282333">"फ़ाइलें और मीडिया"</string>
<string name="permgroupdesc_storage" msgid="6351503740613026600">"अपने डिवाइस पर मौजूद फ़ोटो, मीडिया और फ़ाइलें ऐक्सेस करने की"</string>
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"अनुमति देने पर, ऐप्लिकेशन, आस-पास मौजूद ब्लूटूथ डिवाइसों को खोज पाएगा और उनसे जुड़ पाएगा"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"जोड़े गए ब्लूटूथ डिवाइसों से कनेक्ट करें"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"अनुमति देने पर, ऐप्लिकेशन, जोड़े गए ब्लूटूथ डिवाइसों से कनेक्ट कर पाएगा"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"आस-पास मौजूद ब्लूटूथ डिवाइसों पर विज्ञापन दिखाएं"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"अनुमति मिलने पर, ऐप्लिकेशन, आस-पास मौजूद ब्लूटूथ डिवाइसों पर विज्ञापन दिखा पाएगा"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"आस-पास मौजूद Ultra-Wideband डिवाइसों के बीच की दूरी का पता लगाएं"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ऐप्लिकेशन को आस-पास मौजूद Ultra-Wideband डिवाइसों के बीच की दूरी का पता लगाने की अनुमति दें"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"NFC का इस्तेमाल करने वाली पैसे चुकाने की पसंदीदा सेवा की जानकारी"</string>
@@ -1721,7 +1719,7 @@
<string name="accessibility_button_instructional_text" msgid="8853928358872550500">"एक सुविधा से दूसरी सुविधा पर जाने के लिए, सुलभता बटन दबाकर रखें."</string>
<string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"एक सुविधा से दूसरी सुविधा पर जाने के लिए, स्क्रीन पर दो उंगलियों से ऊपर की ओर स्वाइप करें और थोड़ी देर तक उंगलियां स्क्रीन पर रखे रहें."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"एक सुविधा से दूसरी सुविधा पर जाने के लिए, स्क्रीन पर तीन उंगलियों से ऊपर की ओर स्वाइप करें और थोड़ी देर तक उंगलियां स्क्रीन पर रखे रहें."</string>
- <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"बड़ा करना"</string>
+ <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"ज़ूम करने की सुविधा"</string>
<string name="user_switched" msgid="7249833311585228097">"मौजूदा उपयोगकर्ता <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> पर स्विच किया जा रहा है…"</string>
<string name="user_logging_out_message" msgid="7216437629179710359">"<xliff:g id="NAME">%1$s</xliff:g> द्वारा प्रस्थान किया जा रहा है…"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"बैटरी सेवर गहरे रंग वाली थीम को चालू कर देता है. साथ ही, यह बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और \"Hey Google\" जैसी दूसरी सुविधाएं इस्तेमाल करने से डिवाइस को रोकता है या इन्हें बंद कर देता है\n\n"<annotation id="url">"ज़्यादा जानें"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"बैटरी सेवर गहरे रंग वाली थीम को चालू कर देता है. साथ ही, यह बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और \"Hey Google\" जैसी दूसरी सुविधाएं इस्तेमाल करने से डिवाइस को रोकता है या इन्हें बंद कर देता है."</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>
@@ -1933,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"बंद करें"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"जवाब दें"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"वीडियो"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"अस्वीकार करें"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"कॉल काटें"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"आने वाला (इनकमिंग) कॉल"</string>
@@ -1994,7 +1991,7 @@
<string name="app_category_productivity" msgid="1844422703029557883">"उत्पादकता"</string>
<string name="app_category_accessibility" msgid="6643521607848547683">"सुलभता"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"डिवाइस में जगह"</string>
- <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"USB डीबग करना"</string>
+ <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"यूएसबी डीबग करना"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"घंटा"</string>
<string name="time_picker_minute_label" msgid="8307452311269824553">"मिनट"</string>
<string name="time_picker_header_text" msgid="9073802285051516688">"समय सेट करें"</string>
@@ -2076,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"इस सूचना के मिलने पर होने वाली आवाज़ बंद कर दी गई है. सुझाव/शिकायत/राय देने के लिए टैप करें."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"इस सूचना को रैंकिंग में ऊपर किया गया था. सुझाव/शिकायत/राय देने के लिए टैप करें."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"इस सूचना को रैंकिंग में नीचे किया गया था. सुझाव/शिकायत/राय देने के लिए टैप करें."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"\'बेहतर सूचनाएं\' सुविधा आज़माएं"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"कार्रवाई और जवाब वगैरह के लिए सुझाव पाना चाहते हैं, तो सूचनाएं पाने की सुविधा बेहतर करें. Android की, ज़रूरत के हिसाब से सूचनाएं पाने की सुविधा अब काम नहीं करती है."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"चालू करें"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"अभी नहीं"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"ज़्यादा जानें"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"\'बेहतर सूचनाएं\' सुविधा, डिवाइस पर मिलने वाली सभी सूचनाओं का कॉन्टेंट पढ़ सकती है. इसमें आपकी निजी जानकारी, जैसे कि संपर्कों के नाम और मैसेज भी शामिल हैं. यह सुविधा, डिवाइस पर आने वाली सूचनाओं को रद्द कर सकती है या सूचनाओं में दिखने वाले बटन से कार्रवाइयां भी कर सकती है, जैसे कि फ़ोन कॉल का जवाब देना.\n\nयह सुविधा, प्राथमिकता मोड को चालू या बंद कर सकती है और इससे जुड़ी सेटिंग में बदलाव भी कर सकती है."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"रूटीन मोड जानकारी की सूचना"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"बैटरी आम तौर पर जितने समय चलती है, उससे पहले खत्म हो सकती है"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"बैटरी लाइफ़ बढ़ाने के लिए \'बैटरी सेवर\' चालू हो गया है"</string>
@@ -2271,18 +2262,14 @@
<string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
<string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
- <string name="window_magnification_prompt_title" msgid="2876703640772778215">"ज़ूम करके देखने की नई सुविधा उपलब्ध है"</string>
+ <string name="window_magnification_prompt_title" msgid="2876703640772778215">"ज़ूम करने की सुविधा वाली नई सेटिंग"</string>
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"अब आप अपनी स्क्रीन के किसी हिस्से को ज़ूम करके देख सकते हैं"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"सेटिंग में जाकर, इस सुविधा को चालू करें"</string>
<string name="dismiss_action" msgid="1728820550388704784">"खारिज करें"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"डिवाइस का माइक्रोफ़ोन अनब्लॉक करें"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"डिवाइस का कैमरा अनब्लॉक करें"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> के साथ-साथ, सभी ऐप्लिकेशन और सेवाओं के लिए"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"अनब्लॉक करें"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"सेंसर से जुड़ी निजता के बारे में सूचना"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ऐप्लिकेशन का आइकॉन"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ऐप्लिकेशन की ब्रैंड इमेज"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 8938dd2..28fb400 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -250,10 +250,10 @@
<string name="global_action_power_options" msgid="1185286119330160073">"Uključi"</string>
<string name="global_action_restart" msgid="4678451019561687074">"Ponovo pokreni"</string>
<string name="global_action_emergency" msgid="1387617624177105088">"Hitne službe"</string>
- <string name="global_action_bug_report" msgid="5127867163044170003">"Izvješće o bugovima"</string>
+ <string name="global_action_bug_report" msgid="5127867163044170003">"Izvješće o programskim pogreškama"</string>
<string name="global_action_logout" msgid="6093581310002476511">"Završi sesiju"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"Snimka zaslona"</string>
- <string name="bugreport_title" msgid="8549990811777373050">"Izvješće o bugovima"</string>
+ <string name="bugreport_title" msgid="8549990811777373050">"Izvješće o programskim pogreškama"</string>
<string name="bugreport_message" msgid="5212529146119624326">"Time će se prikupiti podaci o trenutačnom stanju vašeg uređaja koje ćete nam poslati u e-poruci. Za pripremu izvješća o programskoj pogrešci potrebno je nešto vremena pa vas molimo za strpljenje."</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"Interaktivno izvješće"</string>
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"To možete upotrebljavati u većini slučajeva. Moći ćete pratiti izradu izvješća, unijeti više pojedinosti o problemu i izraditi snimke zaslona. Mogu se izostaviti neki odjeljci koji se upotrebljavaju rjeđe i produljuju izradu izvješća."</string>
@@ -1180,7 +1180,7 @@
<string name="redo" msgid="7231448494008532233">"Ponovi"</string>
<string name="autofill" msgid="511224882647795296">"Automatsko popunjavanje"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"Odabir teksta"</string>
- <string name="addToDictionary" msgid="8041821113480950096">"Dodaj u rječnik"</string>
+ <string name="addToDictionary" msgid="8041821113480950096">"Dodavanje u rječnik"</string>
<string name="deleteText" msgid="4200807474529938112">"Izbriši"</string>
<string name="inputMethod" msgid="1784759500516314751">"Način unosa"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Radnje s tekstom"</string>
@@ -1406,7 +1406,7 @@
<string name="share_remote_bugreport_action" msgid="7630880678785123682">"DIJELI"</string>
<string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ODBIJ"</string>
<string name="select_input_method" msgid="3971267998568587025">"Odabir načina unosa"</string>
- <string name="show_ime" msgid="6406112007347443383">"Zadržava se na zaslonu dok je fizička tipkovnica aktivna"</string>
+ <string name="show_ime" msgid="6406112007347443383">"Zadrži na zaslonu dok je fizička tipkovnica aktivna"</string>
<string name="hardware" msgid="1800597768237606953">"Prikaži virtualnu tipkovnicu"</string>
<string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Konfigurirajte fizičku tipkovnicu"</string>
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Dodirnite da biste odabrali jezik i raspored"</string>
@@ -1712,12 +1712,12 @@
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nemoj uključiti"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"UKLJUČENO"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"ISKLJUČENO"</string>
- <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Želite li dopustiti usluzi <xliff:g id="SERVICE">%1$s</xliff:g> potpunu kontrolu nad uređajem?"</string>
+ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Želite li usluzi <xliff:g id="SERVICE">%1$s</xliff:g> dopustiti potpunu kontrolu nad uređajem?"</string>
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"Ako uključite <xliff:g id="SERVICE">%1$s</xliff:g>, vaš uređaj neće upotrebljavati zaključavanje zaslona za bolju enkripciju podataka."</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"Potpuna kontrola prikladna je za aplikacije koje vam pomažu s potrebama pristupačnosti, ali ne i za većinu aplikacija."</string>
- <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Prikaz zaslona i upravljanje njime"</string>
+ <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Pregled zaslona i upravljanje njime"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Može čitati sav sadržaj na zaslonu i prikazati sadržaj povrh drugih aplikacija."</string>
- <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Prikaz i izvršavanje radnji"</string>
+ <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Pregled i izvršavanje radnji"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Može pratiti vaše interakcije s aplikacijama ili senzorom uređaja i stupati u interakciju s aplikacijama u vaše ime."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Dopusti"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Odbij"</string>
@@ -1730,7 +1730,7 @@
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Isključi prečac"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Upotrijebi prečac"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
- <string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcija boje"</string>
+ <string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcija boja"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Dodatno zatamnjenje"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za glasnoću. Uključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za glasnoću. Isključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
@@ -1875,10 +1875,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Štednja baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizualne efekte i značajke kao što je Hey Google\n\n"<annotation id="url">"Saznajte više"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Štednja baterije uključuje tamnu temu i ograničava ili isključuje aktivnosti u pozadini, neke vizualne efekte i značajke kao što je Hey Google."</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>
@@ -1962,6 +1960,7 @@
<string name="close_button_text" msgid="10603510034455258">"Zatvori"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Odgovori"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Videozapis"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Odbij"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Prekini"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Dolazni poziv"</string>
@@ -2107,18 +2106,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Obavijest je degradirana u bešumnu. Dodirnite da biste poslali povratne informacije."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Obavijest je više rangirana. Dodirnite da biste poslali povratne informacije."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Obavijest je niže rangirana. Dodirnite da biste poslali povratne informacije."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Proba poboljšanih obavijesti"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Da biste i dalje primali prijedloge za radnje, odgovore i druge informacije, uključite poboljšane obavijesti. Prilagodljive obavijesti za Android više nisu podržane."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Uključi"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Ne sad"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saznajte više"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Poboljšane obavijesti mogu čitati sadržaj svih obavijesti, uključujući osobne podatke kao što su imena kontakata i poruke. Ta značajka može i odbacivati obavijesti ili poduzimati radnje povezane s gumbima u obavijestima kao što je odgovaranje na telefonske pozive.\n\nZnačajka također može uključiti ili isključiti način prioritetnih obavijesti i promijeniti povezane postavke."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Obavještavanje o informacijama u Rutinskom načinu rada"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Baterija se može isprazniti prije uobičajenog vremena punjenja"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Štednja baterije aktivirana je kako bi se produljilo trajanje baterije"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 8a4cfab..4350d60 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"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, bizonyos vizuális effekteket és olyan funkciókat, mint az „Ok Google”.\n\n"<annotation id="url">"További információ"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"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, bizonyos vizuális effekteket és olyan funkciókat, mint az „Ok Google”."</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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Bezárás"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Fogadás"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Elutasítás"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Befejezés"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Bejövő hívás"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Ezt az értesítést némára állította a rendszer. Visszajelzés küldéséhez koppintson ide."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Ezt az értesítést előrébb sorolta a rendszer. Visszajelzés küldéséhez koppintson ide."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Ezt az értesítést hátrébb sorolta a rendszer. Visszajelzés küldéséhez koppintson ide."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Bővített értesítés kipróbálása"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Ha továbbra is szeretne kapni javasolt műveleteket, válaszokat és egyebeket, kapcsolja be a bővített értesítéseket. Az androidos alkalmazkodó értesítések már nem támogatottak."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Bekapcsolás"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Most nem"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"További információ"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"A bővített értesítések minden értesítéstartalmat olvashatnak (így a személyes adatokat, mint például a névjegyek nevét és az üzeneteket is). Ez a funkció emellett elvetheti az értesítéseket, valamint műveleteket végezhet az értesítésekben lévő gombokkal, például felveheti a telefonhívásokat.\n\nEz a funkció a Prioritásos módot is be- vagy kikapcsolhatja, és módosíthatja a kapcsolódó beállításokat."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Információs értesítés a rutinmódról"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Előfordulhat, hogy az akkumulátor lemerül a szokásos töltési időszak előtt"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Akkumulátorkímélő mód aktiválva az akkumulátor üzemidejének növelése érdekében"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index aebdbe6..bbf50a7 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Թույլ է տալիս հավելվածին հայտնաբերել և զուգակցել մոտակա Bluetooth սարքերը"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"միանալ զուգակցված Bluetooth սարքերի"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Թույլ է տալիս հավելվածին միանալ զուգակցված Bluetooth սարքերի"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"Գովազդ մոտակա Bluetooth սարքերում"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Թույլատրում է հավելվածին գովազդ փոխանցել մոտակա Bluetooth սարքերին"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"որոշել մոտակա UWB սարքերի միջև հարաբերական դիրքավորումը"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Թույլատրել հավելվածին որոշել գերլայնաշերտ կապի տեխնոլոգիան աջակցող մոտակա սարքերի միջև հարաբերական դիրքավորումը"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Տեղեկություններ NFC վճարային ծառայության մասին"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Մարտկոցի տնտեսումը միացնում է մուգ թեման և սահմանափակում կամ անջատում է ֆոնային գործընթացները, որոշ վիզուալ էֆեկտներ և այլ գործառույթներ, օրինակ՝ «Ok Google» հրահանգի ճանաչումը։\n\n"<annotation id="url">"Իմանալ ավելին"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Մարտկոցի տնտեսումը միացնում է մուգ թեման և սահմանափակում կամ անջատում է ֆոնային գործընթացները, որոշ վիզուալ էֆեկտներ և այլ գործառույթներ, օրինակ՝ «Ok Google» հրահանգի ճանաչումը։"</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>
@@ -1933,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Փակել"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>՝ <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Պատասխանել"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Մերժել"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Ավարտել"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Մուտքային զանգ"</string>
@@ -1990,7 +1988,7 @@
<string name="app_category_image" msgid="7307840291864213007">"Լուսանկարներ և պատկերներ"</string>
<string name="app_category_social" msgid="2278269325488344054">"Սոցցանցեր և հաղորդակցություն"</string>
<string name="app_category_news" msgid="1172762719574964544">"Նորություններ և ամսագրեր"</string>
- <string name="app_category_maps" msgid="6395725487922533156">"Քարտեզներ և նավարկում"</string>
+ <string name="app_category_maps" msgid="6395725487922533156">"Քարտեզներ և նավիգացիա"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"Արդյունավետություն"</string>
<string name="app_category_accessibility" msgid="6643521607848547683">"Հատուկ գործառույթներ"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Սարքի հիշողություն"</string>
@@ -2059,7 +2057,7 @@
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> հավելվածն ուզում է ցուցադրել հատվածներ <xliff:g id="APP_2">%2$s</xliff:g> հավելվածից"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Փոփոխել"</string>
<string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"Զանգերի և ծանուցումների համար թրթռոցը միացված է"</string>
- <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"Զանգերի և ծանուցումների համար ձայնն անջատած է"</string>
+ <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"Զանգերի և ծանուցումների համար ձայնն անջատված է"</string>
<string name="notification_channel_system_changes" msgid="2462010596920209678">"Համակարգի փոփոխություններ"</string>
<string name="notification_channel_do_not_disturb" msgid="7832584281883687653">"Չանհանգստացնել"</string>
<string name="zen_upgrade_notification_visd_title" msgid="2001148984371968620">"Այժմ «Չանհանգստացնել» ռեժիմում ծանուցումները թաքցվում են"</string>
@@ -2076,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Այս ծանուցման կարևորության մակարդակը իջեցվել է և դարձել անձայն։ Հպեք՝ կարծիք հայտնելու համար։"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Այս ծանուցման կարևորության մակարդակը բարձրացվել է։ Հպեք՝ կարծիք հայտնելու համար։"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Այս ծանուցման կարևորության մակարդակն իջեցվել է։ Հպեք՝ կարծիք հայտնելու համար։"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Փորձեք ընդլայնված ծանուցումները"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Միացրեք ընդլայնված ծանուցումները, որպեսզի այսուհետ ևս ստանաք գործողությունների, պատասխանների և այլ առաջարկներ։ Android-ի հարմարվող ծանուցումներն այլևս չեն աջակցվում։"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Միացնել"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Ոչ հիմա"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Իմանալ ավելին"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Ընդլայնված ծանուցումներին հասանելի է բոլոր ծանուցումների պարունակությունը, ներառյալ անձնական տվյալները, օրինակ՝ կոնտակտների անուններն ու հաղորդագրությունները։ Այս գործառույթը կարող է նաև փակել ծանուցումները կամ ակտիվացնել դրանցում առկա կոճակները, այդ թվում՝ պատասխանել հեռախոսազանգերի։\n\nԱյս գործառույթը կարող է նաև միացնել/անջատել «Միայն կարևորները» ռեժիմը և փոխել համապատասխան կարգավորումները։"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Ծանուցում լիցքավորման մասին"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Մարտկոցը կարող է սովորականից շուտ սպառվել"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Մարտկոցի կյանքը երկարացնելու համար ակտիվացվել է մարտկոցի տնտեսման ռեժիմը"</string>
@@ -2275,14 +2267,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Դուք կարող եք խոշորացնել ձեր էկրանի մի մասը"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Միացնել կարգավորումներում"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Փակել"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Արգելահանել սարքի խոսափողը"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Արգելահանել սարքի տեսախցիկը"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> հավելվածի և մյուս բոլոր հավելվածների ու ծառայությունների համար"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Արգելահանել"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Տվիչների գաղտնիություն"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Հավելվածի պատկերակ"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Հավելվածի բրենդային պատկեր"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index bae2d48..222bbe8 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Mengizinkan aplikasi menemukan dan menyambungkan perangkat Bluetooth di sekitar"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"hubungkan ke perangkat Bluetooth yang disambungkan"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Mengizinkan aplikasi terhubung ke perangkat Bluetooth yang disambungkan"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"tampilkan iklan ke perangkat Bluetooth di sekitar"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Mengizinkan aplikasi untuk menampilkan iklan ke perangkat Bluetooth di sekitar"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"menentukan posisi relatif antar-perangkat Ultra-Wideband di sekitar"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Mengizinkan aplikasi menentukan posisi relatif antar-perangkat Ultra-Wideband di sekitar"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informasi Layanan Pembayaran NFC Pilihan"</string>
@@ -1049,8 +1047,8 @@
<string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Aktifkan Menjelajah dengan Sentuhan?"</string>
<string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ingin mengaktifkan Menjelajah dengan Sentuhan. Saat Menjelajah dengan Sentuhan diaktifkan, Anda dapat melihat atau mendengar deskripsi dari apa yang ada di bawah jari Anda atau melakukan gerakan untuk berinteraksi dengan tablet."</string>
<string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ingin mengaktifkan Menjelajah dengan Sentuhan. Saat Menjelajah dengan Sentuhan diaktifkan, Anda dapat mendengar atau melihat deskripsi dari apa yang ada di bawah jari Anda atau melakukan gerakan untuk berinteraksi dengan ponsel."</string>
- <string name="oneMonthDurationPast" msgid="4538030857114635777">"1 bulan yang lalu"</string>
- <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Sebelum 1 bulan yang lalu"</string>
+ <string name="oneMonthDurationPast" msgid="4538030857114635777">"1 bulan lalu"</string>
+ <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Sebelum 1 bulan lalu"</string>
<plurals name="last_num_days" formatted="false" msgid="687443109145393632">
<item quantity="other"> <xliff:g id="COUNT_1">%d</xliff:g> hari terakhir</item>
<item quantity="one"> <xliff:g id="COUNT_0">%d</xliff:g> hari terakhir</item>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Penghemat Baterai mengaktifkan Tema gelap dan membatasi atau menonaktifkan aktivitas di latar belakang, beberapa efek visual, dan fitur seperti “Ok Google”\n\n"<annotation id="url">"Pelajari lebih lanjut"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Penghemat Baterai mengaktifkan Tema gelap dan membatasi atau menonaktifkan aktivitas di latar belakang, beberapa efek visual, dan fitur seperti “Ok Google”."</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>
@@ -1933,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Tutup"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Jawab"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Tolak"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Tutup"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Panggilan masuk"</string>
@@ -2076,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Notifikasi ini didemosikan menjadi Senyap. Ketuk untuk memberikan masukan."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Notifikasi ini diberi peringkat lebih tinggi. Ketuk untuk memberikan masukan."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Notifikasi ini diberi peringkat lebih rendah. Ketuk untuk memberikan masukan."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Coba notifikasi yang disempurnakan"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Untuk terus mendapatkan saran tindakan, balasan, dan lainnya, aktifkan notifikasi yang disempurnakan. Notifikasi Adaptif Android tidak lagi didukung."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Aktifkan"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Lain kali"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Pelajari lebih lanjut"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Notifikasi yang disempurnakan dapat membaca semua isi notifikasi, termasuk informasi pribadi seperti nama kontak dan pesan. Fitur ini juga dapat menutup notifikasi atau memicu tindakan pada tombol di notifikasi, seperti menjawab panggilan telepon.\n\nFitur ini juga dapat mengaktifkan atau menonaktifkan mode Prioritas dan mengubah setelan terkait."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notifikasi info Mode Rutinitas"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Baterai mungkin habis sebelum pengisian daya biasanya"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Penghemat Baterai diaktifkan untuk memperpanjang masa pakai baterai"</string>
@@ -2275,14 +2267,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Sekarang Anda dapat memperbesar sebagian layar"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Aktifkan di Setelan"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Tutup"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Batalkan pemblokiran mikrofon perangkat"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Batalkan pemblokiran kamera perangkat"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Untuk <b><xliff:g id="APP">%s</xliff:g></b> serta semua aplikasi dan layanan"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Batalkan pemblokiran"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privasi Sensor"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikon aplikasi"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Brand image aplikasi"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index c23362e..47503ed 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Rafhlöðusparnaður kveikir á dökku þema og takmarkar eða slekkur á bakgrunnsvirkni, tilteknum myndbrellum og eiginleikum eins og „Ok Google“.\n\n"<annotation id="url">"Nánar"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Rafhlöðusparnaður kveikir á dökku þema og takmarkar eða slekkur á bakgrunnsvirkni, tilteknum myndbrellum og eiginleikum eins og „Ok Google“."</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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Loka"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Svara"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Hafna"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Leggja á"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Símtal berst"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Þessi tilkynning var gerð þögul. Ýttu til að senda ábendingu."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Þessi tilkynning fékk hærri stöðu. Ýttu til að senda ábendingu."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Þessi tilkynning fékk lægri stöðu. Ýttu til að senda ábendingu."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Prófa auknar tilkynningar"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Kveiktu á auknum tilkynningum til að halda áfram að fá tillögur að aðgerðum, svörum og fleira. Breytilegar tilkynningar í Android eru ekki lengur studdar."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Kveikja"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Ekki núna"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Nánar"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Auknar tilkynningar geta lesið allt efni tilkynninga, þar á meðal persónuupplýsingar á borð við nöfn tengiliða og skilaboð. Þessi eiginleiki getur einnig hunsað tilkynningar eða notað hnappa í tilkynningum eins og að svara símtölum.\n\nÞessi eiginleiki getur einnig kveikt og slökkt á forgangsstillingu og breytt tengdum stillingum."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Upplýsingatilkynning aðgerðastillingar"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Rafhlaðan kann að tæmast áður en hún kemst í hleðslu"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Kveikt á rafhlöðusparnaði til að lengja endingu rafhlöðunnar"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 80aaa37..31096c7 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -538,8 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Consente all\'app di rilevare e accoppiare dispositivi Bluetooth nelle vicinanze"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"Connessione a dispositivi Bluetooth accoppiati"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Consente all\'app di connettersi ai dispositivi Bluetooth accoppiati"</string>
- <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"Pubblicità su dispositivi Bluetooth vicini"</string>
- <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Consente all\'app di fare pubblicità sui dispositivi Bluetooth nelle vicinanze"</string>
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"trasmettere annunci a dispositivi Bluetooth vicini"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Consente all\'app di trasmettere annunci ai dispositivi Bluetooth nelle vicinanze"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"Possibilità di stabilire la posizione relativa tra dispositivi a banda ultralarga nelle vicinanze"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Consenti all\'app di stabilire la posizione relativa tra dispositivi a banda ultralarga nelle vicinanze"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informazioni del servizio di pagamento NFC preferito"</string>
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"L\'opzione Risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, alcuni effetti visivi e funzionalità come \"Hey Google\"\n\n"<annotation id="url">"Scopri di più"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"L\'opzione Risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, alcuni effetti visivi e funzionalità come \"Hey Google\"."</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>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Chiudi"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Rispondi"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Rifiuta"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Riaggancia"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Chiamata in arrivo"</string>
@@ -1955,7 +1954,7 @@
<string name="locale_search_menu" msgid="6258090710176422934">"Cerca"</string>
<string name="app_suspended_title" msgid="888873445010322650">"App non disponibile"</string>
<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">"Ulteriori informazioni"</string>
+ <string name="app_suspended_more_details" msgid="211260942831587014">"Scopri di più"</string>
<string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Riattiva app"</string>
<string name="work_mode_off_title" msgid="5503291976647976560">"Attivare il profilo di lavoro?"</string>
<string name="work_mode_off_message" msgid="8417484421098563803">"Le tue app di lavoro, le notifiche, i dati e altri elementi del profilo di lavoro saranno attivati."</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Questa notifica è stata retrocessa a Silenziosa. Tocca per dare un feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Questa notifica è stata posizionata più in alto. Tocca per dare un feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Questa notifica è stata posizionata più in basso. Tocca per dare un feedback."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Prova le notifiche avanzate"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Per continuare a ricevere suggerimenti di azioni, risposte e altro, attiva le notifiche avanzate. Le notifiche adattive Android non sono più supportate."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Attiva"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Non ora"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Scopri di più"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Le notifiche avanzate possono accedere all\'intero contenuto di una notifica, incluse le informazioni personali, come i nomi dei contatti e i messaggi. Questa funzionalità può anche ignorare le notifiche o svolgere azioni sui pulsanti nelle notifiche, come rispondere alle telefonate.\n\nQuesta funzionalità può anche attivare o disattivare la modalità Priorità e modificare le relative impostazioni."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notifica di informazioni sulla modalità Routine"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"La batteria potrebbe esaurirsi prima della ricarica abituale"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Risparmio energetico attivo per far durare di più la batteria"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 734224c..b3134a7 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -325,7 +325,7 @@
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"גישה לפעילות הגופנית שלך"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"מצלמה"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"צילום תמונות והקלטת וידאו"</string>
- <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"מכשירים קרובים"</string>
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"מכשירים בקרבת מקום"</string>
<string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"איתור מכשירים שנמצאים בקרבת מקום והתחברות אליהם"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"יומני שיחות"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"קריאה וכתיבה של יומן השיחות של הטלפון"</string>
@@ -1405,7 +1405,7 @@
<string name="usb_power_notification_message" msgid="7284765627437897702">"המכשיר המחובר בטעינה. יש להקיש לאפשרויות נוספות."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"המכשיר זיהה התקן אודיו אנלוגי"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"ההתקן שחיברת לא תואם לטלפון הזה. יש להקיש לקבלת מידע נוסף."</string>
- <string name="adb_active_notification_title" msgid="408390247354560331">"ניפוי באגים של USB מחובר"</string>
+ <string name="adb_active_notification_title" msgid="408390247354560331">"ניפוי באגים ב-USB מחובר"</string>
<string name="adb_active_notification_message" msgid="5617264033476778211">"יש להקיש כדי לכבות את ניפוי הבאגים ב-USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"יש ללחוץ על ההתראה כדי להשבית ניפוי באגים ב-USB."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"ניפוי הבאגים האלחוטי מחובר"</string>
@@ -1734,13 +1734,13 @@
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"לא להפעיל"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"מופעל"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"כבוי"</string>
- <string name="accessibility_enable_service_title" msgid="3931558336268541484">"ברצונך להעניק לשירות <xliff:g id="SERVICE">%1$s</xliff:g> שליטה מלאה במכשיר?"</string>
+ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"להעניק לשירות <xliff:g id="SERVICE">%1$s</xliff:g> שליטה מלאה במכשיר?"</string>
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"אם השירות <xliff:g id="SERVICE">%1$s</xliff:g> יופעל, המכשיר לא ישתמש בנעילת המסך כדי לשפר את הצפנת הנתונים."</string>
- <string name="accessibility_service_warning_description" msgid="291674995220940133">"שליטה מלאה מתאימה לאפליקציות שעוזרות עם צורכי הנגישות שלך, אבל לא לרוב האפליקציות."</string>
+ <string name="accessibility_service_warning_description" msgid="291674995220940133">"האפשרות לשליטה מלאה במכשיר לא מתאימה לכל האפליקציות, אלא רק לאפליקציות שעוזרות עם צורכי הנגישות שלך."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"הצגת המסך ושליטה בו"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"אפשרות לקריאת כל התוכן במסך ולהצגת התוכן מעל אפליקציות אחרות."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"הצגה וביצוע של פעולות"</string>
- <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"זוהי אפשרות למעקב אחר האינטראקציות שלך עם אפליקציה או חיישן חומרה כלשהם, ולביצוע אינטראקציה בשמך."</string>
+ <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"אפשרות למעקב אחר האינטראקציה שלך עם אפליקציות או חיישני חומרה, וביצוע אינטראקציה בשמך."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"אישור"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"עדיף שלא"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"יש להקיש על תכונה כדי להתחיל להשתמש בה:"</string>
@@ -1898,10 +1898,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"התכונה \'חיסכון בסוללה\' מפעילה עיצוב כהה ומגבילה או מכבה פעילות ברקע, חלק מהאפקטים החזותיים ותכונות כמו \"Hey Google\"\n\n"<annotation id="url">"מידע נוסף"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"התכונה \'חיסכון בסוללה\' מפעילה עיצוב כהה ומגבילה או מכבה פעילות ברקע, חלק מהאפקטים החזותיים ותכונות כמו \"Hey Google\"."</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>
@@ -1993,6 +1991,8 @@
<string name="close_button_text" msgid="10603510034455258">"סגירה"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"תשובה"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"דחייה"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"ניתוק"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"שיחה נכנסת"</string>
@@ -2012,7 +2012,7 @@
<string name="user_creation_adding" msgid="7305185499667958364">"לאפשר לאפליקציה <xliff:g id="APP">%1$s</xliff:g> ליצור משתמש חדש באמצעות <xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
<string name="language_selection_title" msgid="52674936078683285">"הוספת שפה"</string>
<string name="country_selection_title" msgid="5221495687299014379">"העדפת אזור"</string>
- <string name="search_language_hint" msgid="7004225294308793583">"צריך להקליד את שם השפה"</string>
+ <string name="search_language_hint" msgid="7004225294308793583">"הקלדת שם השפה"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"הצעות"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"כל השפות"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"כל האזורים"</string>
@@ -2140,18 +2140,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ההתראה הזו הורדה בדרגה ל\'שקטה\'. יש להקיש כדי לשלוח משוב."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"דירוג ההתראה הזו הוגבה. יש להקיש כדי לשלוח משוב."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"ההתראה הזו דורגה נמוך יותר. יש להקיש כדי לשלוח משוב."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"רוצה לנסות את ההתראות המשופרות?"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"כדי להמשיך לקבל הצעות לפעולות, לתשובות ועוד, יש להפעיל את ההתראות המשופרות. אין יותר תמיכה בהתראות מותאמות ל-Android."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"הפעלה"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"לא עכשיו"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"מידע נוסף"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"כשתכונת ההתראות המשופרות פועלת, המערכת יכולה לקרוא את כל תוכן ההתראות, כולל מידע אישי כמו שמות של אנשי קשר והודעות. כמו כן, התכונה תוכל לסגור התראות או לבצע פעולות שהן כוללות, כמו מענה לשיחות טלפון.\n\nהתכונה תוכל גם להפעיל או להשבית את מצב העדיפות ולשנות את ההגדרות הקשורות."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"התראת מידע לגבי מצב שגרתי"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"הסוללה עלולה להתרוקן לפני המועד הרגיל של הטעינה"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"תכונת החיסכון בסוללה הופעלה כדי להאריך את חיי הסוללה"</string>
@@ -2337,7 +2331,7 @@
<string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
<string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
- <string name="window_magnification_prompt_title" msgid="2876703640772778215">"הגדרות חדשות להגדלה"</string>
+ <string name="window_magnification_prompt_title" msgid="2876703640772778215">"הגדרות חדשות לתכונה \'הגדלה\'"</string>
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"עכשיו אפשר להגדיל חלק מהמסך"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"הפעלה בהגדרות"</string>
<string name="dismiss_action" msgid="1728820550388704784">"סגירה"</string>
@@ -2349,5 +2343,5 @@
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"סמל האפליקציה"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"תדמית המותג של האפליקציה"</string>
<string name="view_and_control_notification_title" msgid="4300765399209912240">"בדיקה של הגדרות הגישה"</string>
- <string name="view_and_control_notification_content" msgid="8003766498562604034">"לשירות <xliff:g id="SERVICE_NAME">%s</xliff:g> יהיו הרשאות לצפייה ולשליטה במסך. יש להקיש כדי לבדוק."</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"לשירות <xliff:g id="SERVICE_NAME">%s</xliff:g> יש הרשאה להצגת המסך ושליטה בו. אפשר להקיש כדי לבדוק."</string>
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index b38d9dc..a8d2f1a 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"バッテリー セーバーを有効にすると、ダークテーマが ON になり、バックグラウンド アクティビティ、一部の視覚効果や、「OK Google」などの機能が制限されるか OFF になります\n\n"<annotation id="url">"詳細"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"バッテリー セーバーを有効にすると、ダークテーマが ON になり、バックグラウンド アクティビティ、一部の視覚効果や、「OK Google」などの機能が制限されるか 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>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"閉じる"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"応答"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"ビデオ"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"拒否"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"通話終了"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"着信"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"この通知の重要度がサイレントに下がりました。タップしてフィードバックをお送りください。"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"この通知の重要度が上がりました。タップしてフィードバックをお送りください。"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"この通知の重要度が下がりました。タップしてフィードバックをお送りください。"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"拡張通知を使ってみる"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"操作や返信の候補などを利用し続けるには、拡張通知を ON にしてください。Android 通知の自動調整はサポートを終了しました。"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"ON にする"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"後で"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"詳細"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"拡張通知はすべての通知コンテンツにアクセスできます。これには、連絡先の名前などの個人情報やメッセージも含まれます。また、通知を非表示にしたり、電話に出るなど、通知内の操作ボタンを実行したりすることもできます。\n\nまた、この機能は、優先モードの設定を切り替えたり、関連する設定を変更したりすることもできます。"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ルーティン モード情報の通知"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"通常の充電を行う前に電池が切れる可能性があります"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"電池を長持ちさせるため、バッテリー セーバーが有効になりました"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index a14c551..ba7beac 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ბატარეის დამზოგი ჩართავს მუქ თემას და შეზღუდავს ან გამორთავს ფონურ აქტივობას, ზოგიერთ ვიზუალურ ეფექტს და ისეთ ფუნქციებს, როგორიცაა „Ok Google“\n\n"<annotation id="url">"შეიტყვეთ მეტი"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"ბატარეის დამზოგი ჩართავს მუქ თემას და შეზღუდავს ან გამორთავს ფონურ აქტივობას, ზოგიერთ ვიზუალურ ეფექტს და ისეთ ფუნქციებს, როგორიცაა „Ok Google“."</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>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"დახურვა"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"პასუხი"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"ვიდეო"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"უარყოფა"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"გათიშვა"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"შემომავალი ზარი"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ეს შეტყობინება ჩამოქვეითდა და გახდა „ჩუმი“. შეეხეთ გამოხმაურების მოსაწოდებლად."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"ეს შეტყობინება დაწინაურდა. შეეხეთ გამოხმაურების მოსაწოდებლად."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"ეს შეტყობინება ჩამოქვეითდა. შეეხეთ გამოხმაურების მოსაწოდებლად."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"გამოცადეთ გაფართოებული შეტყობინებები"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"შემოთავაზებული ქმედებების, პასუხების და სხვა მითითებების მიღების გასაგრძელებლად ჩართეთ გაფართოებული შეტყობინებები. Android-ის ადაპტაციური შეტყობინებები აღარაა მხარდაჭერილი."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"ჩართვა"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"ახლა არა"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"შეიტყვეთ მეტი"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"გაფართოებულ შეტყობინებებს შეუძლია ყველა შეტყობინების კონტენტის, მათ შორის, ისეთი პერსონალური ინფორმაციის წაკითხვა, როგორიცაა კონტაქტების სახელები და შეტყობინებები. ამ ფუნქციას ასევე შეუძლია შეტყობინებების დახურვა ან შეტყობინებათა ღილაკების ამოქმედება, მაგალითად, სატელეფონო ზარებზე პასუხი.\n\nამ ფუნქციას ასევე შეუძლია პრიორიტეტული რეჟიმის ჩართვა თუ გამორთვა და დაკავშირებული პარამეტრების შეცვლა."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"რუტინის რეჟიმის საინფორმაციო შეტყობინება"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ბატარეა შეიძლება დაჯდეს დატენის ჩვეულ დრომდე"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ბატარეის დამზოგი გააქტიურდა ბატარეის მუშაობის გასახანგრძლივლებლად"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 8dbe6ee..7d6bf12 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -305,7 +305,7 @@
<string name="managed_profile_label" msgid="7316778766973512382">"Жұмыс профиліне ауысу"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"Контактілер"</string>
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"контактілерге кіру"</string>
- <string name="permgrouplab_location" msgid="1858277002233964394">"Геодерек"</string>
+ <string name="permgrouplab_location" msgid="1858277002233964394">"Локация"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"бұл құрылғының орналасқан жерін көру"</string>
<string name="permgrouplab_calendar" msgid="6426860926123033230">"Күнтізбе"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"күнтізбеге кіру"</string>
@@ -538,12 +538,10 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Қолданбаға маңайдағы Bluetooth құрылғыларын анықтап, жұптауға рұқсат береді."</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"жұпталған Bluetooth құрылғыларына қосылу"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Қолданбаға жұпталған Bluetooth құрылғыларына қосылуға рұқсат береді."</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
- <string name="permlab_uwb_ranging" msgid="8141915781475770665">"маңайдағы кеңжолақты құрылғылардың бір-біріне қатысты орнын анықтау"</string>
- <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Қолданбаға маңайдағы кеңжолақты құрылғылардың бір-біріне қатысты орнын анықтауға мүмкіндік береді."</string>
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"Жарнаманы маңайдағы Bluetooth құрылғыларына беру"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Қолданба жарнаманы маңайдағы Bluetooth құрылғыларына бере алады."</string>
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"маңайдағы кең жолақты құрылғылардың бір-біріне қатысты орнын анықтау"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Қолданбаға маңайдағы кең жолақты құрылғылардың бір-біріне қатысты орнын анықтауға мүмкіндік береді."</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Таңдаулы NFC төлеу қызметі туралы ақпарат"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Қолданба тіркелген көмектер және баратын жер маршруты сияқты таңдаулы NFC төлеу қызметі туралы ақпаратты ала алатын болады."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"NFC функциясын басқару"</string>
@@ -1575,7 +1573,7 @@
<string name="storage_usb_drive_label" msgid="6631740655876540521">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB дискі"</string>
<string name="storage_usb" msgid="2391213347883616886">"USB жады"</string>
<string name="extract_edit_menu_button" msgid="63954536535863040">"Өзгерту"</string>
- <string name="data_usage_warning_title" msgid="9034893717078325845">"Деректердің пайдаланылуы туралы ескерту"</string>
+ <string name="data_usage_warning_title" msgid="9034893717078325845">"Дерек шығыны туралы ескерту"</string>
<string name="data_usage_warning_body" msgid="1669325367188029454">"Деректің <xliff:g id="APP">%s</xliff:g> пайдаландыңыз"</string>
<string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"Мобильдік деректер шегіне жетті"</string>
<string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Wi-Fi деректер шегіне жеттіңіз"</string>
@@ -1692,7 +1690,7 @@
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Қосылмасын"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"ҚОСУЛЫ"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"ӨШІРУЛІ"</string>
- <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> қызметі құрылғыңызды толық басқаруына рұқсат етілсін бе?"</string>
+ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> қызметіне құрылғыны толық басқаруға рұқсат етілсін бе?"</string>
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"<xliff:g id="SERVICE">%1$s</xliff:g> қоссаңыз, құрылғыңыз деректерді шифрлау үшін экранды бекітуді пайдаланбайды."</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"Арнайы мүмкіндіктер бойынша көмектесетін қолданбаларға ғана құрылғыны толық басқару рұқсатын берген дұрыс."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Экранды көру және басқару"</string>
@@ -1700,7 +1698,7 @@
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Әрекеттерді көру және орындау"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Ол қолданбамен немесе жабдық датчигімен істеген тапсырмаларыңызды бақылайды және қолданбаларды сіздің атыңыздан пайдаланады."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Рұқсат ету"</string>
- <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Қабылдамау"</string>
+ <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Тыйым салу"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Функцияны пайдалана бастау үшін түртіңіз:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"\"Арнайы мүмкіндіктер\" түймесімен қолданылатын функцияларды таңдаңыз"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Дыбыс деңгейі пернелері тіркесімімен қолданылатын функцияларды таңдаңыз"</string>
@@ -1711,7 +1709,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>
- <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Аса күңгірт"</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>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін пайдалану үшін дыбыс деңгейін реттейтін екі түймені де 3 секунд басып тұрыңыз"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Батареяны үнемдеу режимі қараңғы тақырыпты іске қосады және фондық әрекеттерге, кейбір визуалдық әсерлерге, \"Ok Google\" сияқты функцияларға шектеу қояды немесе оларды өшіреді.\n\n"<annotation id="url">"Толығырақ"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Батареяны үнемдеу режимі қараңғы тақырыпты іске қосады және фондық әрекеттерге, кейбір визуалдық әсерлерге, \"Ok Google\" сияқты функцияларға шектеу қояды немесе оларды өшіреді."</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>
@@ -1933,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Жабу"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Жауап"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Қабылдамау"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Тұтқаны қою"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Кіріс қоңырау"</string>
@@ -2076,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Бұл хабарландырудың маңыздылық деңгейі \"Үнсіз\" санатына төмендетілді. Пікір қалдыру үшін түртіңіз."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Бұл хабарландырудың маңыздылық деңгейі көтерілді. Пікір қалдыру үшін түртіңіз."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Бұл хабарландырудың маңыздылық деңгейі төмендетілді. Пікір қалдыру үшін түртіңіз."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Кеңейтілген хабарландыруларды пайдалану"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Ұсынылған әрекеттер, жауаптар және т.б. алып отыру үшін \"Кеңейтілген хабарландырулар\" функциясын қосыңыз. Android-тың \"Бейімделетін хабарландырулар\" функциясына бұдан былай қолдау көрсетілмейді."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Қосу"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Қазір емес"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Толығырақ"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"\"Кеңейтілген хабарландырулар\" функциясы барлық хабарландыру мазмұнын (контакт атаулары мен хабарлар сияқты жеке ақпаратты қоса алғанда) оқи алады. Сондай-ақ бұл функция арқылы хабарландыруларды жабуға немесе хабарландырулардағы түймелерді басқаруға (мысалы, телефон қоңырауларына жауап беру) болады.\n\nОл арқылы \"Маңызды\" режимін қосуға немесе өшіруге, қатысты параметрлерді өзгертуге де болады."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Режим туралы хабарландыру"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батарея заряды азаюы мүмкін"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Батарея ұзаққа жетуі үшін, Батареяны үнемдеу режимі іске қосылды"</string>
@@ -2275,17 +2267,13 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Енді экранның бір бөлігін ұлғайтуға болады."</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Параметрлер бөлімінен қосу"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Қабылдамау"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Құрылғы микрофонының бөгеуін алыңыз"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Құрылғы камерасының бөгеуін алыңыз"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> және барлық қолданбалар мен қызметтерге арналған."</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Бөгеуді алу"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Датчикке қатысты құпиялылық"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Қолданба белгішесі"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Қолданба брендін ілгері жылжыту кескіні"</string>
<string name="view_and_control_notification_title" msgid="4300765399209912240">"Пайдалану параметрлерін тексеріңіз"</string>
- <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> қызметі экраныңызды көріп, бақылай алады. Көру үшін түртіңіз."</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> экраныңызды көріп, оны басқара алады. Өту үшін түртіңіз."</string>
</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index e294f38..370bb29 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"អនុញ្ញាតឱ្យកម្មវិធីស្វែងរក និងផ្គូផ្គងឧបករណ៍ប៊្លូធូសដែលនៅជិត"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"ភ្ជាប់ទៅឧបករណ៍ប៊្លូធូសដែលបានផ្គូផ្គង"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"អនុញ្ញាតឱ្យកម្មវិធីភ្ជាប់ទៅឧបករណ៍ប៊្លូធូសដែលបានផ្គូផ្គង"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"ផ្សាយពាណិជ្ជកម្មទៅឧបករណ៍ប៊្លូធូសដែលនៅជិត"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"អនុញ្ញាតឱ្យកម្មវិធីផ្សាយពាណិជ្ជកម្មទៅឧបករណ៍ប៊្លូធូសដែលនៅជិត"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"កំណត់ចម្ងាយពាក់ព័ន្ធរវាងឧបករណ៍ Ultra-Wideband ដែលនៅជិត"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"អនុញ្ញាតឱ្យកម្មវិធីកំណត់ចម្ងាយពាក់ព័ន្ធរវាងឧបករណ៍ Ultra-Wideband ដែលនៅជិត"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ព័ត៌មានអំពីសេវាបង់ប្រាក់តាម NFC ជាអាទិភាព"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"មុខងារសន្សំថ្មបើករចនាប័ទ្មងងឹត និងបិទឬដាក់កំហិតលើសកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពលរូបភាពមួយចំនួន និងមុខងារដូចជា “Ok Google” ជាដើម\n\n"<annotation id="url">"ស្វែងយល់បន្ថែម"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"មុខងារសន្សំថ្មបើករចនាប័ទ្មងងឹត និងបិទឬដាក់កំហិតលើសកម្មភាពផ្ទៃខាងក្រោយ ឥទ្ធិពលរូបភាពមួយចំនួន និងមុខងារដូចជា “Ok Google” ជាដើម។"</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>
@@ -1933,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"បិទ"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>៖ <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"ឆ្លើយ"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"វីដេអូ"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"បដិសេធ"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"ដាក់ចុះ"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"ការហៅចូល"</string>
@@ -2076,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ការជូនដំណឹងនេះត្រូវបានបន្ទាបតំណែងទៅស្ងាត់។ សូមចុចដើម្បីផ្ដល់មតិកែលម្អ។"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"ការជូនដំណឹងនេះត្រូវបានចាត់ថ្នាក់ខ្ពស់ជាងមុន។ សូមចុចដើម្បីផ្ដល់មតិកែលម្អ។"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"ការជូនដំណឹងនេះត្រូវបានចាត់ថ្នាក់ទាបជាងមុន។ សូមចុចដើម្បីផ្ដល់មតិកែលម្អ។"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"សាកល្បងប្រើការជូនដំណឹងប្រសើរជាងមុន"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"ដើម្បីបន្តទទួលបានការឆ្លើយតប សកម្មភាពដែលបានណែនាំ និងអ្វីៗជាច្រើនទៀត សូមបើកការជូនដំណឹងប្រសើរជាងមុន។ ការជូនដំណឺងដែលមានភាពបត់បែន Android មិនអាចប្រើបានទៀតទេ។"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"បើក"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"កុំទាន់"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"ស្វែងយល់បន្ថែម"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"ការជូនដំណឹងប្រសើរជាងមុនអាចអានខ្លឹមសារជូនដំណឹងទាំងអស់ រួមទាំងព័ត៌មានផ្ទាល់ខ្លួនដូចជា ឈ្មោះទំនាក់ទំនង និងសារជាដើម។ មុខងារនេះក៏អាចច្រានចោលការជូនដំណឹង ឬធ្វើសកម្មភាពលើប៊ូតុងនៅក្នុងការជូនដំណឹងផងដែរ ដូចជាការទទួលការហៅទូរសព្ទជាដើម។\n\nមុខងារនេះក៏អាចបើកឬបិទមុខងារអាទិភាព និងផ្លាស់ប្ដូរការកំណត់ដែលពាក់ព័ន្ធផងដែរ។"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ការជូនដំណឹងព័ត៌មានរបស់មុខងារទម្លាប់"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ថ្មអាចនឹងអស់ មុនពេលសាកថ្មធម្មតា"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"បានបើកដំណើរការមុខងារសន្សំថ្ម ដើម្បីបង្កើនកម្រិតថាមពលថ្ម"</string>
@@ -2275,14 +2266,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"ឥឡូវនេះ អ្នកអាចពង្រីកផ្នែកនៃអេក្រង់របស់អ្នកបានហើយ"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"បើកនៅក្នុងការកំណត់"</string>
<string name="dismiss_action" msgid="1728820550388704784">"ច្រានចោល"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"ឈប់ទប់ស្កាត់មីក្រូហ្វូនរបស់ឧបករណ៍"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"ឈប់ទប់ស្កាត់កាមេរ៉ារបស់ឧបករណ៍"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"សម្រាប់ <b><xliff:g id="APP">%s</xliff:g></b> និង កម្មវិធីនិងសេវាកម្មទាំងអស់"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"ឈប់ទប់ស្កាត់"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ឯកជនភាពឧបករណ៍ចាប់សញ្ញា"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"រូបកម្មវិធី"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"រូបភាពផ្សព្វផ្សាយម៉ាកកម្មវិធី"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index a030715..0daa7fe 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -319,10 +319,8 @@
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"ನಿಮ್ಮ ದೈಹಿಕ ಚಟುವಟಿಕೆಯನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"ಕ್ಯಾಮರಾ"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"ಚಿತ್ರಗಳನ್ನು ತೆಗೆಯಲು, ವೀಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಲು"</string>
- <!-- no translation found for permgrouplab_nearby_devices (5529147543651181991) -->
- <skip />
- <!-- no translation found for permgroupdesc_nearby_devices (3213561597116913508) -->
- <skip />
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"ಸಮೀಪದಲ್ಲಿರುವ ಸಾಧನಗಳು"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"ಸಮೀಪದಲ್ಲಿರುವ ಸಾಧನಗಳನ್ನು ಅನ್ವೇಷಿಸಿ ಮತ್ತು ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"ಕರೆಯ ಲಾಗ್"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"ಪೋನ್ ಕರೆಯ ಲಾಗ್ ಅನ್ನು ಓದಿ ಮತ್ತು ಬರೆಯಿರಿ"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"ಫೋನ್"</string>
@@ -540,14 +538,10 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"ಸಮೀಪದಲ್ಲಿರುವ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ಅನ್ವೇಷಿಸಲು ಮತ್ತು ಅವುಗಳಿಗೆ ಜೋಡಿಸಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಿ"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"ಜೋಡಿಸಿರುವ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳಿಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"ಜೋಡಿಸಲಾಗಿರುವ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳಿಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
- <!-- no translation found for permlab_uwb_ranging (8141915781475770665) -->
- <skip />
- <!-- no translation found for permdesc_uwb_ranging (2519723069604307055) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"ಸಮೀಪದ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳಿಗೆ ಜಾಹೀರಾತು ನೀಡಿ"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"ಸಮೀಪದ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳಿಗೆ ಜಾಹೀರಾತು ನೀಡಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"ಸಮೀಪದಲ್ಲಿರುವ ಅಲ್ಟ್ರಾ-ವೈಡ್ಬ್ಯಾಂಡ್ ಸಾಧನಗಳ ನಡುವೆ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸುತ್ತದೆ"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ಸಮೀಪದಲ್ಲಿರುವ ಅಲ್ಟ್ರಾ-ವೈಡ್ಬ್ಯಾಂಡ್ ಸಾಧನಗಳ ನಡುವೆ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಿ"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ಆದ್ಯತೆಯ NFC ಪಾವತಿ ಸೇವಾ ಮಾಹಿತಿ"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ನೋಂದಾಯಿತ ಅಪ್ಲಿಕೇಶನ್ ಗುರುತಿಸುವಿಕೆಗಳು ಮತ್ತು ಮಾರ್ಗ ಗಮ್ಯಸ್ಥಾನಗಳಂತಹ ಆದ್ಯತೆಯ NFC ಪಾವತಿ ಸೇವೆಗಳ ಬಗ್ಗೆ ಮಾಹಿತಿಯನ್ನು ಪಡೆಯಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ಸಮೀಪ ಕ್ಷೇತ್ರ ಸಂವಹನವನ್ನು ನಿಯಂತ್ರಿಸಿ"</string>
@@ -1699,7 +1693,7 @@
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"ನಿಮ್ಮ ಸಾಧನದ ಪೂರ್ಣ ನಿಯಂತ್ರಣ ಹೊಂದಲು <xliff:g id="SERVICE">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"ನೀವು <xliff:g id="SERVICE">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಿದರೆ, ನಿಮ್ಮ ಸಾಧನವು ಡೇಟಾ ಎನ್ಕ್ರಿಪ್ಶನ್ ಅನ್ನು ವರ್ಧಿಸಲು ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ಲಾಕ್ ಅನ್ನು ಬಳಸುವುದಿಲ್ಲ."</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"ಪ್ರವೇಶಿಸುವಿಕೆಯ ಅವಶ್ಯಕತೆಗಳಿಗೆ ಸಹಾಯ ಮಾಡುವ ಆ್ಯಪ್ಗಳಿಗೆ ಪೂರ್ಣ ನಿಯಂತ್ರಣ ನೀಡುವುದು ಸೂಕ್ತವಾಗಿರುತ್ತದೆ, ಆದರೆ ಬಹುತೇಕ ಆ್ಯಪ್ಗಳಿಗೆ ಇದು ಸೂಕ್ತವಲ್ಲ."</string>
- <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"ಪರದೆಯನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿಯಂತ್ರಿಸಿ"</string>
+ <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"ಸ್ಕ್ರೀನ್ ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿಯಂತ್ರಿಸಿ"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"ಇದು ಪರದೆಯ ಮೇಲಿನ ಎಲ್ಲಾ ವಿಷಯವನ್ನು ಓದಬಹುದು ಮತ್ತು ಇತರ ಆ್ಯಪ್ಗಳ ಮೇಲೆ ವಿಷಯವನ್ನು ಪ್ರದರ್ಶಿಸಬಹುದು."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"ಕ್ರಿಯೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿರ್ವಹಿಸಿ"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ಇದು ಆ್ಯಪ್ ಅಥವಾ ಹಾರ್ಡ್ವೇರ್ ಸೆನ್ಸರ್ನ ಜೊತೆಗಿನ ನಿಮ್ಮ ಸಂವಹನಗಳನ್ನು ಟ್ರ್ಯಾಕ್ ಮಾಡಬಹುದು, ಮತ್ತು ನಿಮ್ಮ ಪರವಾಗಿ ಆ್ಯಪ್ಗಳ ಜೊತೆ ಸಂವಹನ ನಡೆಸಬಹುದು."</string>
@@ -1858,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಡಾರ್ಕ್ ಥೀಮ್ ಅನ್ನು ಆನ್ ಮಾಡುತ್ತದೆ ಮತ್ತು ಹಿನ್ನೆಲೆ ಚಟುವಟಿಕೆ, ಕೆಲವು ವಿಷುವಲ್ ಎಫೆಕ್ಟ್ಗಳು ಮತ್ತು “Ok Google” ನಂತಹ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಮಿತಿಗೊಳಿಸುತ್ತದೆ ಅಥವಾ ಆಫ್ ಮಾಡುತ್ತದೆ.\n\n"<annotation id="url">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಡಾರ್ಕ್ ಥೀಮ್ ಅನ್ನು ಆನ್ ಮಾಡುತ್ತದೆ ಮತ್ತು ಹಿನ್ನೆಲೆ ಚಟುವಟಿಕೆ, ಕೆಲವು ವಿಷುವಲ್ ಎಫೆಕ್ಟ್ಗಳು ಮತ್ತು “Ok Google” ನಂತಹ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಮಿತಿಗೊಳಿಸುತ್ತದೆ ಅಥವಾ ಆಫ್ ಮಾಡುತ್ತದೆ."</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>
@@ -1937,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"ಮುಚ್ಚು"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"ಉತ್ತರಿಸಿ"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"ನಿರಾಕರಿಸಿ"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"ಹ್ಯಾಂಗ್ ಅಪ್"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"ಒಳಬರುವ ಕರೆ"</string>
@@ -2080,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ಈ ಅಧಿಸೂಚನೆಗೆ ಸೈಲೆಂಟ್ಗೆ ಹಿಂಬಡ್ತಿ ನೀಡಲಾಗಿದೆ. ಪ್ರತಿಕ್ರಿಯೆ ನೀಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"ಈ ಅಧಿಸೂಚನೆಗೆ ಮೇಲಿನ ಸ್ಥಾನವನ್ನು ನೀಡಲಾಗಿದೆ. ಪ್ರತಿಕ್ರಿಯೆ ನೀಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"ಈ ಅಧಿಸೂಚನೆಗೆ ಕೆಳಗಿನ ಸ್ಥಾನವನ್ನು ನೀಡಲಾಗಿದೆ. ಪ್ರತಿಕ್ರಿಯೆ ನೀಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"ವರ್ಧಿತ ಅಧಿಸೂಚನೆಗಳು ಪ್ರಯತ್ನಿಸಿ"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"ಸೂಚಿಸಲಾದ ಕ್ರಿಯೆಗಳು, ಪ್ರತ್ಯುತ್ತರಗಳು ಮತ್ತು ಹೆಚ್ಚಿನದನ್ನು ಪಡೆಯಲು, ವರ್ಧಿತ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆನ್ ಮಾಡಿ. Android ಅಡಾಪ್ಟಿವ್ ಅಧಿಸೂಚನೆಗಳು ಇನ್ನು ಮುಂದೆ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"ಆನ್ ಮಾಡಿ"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"ಈಗ ಬೇಡ"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"ವರ್ಧಿತ ಅಧಿಸೂಚನೆಗಳು ಸಂಪರ್ಕ ಹೆಸರುಗಳು ಮತ್ತು ಸಂದೇಶಗಳಂತಹ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಂತೆ ಎಲ್ಲಾ ಅಧಿಸೂಚನೆ ವಿಷಯವನ್ನು ಓದಬಹುದು. ಈ ವೈಶಿಷ್ಟ್ಯವು ಅಧಿಸೂಚನೆಗಳನ್ನು ವಜಾಗೊಳಿಸಬಹುದು ಅಥವಾ ಫೋನ್ ಕರೆಗಳಿಗೆ ಉತ್ತರಿಸುವಂತಹ ಅಧಿಸೂಚನೆಗಳಲ್ಲಿನ ಬಟನ್ಗಳಿಗೆ ಸಂಬಂಧಿಸಿದ ಕ್ರಮ ತೆಗೆದುಕೊಳ್ಳಬಹುದು.\n\nಈ ವೈಶಿಷ್ಟ್ಯವು ಆದ್ಯತಾ ಮೋಡ್ ಅನ್ನು ಆನ್ ಅಥವಾ ಆಫ್ ಮಾಡಬಹುದು ಮತ್ತು ಸಂಬಂಧಿತ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಬದಲಾಯಿಸಬಹುದು."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ದೈನಂದಿನ ಸ್ಥಿತಿಯ ಮಾಹಿತಿಯ ಅಧಿಸೂಚನೆ"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ಚಾರ್ಜ್ಗೆ ಮೊದಲೆ ಬ್ಯಾಟರಿ ಮುಗಿದು ಬಿಡಬಹುದು"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ಬ್ಯಾಟರಿ ಅವಧಿ ಹೆಚ್ಚಿಸಲು ಬ್ಯಾಟರಿ ಸೇವರ್ ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
@@ -2279,14 +2267,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"ನೀವು ಇದೀಗ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನ ಭಾಗವನ್ನು ಹಿಗ್ಗಿಸಬಹುದು"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಆನ್ ಮಾಡಿ"</string>
<string name="dismiss_action" msgid="1728820550388704784">"ವಜಾಗೊಳಿಸಿ"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"ಸಾಧನದ ಮೈಕ್ರೋಫೋನ್ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"ಸಾಧನದ ಕ್ಯಾಮರಾ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> ಮತ್ತು ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು ಹಾಗೂ ಸೇವೆಗಳಿಗಾಗಿ"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"ನಿರ್ಬಂಧವನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ಸೆನ್ಸರ್ ಗೌಪ್ಯತೆ"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ಅಪ್ಲಿಕೇಶನ್ ಐಕಾನ್"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ಅಪ್ಲಿಕೇಶನ್ ಬ್ರ್ಯಾಂಡಿಂಗ್ ಚಿತ್ರ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index aa64179..da8f4ab 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"앱이 근처의 블루투스 기기를 찾고 페어링하도록 허용"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"페어링된 블루투스 기기에 연결"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"앱이 페어링된 블루투스 기기에 연결하도록 허용"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"근처의 블루투스 기기로 광고"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"앱에서 근처의 블루투스 기기로 광고하도록 허용"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"근처 초광대역 기기 간 상대적 위치 파악"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"앱이 근처의 초광대역 기기 간 상대적 위치를 파악하도록 허용"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"기본 NFC 결제 서비스 정보"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"절전 기능은 어두운 테마를 사용 설정하고 백그라운드 활동, 일부 시각 효과, \'Hey Google\'과 같은 기능을 제한하거나 사용 중지합니다.\n\n"<annotation id="url">"자세히 알아보기"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"절전 기능은 어두운 테마를 사용 설정하고 백그라운드 활동, 일부 시각 효과, \'Hey Google\'과 같은 기능을 제한하거나 사용 중지합니다."</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>
@@ -1933,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"닫기"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"답변"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"동영상"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"거절"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"전화 끊기"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"수신 전화"</string>
@@ -2076,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"이 알림의 중요도가 무음으로 하향되었습니다. 의견을 보내려면 탭하세요."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"이전에 이 알림의 중요도는 더 높았습니다. 의견을 보내려면 탭하세요."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"이전에 이 알림의 중요도는 더 낮았습니다. 의견을 보내려면 탭하세요."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"개선된 알림 기능 사용해 보기"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"추천 작업, 답장 등을 계속 받으려면 개선된 알림 기능을 사용 설정하세요. Android 적응형 알림은 더 이상 지원되지 않습니다."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"사용 설정"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"나중에"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"자세히 알아보기"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"개선된 알림 기능은 연락처 이름과 메시지 등 개인 정보가 포함된 모든 알림 내용을 읽어줍니다. 알림을 닫거나 알림에 표시되는 버튼 관련 작업(예: 전화 받기)을 실행할 수도 있습니다.\n\n또한 이 기능은 우선순위 모드를 사용 또는 사용 중지하고 관련 설정을 변경할 수 있습니다."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"루틴 모드 정보 알림"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"평소에 충전하는 시간 전에 배터리가 소진될 수 있습니다."</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"배터리 수명을 연장하기 위해 절전 모드가 활성화되었습니다."</string>
@@ -2275,14 +2266,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"이제 화면 일부를 확대할 수 있습니다."</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"설정에서 사용 설정"</string>
<string name="dismiss_action" msgid="1728820550388704784">"닫기"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"기기 마이크 차단 해제"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"기기 카메라 차단 해제"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> 및 모든 앱 및 서비스 대상"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"차단 해제"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"센서 개인정보 보호"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"애플리케이션 아이콘"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"애플리케이션 브랜드 이미지"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 86aa419..de0acbe 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Колдонмого жакын жердеги Bluetooth түзмөктөрүн аныктап, жупташтырууга уруксат берет"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"жупташтырылган Bluetooth түзмөктөрү"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Колдонмого жупташтырылган Bluetooth түзмөктөрү менен байланышууга уруксат берет"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"жакын жердеги Bluetooth түзмөктөрүнө жарнамалоо"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Колдонмого жакын жердеги Bluetooth түзмөктөрүнө жарнама көрсөтүүгө мүмкүндүк берет"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"кең тилкелүү тармак аркылуу туташа турган жакын жердеги түзмөктөрдү аныктоо"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Колдонмо кең тилкелүү тармак аркылуу туташа турган жакын жердеги түзмөктөрдү аныктай алат"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Тандалган NFC төлөм кызматы жөнүндө маалымат"</string>
@@ -553,11 +551,11 @@
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"экранды бөгөттөөнүн татаалдык деңгээлин суроо"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Колдонмого экранды бөгөттөөнүн татаалдыгын (татаал, орточо, оңой же такыр жок) үйрөнүүгө мүмкүнчүлүк берет. Татаалдык деңгээли сырсөздүн узундугу жана экранды бөгөттөөнүн түрү боюнча айырмаланат. Колдонмо экранды бөгөттөөнү белгилүү деңгээлге тууралоону колдонуучуларга сунуштай да алат, бирок колдонуучулар ага көңүл бурбай койсо болот. Сырсөздү колдонмо билбеши үчүн, экранды бөгөттөө сырсөзүн кадимки текстте сактоого болбойт."</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"биометрикалык аппаратты колдонуу"</string>
- <string name="permdesc_useBiometric" msgid="7502858732677143410">"Колдонмого аныктыгын текшерүү үчүн, биометрикалык аппаратты пайдалануу мүмкүндүгүн берет"</string>
+ <string name="permdesc_useBiometric" msgid="7502858732677143410">"Колдонмого аныктыгын текшерүү үчүн биометрикалык аппаратты пайдалануу мүмкүндүгүн берет"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"манжа изинин аппараттык камсыздоосун башкаруу"</string>
<string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Колдонмого пайдалануу үчүн манжа изинин үлгүлөрүн кошуу жана жок кылуу мүмкүндүгүн берет."</string>
<string name="permlab_useFingerprint" msgid="1001421069766751922">"манжа изинин аппараттык камсыздоосун колдонуу"</string>
- <string name="permdesc_useFingerprint" msgid="412463055059323742">"Колдонмого аныктыгын текшерүү үчүн, манжа изинин аппараттык камсыздоосун пайдалануу мүмкүндүгүн берет"</string>
+ <string name="permdesc_useFingerprint" msgid="412463055059323742">"Колдонмого аныктыгын текшерүү үчүн манжа изинин аппараттык камсыздоосун пайдалануу мүмкүндүгүн берет"</string>
<string name="permlab_audioWrite" msgid="8501705294265669405">"музыка жыйнагыңызды өчүрүү"</string>
<string name="permdesc_audioWrite" msgid="8057399517013412431">"Колдонмого музыка жыйнагыңызды өзгөртүүгө мүмкүнчүлүк берет."</string>
<string name="permlab_videoWrite" msgid="5940738769586451318">"видео жыйнагыңызды өзгөртүү"</string>
@@ -1374,7 +1372,7 @@
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Мүчүлүштүктөрдү зымсыз оңдоону өчүрүү үчүн таптап коюңуз"</string>
<string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Мүчүлүштүктөрдү Wi-Fi аркылуу оңдоону өчүрүңүз."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Сыноо программасынын режими иштетилди"</string>
- <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Сыноо программасынын режимин өчүрүү үчүн, баштапкы жөндөөлөргө кайтарыңыз."</string>
+ <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Сыноо программасынын режимин өчүрүү үчүн баштапкы жөндөөлөргө кайтарыңыз."</string>
<string name="console_running_notification_title" msgid="6087888939261635904">"Сериялык консоль иштетилди"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Майнаптуулугуна таасири тиет. Аны өчүрүү үчүн операциялык тутумду жүктөгүчтү текшериңиз."</string>
<string name="usb_contaminant_detected_title" msgid="4359048603069159678">"USB портунда суюктук же урандылар бар"</string>
@@ -1679,12 +1677,12 @@
<string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Графикалык ачкычыңызды <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес чийдиңиз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> ийгиликсиз аракеттен кийин, Android TV түзмөгүңүздүн кулпусун электрондук почта аккаунтуңуз менен ачышыңыз керек болот.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секунддан кийин кайталап көрүңүз."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Графикалык ачкычты <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес көрсөттүңүз. <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу туура эмес көрсөтүлгөндөн кийин, телефондун кулпусун ачуу үчүн Google аккаунтуңузга кирүүгө туура келет.\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g> секундадан кийин кайталап көрсөңүз болот."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
- <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Алып салуу"</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Өчүрүү"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Сунушталган деңгээлден да катуулатып уккуңуз келеби?\n\nМузыканы узакка чейин катуу уксаңыз, угууңуз начарлап кетиши мүмкүн."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Ыкчам иштетесизби?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз."</string>
<string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Атайын мүмкүнчүлүктөрдүн ыкчам баскычын иштетесизби?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Атайын мүмкүнчүлүктөр функциясын иштетүү үчүн, үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nУчурдагы функциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТандалган функцияларды өзгөртүү үчүн, Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Атайын мүмкүнчүлүктөр функциясын иштетүү үчүн, үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nУчурдагы функциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТандалган функцияларды өзгөртүү үчүн Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"<xliff:g id="SERVICE">%1$s</xliff:g> ыкчам баскычын иштетесизби?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"<xliff:g id="SERVICE">%1$s</xliff:g> кызматын иштетүү үчүн, үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nБаскычтардын ушул айкалышын башка функцияга дайындоо үчүн, Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Батареяны үнөмдөгүч режиминде Караңгы тема күйгүзүлүп, фондогу аракеттер, айрым визуалдык эффекттер жана \"Окей, Google\" сыяктуу функциялар чектелип же өчүрүлөт\n\n"<annotation id="url">"Кеңири маалымат"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Батареяны үнөмдөгүч режиминде Караңгы тема күйгүзүлүп, фондогу аракеттер, айрым визуалдык эффекттер жана \"Окей, Google\" сыяктуу функциялар чектелип же өчүрүлөт."</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>
@@ -1933,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Жабуу"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Жооп берүү"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Видео"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Четке кагуу"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Чалууну бүтүрүү"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Кирүүчү чалуу"</string>
@@ -2076,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Бул билдирменин маанилүүлүгү Үнсүз болуп төмөндөтүлдү. Пикир билдирүү үчүн таптап коюңуз."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Бул билдирменин маанилүүлүгү жогорулатылды. Пикир билдирүү үчүн таптап коюңуз."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Бул билдирменин маанилүүлүгү төмөндөтүлдү. Пикир билдирүү үчүн таптап коюңуз."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Жакшыр-ган бил-ди байкап көрүү"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Сунушталган аракеттерди, жоопторду жана башка маалыматты ала берүү үчүн жакшыртылган билдирмелерди күйгүзүңүз. Android\'дин Ыңгайлаштырылуучу билдирмелери колдоого алынбай калды."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Күйгүзүү"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Азыр эмес"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Кененирээк"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Жакшыртылган билдирмелер бардык билдирмелердин мазмунун, ошондой эле байланыштардын аты-жөнү жана билдирүүлөр сыяктуу жеке маалыматты окуй алат. Мындан тышкары, билдирмелерди жаап же телефон чалууларына жооп берүү сыяктуу билдирмелердеги баскычтарды баса алат.\n\nБул функция Маанилүү жазышуулар режимин күйгүзүп же өчүрүп, ошондой эле анын жөндөөлөрүн өзгөртө алат."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Режимдин адаттагы билдирмеси"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батарея кубаттоого чейин отуруп калышы мүмкүн"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Батареянын отуруп калбашы үчүн Батареяны үнөмдөгүч режими иштетилди"</string>
@@ -2140,7 +2131,7 @@
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"сүрөт жөнөттү"</string>
<string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Жазышуу"</string>
- <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Топтук маек"</string>
+ <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Топтошуп жазышуу"</string>
<string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string>
<string name="resolver_personal_tab" msgid="2051260504014442073">"Жеке"</string>
<string name="resolver_work_tab" msgid="2690019516263167035">"Жумуш"</string>
@@ -2271,18 +2262,14 @@
<string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
<string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
- <string name="window_magnification_prompt_title" msgid="2876703640772778215">"Жаңы чоңойтуу жөндөөлөрү"</string>
+ <string name="window_magnification_prompt_title" msgid="2876703640772778215">"Чоңойтуу функциясынын жаңы жөндөөлөрү"</string>
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Эми экрандын бир бөлүгүн чоңойто аласыз"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Жөндөөлөрдөн күйгүзүү"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Жабуу"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Түзмөктүн микрофонунун кулпусун ачуу"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Түзмөктүн камерасынын кулпусун ачуу"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> жана башка бардык колдонмолор менен кызматтар үчүн"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Бөгөттөн чыгаруу"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Сенсордун купуялыгы"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Колдонмонун сүрөтчөсү"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Колдонмонун брендинин сүрөтү"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 0b53554..caa9154 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ຕົວປະຢັດແບັດເຕີຣີຈະເປີດໃຊ້ຮູບແບບສີສັນມືດ ແລະ ຈຳກັດ ຫຼື ປິດການເຄື່ອນໄຫວໃນພື້ນຫຼັງ, ເອັບເຟັກດ້ານພາບບາງຢ່າງ ແລະ ຄຸນສົມບັດຕ່າງໆ ເຊັ່ນ: “Ok Google”\n\n"<annotation id="url">"ສຶກສາເພີ່ມເຕີມ"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"ຕົວປະຢັດແບັດເຕີຣີຈະເປີດໃຊ້ຮູບແບບສີສັນມືດ ແລະ ຈຳກັດ ຫຼື ປິດການເຄື່ອນໄຫວໃນພື້ນຫຼັງ, ເອັບເຟັກດ້ານພາບບາງຢ່າງ ແລະ ຄຸນສົມບັດຕ່າງໆ ເຊັ່ນ: “Ok Google”."</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>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"ປິດ"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"ຮັບສາຍ"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"ວິດີໂອ"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"ປະຕິເສດ"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"ວາງສາຍ"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"ສາຍໂທເຂົ້າ"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ການແຈ້ງເຕືອນນີ້ຖືກຫຼຸດລະດັບເປັນປິດສຽງແລ້ວ. ແຕະເພື່ອສົ່ງຄຳຕິຊົມ."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"ການແຈ້ງເຕືອນນີ້ຖືກເລື່ອນລະດັບຂຶ້ນແລ້ວ. ແຕະເພື່ອສົ່ງຄຳຕິຊົມ."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"ການແຈ້ງເຕືອນນີ້ຖືກຫຼຸດລະດັບລົງແລ້ວ. ແຕະເພື່ອສົ່ງຄຳຕິຊົມ."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"ລອງໃຊ້ການແຈ້ງເຕືອນທີ່ປັບປຸງໃຫ້ດີຂຶ້ນ"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"ກະລຸນາເປີດໃຊ້ການແຈ້ງເຕືອນທີ່ປັບປຸງໃຫ້ດີຂຶ້ນເພື່ອສືບຕໍ່ຮັບຄຳສັ່ງທີ່ແນະນຳ, ການຕອບກັບ ແລະ ອື່ນໆ. ບໍ່ຮອງຮັບການແຈ້ງເຕືອນແບບປັບຕົວໄດ້ຂອງ Android ອີກຕໍ່ໄປແລ້ວ."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"ເປີດໃຊ້"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"ບໍ່ຟ້າວເທື່ອ"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"ສຶກສາເພີ່ມເຕີມ"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"ການແຈ້ງເຕືອນທີ່ປັບປຸງໃຫ້ດີຂຶ້ນສາມາດອ່ານເນື້ອຫາການແຈ້ງເຕືອນທັງໝົດໄດ້, ຮວມທັງຂໍ້ມູນສ່ວນຕົວ ເຊັ່ນ: ຊື່ຜູ້ຕິດຕໍ່ ແລະ ຂໍ້ຄວາມຕ່າງໆ. ນອກຈາກນັ້ນ, ຄຸນສົມບັດນີ້ຍັງສາມາດປິດການແຈ້ງເຕືອນໄວ້ ຫຼື ໃຊ້ຄຳສັ່ງຕ່າງໆຢູ່ປຸ່ມໃນການແຈ້ງເຕືອນໄດ້ນຳ ເຊັ່ນ: ການຮັບສາຍໂທລະສັບ.\n\nຄຸນສົມບັດນີ້ສາມາດເປີດ ຫຼື ປິດໂໝດສຳຄັນ ແລະ ປ່ຽນການຕັ້ງຄ່າທີ່ກ່ຽວຂ້ອງໄດ້ນຳ."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ການແຈ້ງເຕືອນຂໍ້ມູນໂໝດກິດຈະວັດປະຈຳວັນ"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ແບັດເຕີຣີອາດໝົດກ່ອນການສາກຕາມປົກກະຕິ"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ເປີດຕົວປະຢັດແບັດເຕີຣີເພື່ອຂະຫຍາຍອາຍຸແບັດເຕີຣີ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 5a0b665..d06bc16 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1898,10 +1898,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Akumuliatoriaus tausojimo priemonė įjungia tamsiąją temą ir apriboja arba išjungia veiklą fone, kai kuriuos vizualinius efektus ir funkcijas, pvz., „Ok Google“\n\n"<annotation id="url">"Sužinokite daugiau"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Akumuliatoriaus tausojimo priemonė įjungia tamsiąją temą ir apriboja arba išjungia veiklą fone, kai kuriuos vizualinius efektus ir funkcijas, pvz., „Ok Google“."</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>
@@ -1993,6 +1991,8 @@
<string name="close_button_text" msgid="10603510034455258">"Uždaryti"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Atsakyti"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Atmesti"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Baigti pok."</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Gaunamasis skambutis"</string>
@@ -2140,18 +2140,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Šio pranešimo svarba sumažinta iki begarsio lygio. Palieskite, kad pateiktumėte atsiliepimą."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Šio pranešimo svarba padidinta. Palieskite, kad pateiktumėte atsiliepimą."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Šio pranešimo svarba sumažinta. Palieskite, kad pateiktumėte atsiliepimą."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Išb. patobulintus pranešimus"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Norėdami toliau gauti siūlomus veiksmus, atsakymus ir daugiau, įjunkite patobulintus pranešimus. „Android“ prisitaikantys pranešimai nebepalaikomi."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Įjungti"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Ne dabar"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Sužinokite daugiau"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Patobulintų pranešimų funkcija gali skaityti visų pranešimų turinį, įskaitant asmens informaciją (pvz., kontaktų vardus ir pranešimus). Ši funkcija taip pat gali atsisakyti pranešimų ar imtis veiksmų su pranešimuose esančiais mygtukais, pvz., atsakyti į telefono skambučius.\n\nBe to, ši funkcija gali įjungti arba išjungti svarbiausių pokalbių režimą ir pakeisti susijusius nustatymus."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Veiksmų sekos režimo informacijos pranešimas"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Akumuliatoriaus energija gali išsekti prieš įprastą įkrovimą"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Akumuliatoriaus tausojimo priemonė suaktyvinta, kad akumuliatorius veiktų ilgiau"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index b4edfc6..7e54fb9 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1875,10 +1875,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Akumulatora enerģijas taupīšanas režīmā tiek ieslēgts tumšais motīvs un ierobežotas vai izslēgtas darbības fonā, konkrēti vizuālie efekti un tādas funkcijas kā īsinājumvārda “Hey Google” atpazīšana.\n\n"<annotation id="url">"Uzzināt vairāk"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Akumulatora enerģijas taupīšanas režīmā tiek ieslēgts tumšais motīvs un ierobežotas vai izslēgtas darbības fonā, konkrēti vizuālie efekti un tādas funkcijas kā īsinājumvārda “Hey Google” atpazīšana."</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>
@@ -1962,6 +1960,8 @@
<string name="close_button_text" msgid="10603510034455258">"Aizvērt"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Atbildēt"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Noraidīt"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Pārtraukt"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Ienākošais zvans"</string>
@@ -2107,18 +2107,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Šī paziņojuma svarīgums tika pazemināts, un paziņojums tiks rādīts bez skaņas. Lai sniegtu atsauksmes, pieskarieties."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Šī paziņojuma rangs tika paaugstināts. Lai sniegtu atsauksmes, pieskarieties."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Šī paziņojuma rangs tika pazemināts. Lai sniegtu atsauksmes, pieskarieties."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Uzlabotie paziņojumi"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Lai arī turpmāk saņemtu darbību un atbilžu ieteikumus un citu saturu, ieslēdziet uzlabotos paziņojumus. Android adaptīvie paziņojumi vairs netiek atbalstīti."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Ieslēgt"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Vēlāk"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Uzzināt vairāk"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Funkcija “Uzlabotie paziņojumi” var lasīt visu paziņojumu saturu, tostarp personas informāciju, piemēram, kontaktpersonu vārdus un ziņojumus. Šī funkcija var arī noraidīt paziņojumus un izmantot paziņojumos esošās pogas darbību veikšanai, piemēram, atbildēt uz tālruņa zvaniem.\n\nTurklāt šī funkcija var ieslēgt un izslēgt režīmu Prioritāte un mainīt ar to saistītos iestatījumus."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Informatīvs paziņojums par akumulatoru"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Akumulators var izlādēties pirms parastā uzlādes laika"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Aktivizēts akumulatora enerģijas taupīšanas režīms, lai palielinātu akumulatora darbības ilgumu"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index fce03ff..1e4ede2 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -297,7 +297,7 @@
<string name="notification_channel_accessibility_security_policy" msgid="1727787021725251912">"Користење на пристапноста"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> користи батерија"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> апликации користат батерија"</string>
- <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Допрете за детали за батеријата и потрошениот сообраќај"</string>
+ <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Допрете за детали за батеријата и потрошениот интернет"</string>
<string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="8974401416068943888">"Безбеден режим"</string>
<string name="android_system_label" msgid="5974767339591067210">"Систем Android"</string>
@@ -1160,7 +1160,7 @@
<string name="redo" msgid="7231448494008532233">"Повтори"</string>
<string name="autofill" msgid="511224882647795296">"Автоматско пополнување"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"Избор на текст"</string>
- <string name="addToDictionary" msgid="8041821113480950096">"Додај во речник"</string>
+ <string name="addToDictionary" msgid="8041821113480950096">"Додајте во речникот"</string>
<string name="deleteText" msgid="4200807474529938112">"Избриши"</string>
<string name="inputMethod" msgid="1784759500516314751">"Метод на внес"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Дејства со текст"</string>
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"„Штедачот на батерија“ вклучува темна тема и исклучува или ограничува активност во заднина, некои визуелни ефекти и функции како „Ok Google“\n\n"<annotation id="url">"Дознајте повеќе"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"„Штедачот на батерија“ вклучува темна тема и исклучува или ограничува активност во заднина, некои визуелни ефекти и функции како „Ok Google“."</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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Затвори"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Одговори"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Одбиј"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Спушти"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Дојдовен повик"</string>
@@ -1948,7 +1948,7 @@
<string name="user_creation_adding" msgid="7305185499667958364">"Дозволувате <xliff:g id="APP">%1$s</xliff:g> да создаде нов корисник со <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="language_selection_title" msgid="52674936078683285">"Додајте јазик"</string>
<string name="country_selection_title" msgid="5221495687299014379">"Претпочитувања за регион"</string>
- <string name="search_language_hint" msgid="7004225294308793583">"Внеси име на јазик"</string>
+ <string name="search_language_hint" msgid="7004225294308793583">"Внесете име на јазик"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Предложени"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Сите јазици"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"Сите региони"</string>
@@ -1992,7 +1992,7 @@
<string name="app_category_productivity" msgid="1844422703029557883">"Продуктивност"</string>
<string name="app_category_accessibility" msgid="6643521607848547683">"Пристапност"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Простор на уредот"</string>
- <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Отстранување грешки на USB"</string>
+ <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Отстранување грешки преку USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"час"</string>
<string name="time_picker_minute_label" msgid="8307452311269824553">"минута"</string>
<string name="time_picker_header_text" msgid="9073802285051516688">"Постави време"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Приоритетноста на известувањево е намалена на „Тивко“. Допрете за да дадете повратни информации."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Известувањево е рангирано повисоко. Допрете за да дадете повратни информации."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Известувањево е рангирано пониско. Допрете за да дадете повратни информации."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Пробај „Подобрени известувања“"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Вклучете ги „Подобрените известувања“ за да продолжите да добивате предлози за дејства, одговори и слично. „Приспособливите известувања на Android“ веќе не се достапни."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Вклучи"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Не сега"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Дознајте повеќе"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"„Подобрените известувања“ може да ги читаат сите содржини од известувањата, вклучително и личните податоци, како што се имињата на контактите и пораките. Функцијава ќе може и да отфрла известувања или да ги користи копчињата во известувањата, како на пр., да одговара на телефонски повици.\n\nФункцијава може и да го вклучува или исклучува „Приоритетниот режим“ и да ги менува поврзаните поставки."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Известување за информации за режимот за рутини"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батеријата може да се потроши пред вообичаеното време за полнење"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Активиран е „Штедачот на батерија“ за да се продолжи траењето на батеријата"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index e50fb7bd..1a27c20 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -307,7 +307,7 @@
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"നിങ്ങളുടെ കോൺടാക്റ്റുകൾ ആക്സസ്സ് ചെയ്യുക"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"ലൊക്കേഷൻ"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്സസ് ചെയ്യാൻ"</string>
- <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string>
+ <string name="permgrouplab_calendar" msgid="6426860926123033230">"കലണ്ടർ"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"നിങ്ങളുടെ കലണ്ടർ ആക്സസ്സ് ചെയ്യുക"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
<string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS സന്ദേശങ്ങൾ അയയ്ക്കുകയും കാണുകയും ചെയ്യുക"</string>
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"സമീപമുള്ള Bluetooth ഉപകരണങ്ങൾ കണ്ടെത്താനും ജോടിയാക്കാനും ആപ്പിനെ അനുവദിക്കുന്നു"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"ജോടിയായ Bluetooth ഉപകരണങ്ങളിലേക്ക് കണക്റ്റ് ചെയ്യൂ"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"ജോടിയാക്കിയ Bluetooth ഉപകരണങ്ങളിലേക്ക് കണക്റ്റ് ചെയ്യാൻ ആപ്പിനെ അനുവദിക്കുന്നു"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"സമീപമുള്ള Bluetooth ഉപകരണങ്ങളിലേക്ക് പരസ്യം ചെയ്യൂ"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"സമീപമുള്ള Bluetooth ഉപകരണങ്ങളിലേക്ക് പരസ്യം ചെയ്യാൻ ആപ്പിനെ അനുവദിക്കുന്നു"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"സമീപമുള്ള അൾട്രാ-വെെഡ്ബാൻഡ് ഉപകരണങ്ങൾ തമ്മിലുള്ള ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കൂ"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"സമീപമുള്ള അൾട്രാ-വെെഡ്ബാൻഡ് ഉപകരണങ്ങൾ തമ്മിലുള്ള ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാൻ ആപ്പിനെ അനുവദിക്കുക"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"തിരഞ്ഞെടുത്ത NFC പേയ്മെന്റ് സേവനത്തെ സംബന്ധിച്ച വിവരങ്ങൾ"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ബാറ്ററി ലാഭിക്കൽ, ഡാർക്ക് തീം ഓണാക്കുന്നു, പശ്ചാത്തല പ്രവർത്തനവും ചില വിഷ്വൽ ഇഫക്റ്റുകളും “Ok Google” പോലുള്ള ഫീച്ചറുകളും നിയന്ത്രിക്കുകയോ ഓഫാക്കുകയോ ചെയ്യുന്നു\n\n"<annotation id="url">"കൂടുതലറിയുക"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"ബാറ്ററി ലാഭിക്കൽ, ഡാർക്ക് തീം ഓണാക്കുന്നു, പശ്ചാത്തല പ്രവർത്തനവും ചില വിഷ്വൽ ഇഫക്റ്റുകളും “Ok Google” പോലുള്ള ഫീച്ചറുകളും നിയന്ത്രിക്കുകയോ ഓഫാക്കുകയോ ചെയ്യുന്നു."</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>
@@ -1933,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"അവസാനിപ്പിക്കുക"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"മറുപടി നൽകുക"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"നിരസിക്കുക"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"കോൾ നിർത്തുക"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"ഇൻകമിംഗ് കോൾ"</string>
@@ -1950,7 +1948,7 @@
<string name="user_creation_adding" msgid="7305185499667958364">"<xliff:g id="ACCOUNT">%2$s</xliff:g> എന്ന അക്കൗണ്ട് ഉപയോഗിച്ച് പുതിയ ഉപയോക്താവിനെ സൃഷ്ടിക്കാൻ <xliff:g id="APP">%1$s</xliff:g> എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="language_selection_title" msgid="52674936078683285">"ഒരു ഭാഷ ചേർക്കുക"</string>
<string name="country_selection_title" msgid="5221495687299014379">"മേഖലാ മുൻഗണന"</string>
- <string name="search_language_hint" msgid="7004225294308793583">"ഭാഷയുടെ പേര് ടൈപ്പുചെയ്യുക"</string>
+ <string name="search_language_hint" msgid="7004225294308793583">"ഭാഷ ടൈപ്പ് ചെയ്യുക"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"നിര്ദ്ദേശിച്ചത്"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"എല്ലാ ഭാഷകളും"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"എല്ലാ പ്രദേശങ്ങളും"</string>
@@ -2076,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ഈ അറിയിപ്പിനെ നിശബ്ദമാക്കി തരം താഴ്ത്തി. ഫീഡ്ബാക്ക് നൽകാൻ ടാപ്പ് ചെയ്യുക."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"ഈ അറിയിപ്പിന് ഉയർന്ന റാങ്ക് നൽകി. ഫീഡ്ബാക്ക് നൽകാൻ ടാപ്പ് ചെയ്യുക."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"ഈ അറിയിപ്പിന് താഴ്ന്ന റാങ്ക് നൽകി. ഫീഡ്ബാക്ക് നൽകാൻ ടാപ്പ് ചെയ്യുക."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"മെച്ചപ്പെടുത്തിയ അറിയിപ്പുകൾ പരീക്ഷിക്കൂ"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"നിർദ്ദേശിച്ച പ്രവർത്തനങ്ങളും മറുപടികളും മറ്റും ലഭിക്കുന്നത് തുടരാൻ, മെച്ചപ്പെടുത്തിയ അറിയിപ്പുകൾ ഓണാക്കുക. Android അഡാപ്റ്റീവ് അറിയിപ്പുകൾക്ക് ഇനി പിന്തുണയില്ല."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"ഓണാക്കുക"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"ഇപ്പോൾ വേണ്ട"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"കൂടുതലറിയുക"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"മെച്ചപ്പെടുത്തിയ അറിയിപ്പുകൾക്ക്, കോൺടാക്റ്റ് പേരുകളും സന്ദേശങ്ങളും പോലുള്ള വ്യക്തിപരമായ വിവരങ്ങൾ ഉൾപ്പെടെ എല്ലാ അറിയിപ്പ് ഉള്ളടക്കവും വായിക്കാനാകും. അറിയിപ്പുകൾ ഡിസ്മിസ് ചെയ്യാനോ ഫോൺ കോളുകൾക്ക് മറുപടി നൽകുന്നത് പോലെ അറിയിപ്പുകളിലെ ബട്ടണുകളിൽ നടപടിയെടുക്കാനോ ഈ ഫീച്ചറിന് കഴിയും.\n\nമുൻഗണനാ മോഡ് ഓണാക്കാനോ ഓഫാക്കാനോ ബന്ധപ്പെട്ട ക്രമീകരണം മാറ്റാനോ ഈ ഫീച്ചറിന് കഴിയും."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ദിനചര്യ മോഡ് വിവരത്തെ കുറിച്ചുള്ള അറിയിപ്പ്"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"സാധാരണയുള്ളതിലും നേരത്തെ ബാറ്ററിയുടെ ചാർജ് തീർന്നേക്കാം"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ബാറ്ററി ലൈഫ് വര്ദ്ധിപ്പിക്കാൻ, ബാറ്ററി ലാഭിക്കൽ സജീവമാക്കി"</string>
@@ -2275,14 +2267,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"നിങ്ങളുടെ സ്ക്രീനിന്റെ ഒരു ഭാഗം ഇപ്പോൾ മാഗ്നിഫൈ ചെയ്യാനാകും"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ക്രമീകരണത്തിൽ ഓണാക്കുക"</string>
<string name="dismiss_action" msgid="1728820550388704784">"ഡിസ്മിസ് ചെയ്യുക"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"ഉപകരണ മൈക്രോഫോൺ അൺബ്ലോക്ക് ചെയ്യുക"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"ഉപകരണ ക്യാമറ അൺബ്ലോക്ക് ചെയ്യുക"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> എന്നതിനും എല്ലാ ആപ്പുകൾക്കും സേവനങ്ങൾക്കും"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"അൺബ്ലോക്ക് ചെയ്യുക"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"സെൻസർ സ്വകാര്യത"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ആപ്പ് ഐക്കൺ"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"അപ്ലിക്കേഷൻ ബ്രാൻഡിംഗ് ഇമേജ്"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 87ee801..a3a3cdb 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1693,7 +1693,7 @@
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g>-д таны төхөөрөмжийг бүрэн хянахыг зөвшөөрөх үү?"</string>
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"Хэрэв та <xliff:g id="SERVICE">%1$s</xliff:g>-г асаавал таны төхөөрөмж өгөгдлийн шифрлэлтийг сайжруулахын тулд таны дэлгэцийн түгжээг ашиглахгүй."</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"Бүрэн хянах нь таны хандалтын үйлчилгээний шаардлагад тусалдаг аппуудад тохиромжтой боловч ихэнх аппад тохиромжгүй байдаг."</string>
- <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Харах болон хянах дэлгэц"</string>
+ <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Дэлгэцийг харах ба хянах"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Энэ нь дэлгэц дээрх бүх контентыг унших болон контентыг бусад аппад харуулах боломжтой."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Үйлдлийг харах болон гүйцэтгэх"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Энэ нь таны апп болон техник хангамжийн мэдрэгчтэй хийх харилцан үйлдлийг хянах болон таны өмнөөс апптай харилцан үйлдэл хийх боломжтой."</string>
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Батарей хэмнэгч нь Бараан загварыг асааж, дэвсгэрийн үйл ажиллагаа, зарим визуал эффект болон “Hey Google” зэрэг онцлогуудыг хязгаарлаж эсвэл унтраана\n\n"<annotation id="url">"Нэмэлт мэдээлэл авах"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Батарей хэмнэгч нь Бараан загварыг асааж, дэвсгэрийн үйл ажиллагаа, зарим визуал эффект болон “Hey Google” зэрэг онцлогуудыг хязгаарлаж эсвэл унтраана."</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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Хаах"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Хариулах"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Татгалзах"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Таслах"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Ирсэн дуудлага"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Энэ мэдэгдлийг Чимээгүй болгож зэргийг нь бууруулсан байна. Санал хүсэлт өгөхийн тулд товшино уу."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Энэ мэдэгдлийг дээгүүр зэрэглэсэн байна. Санал хүсэлт өгөхийн тулд товшино уу."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Энэ мэдэгдлийг доогуур зэрэглэсэн байна. Санал хүсэлт өгөхийн тулд товшино уу."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Сайжруулсан мэдэгдлийг турших"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Санал болгосон үйлдэл, хариу болон илүү ихийг үргэлжлүүлэн авахын тулд сайжруулсан мэдэгдлийг асаана уу. Android-н Орчинтой тохирсон мэдэгдлийг дэмжихээ больсон байна."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Асаах"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Одоо биш"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Нэмэлт мэдээлэл авах"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Сайжруулсан мэдэгдэл нь харилцагчийн нэр, мессеж зэрэг хувийн мэдээллийг оруулаад бүх мэдэгдлийн контентыг унших боломжтой. Энэ онцлог мөн мэдэгдлийг хаах эсвэл утасны дуудлагад хариулах гэх мэт мэдэгдэл дэх товчлуур дээр үйлдэл хийх боломжтой.\n\nЭнэ онцлог мөн Чухал горимыг асаах, унтраах болон холбогдох тохиргоог өөрчлөх боломжтой."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Хэвшлийн горимын мэдээллийн мэдэгдэл"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батарей ихэвчлэн цэнэглэдэг хугацаанаас өмнө дуусаж болзошгүй"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Батарейн ажиллах хугацааг уртасгахын тулд Батарей хэмнэгчийг идэвхжүүллээ"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 01b767e..24ed1b6 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -277,7 +277,7 @@
<string name="notification_hidden_text" msgid="2835519769868187223">"नवीन सूचना"</string>
<string name="notification_channel_virtual_keyboard" msgid="6465975799223304567">"व्हर्च्युअल कीबोर्ड"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"वास्तविक कीबोर्ड"</string>
- <string name="notification_channel_security" msgid="8516754650348238057">"सुरक्षितता"</string>
+ <string name="notification_channel_security" msgid="8516754650348238057">"सुरक्षा"</string>
<string name="notification_channel_car_mode" msgid="2123919247040988436">"कार मोड"</string>
<string name="notification_channel_account" msgid="6436294521740148173">"खाते स्थिती"</string>
<string name="notification_channel_developer" msgid="1691059964407549150">"डेव्हलपर मेसेज"</string>
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"ॲपला जवळपासची ब्लूटूथ डिव्हाइस शोधण्यासाठी आणि ती पेअर करण्यासाठी अनुमती देते"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"पेअर केलेल्या ब्लूटूथ डिव्हाइसशी कनेक्ट करा"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"पेअर केलेल्या ब्लूटूथ डिव्हाइसशी कनेक्ट करण्यासाठी ॲपला अनुमती द्या"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"जवळपासच्या ब्लूटूथ डिव्हाइसवर जाहिरात करा"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"जवळपासच्या ब्लूटूथ डिव्हाइसवर जाहिरात करण्याची ॲपला परवानगी देते"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"जवळच्या अल्ट्रा-वाइडबँड डिव्हाइसदरम्यानचे संबंधित स्थान निर्धारित करा"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ॲपला जवळच्या अल्ट्रा-वाइडबँड डिव्हाइसदरम्यानचे संबंधित स्थान निर्धारित करण्याची अनुमती द्या"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"प्राधान्यकृत NFC पेमेंट सेवा माहिती"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"बॅटरी सेव्हर गडद थीम सुरू करते आणि बॅकग्राउंड ॲक्टिव्हिटी, काही व्हिज्युअल इफेक्ट व “Ok Google” सारखी वैशिष्ट्ये मर्यादित किंवा बंद करते\n\n"<annotation id="url">"अधिक जाणून घ्या"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"बॅटरी सेव्हर गडद थीम सुरू करते आणि बॅकग्राउंड ॲक्टिव्हिटी, काही व्हिज्युअल इफेक्ट व “Ok Google” सारखी वैशिष्ट्ये मर्यादित किंवा बंद करते."</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>
@@ -1933,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"बंद करा"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"उत्तर द्या"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"नकार द्या"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"कॉल बंद करा"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"इनकमिंग कॉल"</string>
@@ -2076,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ही सूचना सायलंट करण्यात आली आहे. फीडबॅक देण्यासाठी टॅप करा."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"हा सूचनेला उच्च रँक करण्यात आले. फीडबॅक देण्यासाठी टॅप करा."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"या सूचनेला कमी रँक करण्यात आले. फीडबॅक देण्यासाठी टॅप करा."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"वर्धित सूचना वापरून पहा"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"सुचवलेल्या कृती, उत्तरे आणि आणखी बरेच काही मिळवत राहण्यासाठी, वर्धित सूचना सुरू करा. Android अॅडॅप्टिव्ह सूचना यांना आता सपोर्ट नाही."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"सुरू करा"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"आता नको"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"अधिक जाणून घ्या"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"संपर्कांची नावे आणि मेसेज यांसारख्या वैयक्तिक माहितीच्या समावेशासह वर्धित सूचना या सर्व सूचनांचा आशय वाचू शकतात. हे वैशिष्ट्य सूचना डिसमिस करू शकते किंवा फोन कॉलना उत्तर देण्यासारख्या सूचनांमधील बटणवर कृतीदेखील करू शकते.\n\nहे वैशिष्ट्य प्राधान्य मोड सुरू किंवा बंद करू शकते आणि संबंधित सेटिंग्जदेखील बदलू शकते."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"दिनक्रम मोडची माहिती सूचना"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"चार्जिंगची सामान्य पातळी गाठेपर्यंत कदाचित बॅटरी संपू शकते"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"बॅटरी लाइफ वाढवण्यासाठी बॅटरी सेव्हर सुरू केला आहे"</string>
@@ -2275,14 +2267,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"आता तुम्ही तुमच्या स्क्रीनचा एखादा भाग मॅग्निफाय करू शकता"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"सेटिंग्ज मध्ये सुरू करा"</string>
<string name="dismiss_action" msgid="1728820550388704784">"डिसमिस करा"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"डिव्हाइसचा मायक्रोफोन अनब्लॉक करा"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"डिव्हाइसचा कॅमेरा अनब्लॉक करा"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"सर्व <b><xliff:g id="APP">%s</xliff:g></b> आणि सर्व ॲप्स व सेवांसाठी"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"अनब्लॉक करा"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"सेन्सरशी संबंधित गोपनीयतेबाबत सूचना"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ॲप्लिकेशन आयकन"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"अॅप्लिकेशन ब्रॅंडिंग इमेज"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index d5fe785..0afa2f9 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Membenarkan apl menemukan dan berganding dengan peranti Bluetooth yang berdekatan"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"sambung kepada peranti Bluetooth yang digandingkan"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Membenarkan apl untuk menyambung kepada peranti Bluetooth yang digandingkan"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"buat siaran kpd peranti Bluetooth yang berdekatan"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Membenarkan apl menyiarkan kandungan kepada peranti Bluetooth yang berdekatan"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"tentukan kedudukan relatif antara peranti Ultrajalur Lebar berdekatan"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Benarkan apl menentukan kedudukan relatif antara peranti Ultrajalur Lebar berdekatan"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Maklumat Perkhidmatan Pembayaran NFC Pilihan"</string>
@@ -1854,10 +1852,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Dikemas kini oleh pentadbir anda"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Dipadamkan oleh pentadbir anda"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Penjimat Bateri menghidupkan Tema gelap dan mengehadkan atau mematikan aktiviti latar, sesetengah kesan visual dan ciri seperti \"Ok Google\"\n\n"<annotation id="url">"Ketahui lebih lanjut"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Penjimat Bateri menghidupkan Tema gelap dan mengehadkan atau mematikan aktiviti latar, sesetengah kesan visual dan ciri seperti \"Ok Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Untuk membantu mengurangkan penggunaan data, Penjimat Data menghalang sesetengah apl daripada menghantar atau menerima data di latar. Apl yang sedang digunakan boleh mengakses data tetapi mungkin tidak secara kerap. Perkara ini mungkin bermaksud bahawa imej tidak dipaparkan sehingga anda mengetik pada imej itu, contohnya."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Hidupkan Penjimat Data?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Hidupkan"</string>
@@ -1933,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Tutup"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Jawapan"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Tolak"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Tamatkan Panggilan"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Panggilan masuk"</string>
@@ -2076,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Pemberitahuan ini telah diturun taraf kepada Senyap. Ketik untuk memberikan maklum balas."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Pemberitahuan ini berada di kedudukan lebih tinggi. Ketik untuk memberikan maklum balas."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Pemberitahuan ini berada di kedudukan lebih rendah. Ketik untuk memberikan maklum balas."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Cuba pemberitahuan dipertingkatkan"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Untuk terus mendapatkan tindakan yang dicadangkan, balasan dan banyak lagi, hidupkan pemberitahuan yang dipertingkatkan. Pemberitahuan Boleh Suai Android tidak disokong lagi."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Hidupkan"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Bukan sekarang"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Ketahui lebih lanjut"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Pemberitahuan yang dipertingkatkan dapat membaca semua kandungan pemberitahuan, termasuk maklumat peribadi seperti nama kenalan dan mesej. Ciri ini juga dapat mengetepikan pemberitahuan atau mengambil tindakan pada butang dalam pemberitahuan, seperti menjawab panggilan telefon.\n\nCiri ini juga dapat menghidupkan atau mematikan mod Keutamaan dan menukar tetapan yang berkaitan."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Pemberitahuan maklumat Mod Rutin"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Bateri mungkin habis sebelum pengecasan biasa"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Penjimat Bateri diaktifkan untuk memanjangkan hayat bateri"</string>
@@ -2275,14 +2267,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Kini anda boleh membesarkan sebahagian skrin anda"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Hidupkan dalam Tetapan"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Tolak"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Nyahsekat mikrofon peranti"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Nyahsekat kamera peranti"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Untuk <b><xliff:g id="APP">%s</xliff:g></b> dan semua apl serta perkhidmatan"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Nyahsekat"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privasi Penderia"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikon aplikasi"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imej jenama aplikasi"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 5f6a26a5..be4bf4fa 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1108,8 +1108,8 @@
<item quantity="one">ပြီးခဲ့သည့် <xliff:g id="COUNT_0">%d</xliff:g> မိနစ်က</item>
</plurals>
<plurals name="duration_hours_relative" formatted="false" msgid="420434788589102019">
- <item quantity="other">ပြီးခဲ့သည့် <xliff:g id="COUNT_1">%d</xliff:g> နာရီက</item>
- <item quantity="one">ပြီးခဲ့သည့် <xliff:g id="COUNT_0">%d</xliff:g> နာရီက</item>
+ <item quantity="other">ပြီးခဲ့သည့် <xliff:g id="COUNT_1">%d</xliff:g> နာရီ</item>
+ <item quantity="one">ပြီးခဲ့သည့် <xliff:g id="COUNT_0">%d</xliff:g> နာရီ</item>
</plurals>
<plurals name="duration_days_relative" formatted="false" msgid="6056425878237482431">
<item quantity="other">ပြီးခဲ့သည့် <xliff:g id="COUNT_1">%d</xliff:g> ရက်က</item>
@@ -1692,12 +1692,12 @@
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"ပိတ်ထားသည်"</string>
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> ကို သင့်စက်အား အပြည့်အဝထိန်းချုပ်ခွင့် ပေးလိုပါသလား။"</string>
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"<xliff:g id="SERVICE">%1$s</xliff:g> ဖွင့်လိုက်ပါက သင်၏စက်သည် ဒေတာအသွင်ဝှက်ခြင်း ပိုကောင်းမွန်စေရန် သင့်ဖန်သားပြင်လော့ခ်ကို သုံးမည်မဟုတ်ပါ။"</string>
- <string name="accessibility_service_warning_description" msgid="291674995220940133">"အများသုံးစွဲနိုင်မှု လိုအပ်ချက်များအတွက် အထောက်အကူပြုသည့် အက်ပ်များကို အပြည့်အဝထိန်းချုပ်ခြင်းသည် သင့်လျော်သော်လည်း အက်ပ်အများစုအတွက် မသင့်လျော်ပါ။"</string>
- <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"မျက်နှာပြင်ကို ကြည့်ရှုပြီး ထိန်းချုပ်ပါ"</string>
+ <string name="accessibility_service_warning_description" msgid="291674995220940133">"အများသုံးစွဲနိုင်မှု လိုအပ်ချက်များအတွက် အထောက်အကူပြုသည့် အက်ပ်များအား အပြည့်အဝ ထိန်းချုပ်ခွင့်ပေးခြင်းသည် သင့်လျော်သော်လည်း အက်ပ်အများစုအတွက် မသင့်လျော်ပါ။"</string>
+ <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"ဖန်သားပြင်ကို ကြည့်ရှုထိန်းချုပ်ခြင်း"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"၎င်းသည် မျက်နှာပြင်ပေါ်ရှိ အကြောင်းအရာများအားလုံးကို ဖတ်နိုင်ပြီး အခြားအက်ပ်များအပေါ်တွင် ထိုအကြောင်းအရာကို ဖော်ပြနိုင်သည်။"</string>
- <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"လုပ်ဆောင်ချက်များကို ကြည့်ရှုလုပ်ဆောင်ပါ"</string>
+ <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"လုပ်ဆောင်ချက်များကို ကြည့်ရှုဆောင်ရွက်ခြင်း"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"၎င်းသည် အက်ပ်တစ်ခု သို့မဟုတ် အာရုံခံကိရိယာကို အသုံးပြု၍ သင့်ပြန်လှန်တုံ့ပြန်မှုများကို မှတ်သားနိုင်ပြီး သင့်ကိုယ်စား အက်ပ်များနှင့် ပြန်လှန်တုံ့ပြန်နိုင်သည်။"</string>
- <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"ခွင့်ပြု"</string>
+ <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"ခွင့်ပြုရန်"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"ပယ်ရန်"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ဝန်ဆောင်မှုကို စတင်အသုံးပြုရန် တို့ပါ−"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"အများသုံးစွဲနိုင်မှု ခလုတ်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုများကို ရွေးပါ"</string>
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"‘ဘက်ထရီ အားထိန်း’ က ‘မှောင်သည့် အပြင်အဆင်’ ကို ဖွင့်ပြီး နောက်ခံလုပ်ဆောင်ချက်၊ ပြသမှုဆိုင်ရာ အထူးပြုလုပ်ချက်အချို့နှင့် “Ok Google” ကဲ့သို့ ဝန်ဆောင်မှုများကို ကန့်သတ်သည် (သို့) ပိတ်သည်\n\n"<annotation id="url">"ပိုမိုလေ့လာရန်"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"‘ဘက်ထရီ အားထိန်း’ က ‘မှောင်သည့် အပြင်အဆင်’ ကို ဖွင့်ပြီး နောက်ခံလုပ်ဆောင်ချက်၊ ပြသမှုဆိုင်ရာ အထူးပြုလုပ်ချက်အချို့နှင့် “Ok Google” ကဲ့သို့ ဝန်ဆောင်မှုများကို ကန့်သတ်သည် (သို့) ပိတ်သည်။"</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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"ပိတ်ရန်"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>− <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"ဖုန်းကိုင်ရန်"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"ငြင်းပယ်ရန်"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"ဖုန်းချရန်"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"အဝင်ခေါ်ဆိုမှု"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ဤအကြောင်းကြားချက်ကို \'အသံတိတ်ခြင်း\' သို့ ပြန်ချိန်ညှိထားသည်။ အကြံပြုချက်ပေးရန် တို့ပါ။"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"ဤအကြောင်းကြားချက်ကို အဆင့်တိုးထားသည်။ အကြံပြုချက်ပေးရန် တို့ပါ။"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"ဤအကြောင်းကြားချက်ကို အဆင့်လျှော့ထားသည်။ အကြံပြုချက်ပေးရန် တို့ပါ။"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"အဆင့်မြင့် အကြောင်းကြားချက်များ စမ်းသုံးကြည့်ခြင်း"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"အကြံပြုထားသော လုပ်ဆောင်ချက်များ၊ ပြန်စာများ စသည်တို့ကို ဆက်လက်ရယူရန် အဆင့်မြင့် အကြောင်းကြားချက်များကို ဖွင့်ပါ။ ‘Android အလိုက်သင့် အကြောင်းကြားချက်များ’ ကို ပံ့ပိုးမထားတော့ပါ။"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"ဖွင့်ရန်"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"ယခုမလုပ်ပါ"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"ပိုမိုလေ့လာရန်"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"အဆင့်မြင့် အကြောင်းကြားချက်များသည် အဆက်အသွယ်အမည်နှင့် မက်ဆေ့ဂျ်များကဲ့သို့ ကိုယ်ရေးကိုယ်တာအချက်လက်များ အပါအဝင် အကြောင်းကြားချက် အကြောင်းအရာအားလုံးကို ဖတ်နိုင်သည်။ ဤဝန်ဆောင်မှုသည် အကြောင်းကြားချက်များကို ပယ်ခြင်း (သို့) ဖုန်းခေါ်ဆိုမှုများ ဖြေခြင်းကဲ့သို့ အကြောင်းကြားချက်များရှိ ခလုတ်များ နှိပ်ခြင်းကိုလည်း ပြုလုပ်နိုင်သည်။\n\nဤဝန်ဆောင်မှုသည် ‘ဦးစားပေးမုဒ်’ ကို ဖွင့်ခြင်း (သို့) ပိတ်ခြင်း ပြုလုပ်နိုင်ပြီး ဆက်စပ်နေသော ဆက်တင်များကိုလည်း ပြောင်းနိုင်သည်။"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ပုံမှန်မုဒ်အတွက် အချက်အလက်ပြသည့် အကြောင်းကြားချက်"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ပုံမှန်အားသွင်းမှုမပြုလုပ်မီ ဘက်ထရီကုန်သွားနိုင်သည်"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ဘက်ထရီသက်တမ်းကို တိုးမြှင့်ရန် \'ဘက်ထရီအားထိန်း\' စတင်ပြီးပါပြီ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index ec8d9d5..9c70fdf 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Batterisparing slår på mørkt tema og begrenser eller slår av bakgrunnsaktivitet, enkelte visuelle effekter og funksjoner, for eksempel «Hey Google»\n\n"<annotation id="url">"Finn ut mer"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Batterisparing slår på mørkt tema og begrenser eller slår av bakgrunnsaktivitet, enkelte visuelle effekter og funksjoner, for eksempel «Hey Google»."</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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Lukk"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g><xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Svar"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Avvis"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Legg på"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Innkommende anrop"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Dette varselet ble nedgradert til lydløst. Trykk for å gi tilbakemelding."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Dette varselet ble rangert høyere. Trykk for å gi tilbakemelding."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Dette varselet ble rangert lavere. Trykk for å gi tilbakemelding."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Prøv forbedrede varsler"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"For å fortsette å få foreslåtte handlinger, svar med mer, slå på forbedrede varsler. Tilpassede Android-varsler støttes ikke lenger."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Slå på"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Ikke nå"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Finn ut mer"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Forbedrede varsler kan lese alt varselinnhold, inkludert personopplysninger som kontaktnavn og meldinger. Denne funksjonen kan også avvise varsler eller bruke knapper i varsler, for eksempel for å svare på telefonanrop.\n\nDenne funksjonen kan også slå prioriteringsmodus på eller av og endre relaterte innstillinger."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Varsel med informasjon om rutinemodus"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Batteriet kan gå tomt før den vanlige ladingen"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Batterisparing er aktivert for å forlenge batterilevetiden"</string>
@@ -2269,7 +2263,7 @@
<string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
<string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
- <string name="window_magnification_prompt_title" msgid="2876703640772778215">"Nye forstørringsinnstillinger"</string>
+ <string name="window_magnification_prompt_title" msgid="2876703640772778215">"Nye innstillinger for forstørring"</string>
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Nå kan du forstørre en del av skjermen"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Slå på i innstillingene"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Avvis"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 894edcf..ece2a53 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -71,7 +71,7 @@
<string name="RuacMmi" msgid="1876047385848991110">"नचाहिएका रिसउठ्दा कलहरूको अस्वीकार"</string>
<string name="CndMmi" msgid="185136449405618437">"कलिङ नम्बर प्रदान गर्ने"</string>
<string name="DndMmi" msgid="8797375819689129800">"बाधा नगर्नुहोस्"</string>
- <string name="CLIRDefaultOnNextCallOn" msgid="4511621022859867988">"सीमति गर्न पूर्वनिर्धारित कलर ID, अर्को कल: सीमति गरिएको"</string>
+ <string name="CLIRDefaultOnNextCallOn" msgid="4511621022859867988">"सीमति गर्न डिफल्ट कलर ID, अर्को कल: सीमति गरिएको"</string>
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"कलर ID पूर्वनिर्धारितको लागि रोकावट छ। अर्को कल: रोकावट छैन"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"कलर ID पूर्वनिर्धारितदेखि प्रतिबन्धित छैन। अर्को कल: प्रतिबन्धित छ"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"कलर ID पूर्वनिर्धारितको लागि रोकावट छैन। अर्को कल: रोकावट छैन"</string>
@@ -294,14 +294,13 @@
<string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"एप चलिरहेको छ"</string>
<string name="notification_channel_foreground_service" msgid="7102189948158885178">"एपहरूले ब्याट्री खपत गर्दै छन्"</string>
<string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"जुम इन गर्ने सुविधा"</string>
- <!-- no translation found for notification_channel_accessibility_security_policy (1727787021725251912) -->
- <skip />
+ <string name="notification_channel_accessibility_security_policy" msgid="1727787021725251912">"सर्वसुलभतासम्बन्धी सेवाहरूको प्रयोग"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले ब्याट्री प्रयोग गर्दै छ"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> एपहरूले ब्याट्री प्रयोग गर्दै छन्"</string>
<string name="foreground_service_tap_for_details" msgid="9078123626015586751">"ब्याट्री र डेटाका प्रयोग सम्बन्धी विवरणहरूका लागि ट्याप गर्नुहोस्"</string>
<string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="8974401416068943888">"सुरक्षित मोड"</string>
- <string name="android_system_label" msgid="5974767339591067210">"एन्ड्रोइड प्रणाली"</string>
+ <string name="android_system_label" msgid="5974767339591067210">"Android सिस्टम"</string>
<string name="user_owner_label" msgid="8628726904184471211">"व्यक्तिगत प्रोफाइलमा बदल्नुहोस्"</string>
<string name="managed_profile_label" msgid="7316778766973512382">"कार्य प्रोफाइलमा बदल्नुहोस्"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"सम्पर्कहरू"</string>
@@ -320,10 +319,8 @@
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"आफ्नो शारीरिक क्रियाकलापको डेटामाथि पहुँच राख्नु"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"क्यामेरा"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"फोटो खिच्नुका साथै भिडियो रेकर्ड गर्नुहोस्"</string>
- <!-- no translation found for permgrouplab_nearby_devices (5529147543651181991) -->
- <skip />
- <!-- no translation found for permgroupdesc_nearby_devices (3213561597116913508) -->
- <skip />
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"नजिकै रहेका डिभाइसहरू"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"नजिकै रहेका डिभाइसहरू फेला पार्ने तथा ती डिभाइसमा कनेक्ट गर्ने"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"कल लग"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"फोन कलको लग पढ्नुहोस् र लेख्नुहोस्"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"फोन"</string>
@@ -541,14 +538,10 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"यो अनुमति दिइएमा एपले नजिकै रहेका ब्लुटुथ चल्ने डिभाइसहरू भेट्टाउन र ती यन्त्रहरूसँग कनेक्ट गर्न सक्छ"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"लिंक गरिएका ब्लुटुथ चल्ने यन्त्रहरूसँग कनेक्ट गर्ने"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"यो अनुमति दिइएमा एपले लिंक गरिएका ब्लुटुथ चल्ने यन्त्रहरूसँग कनेक्ट गर्न सक्छ"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
- <!-- no translation found for permlab_uwb_ranging (8141915781475770665) -->
- <skip />
- <!-- no translation found for permdesc_uwb_ranging (2519723069604307055) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"नजिकै रहेका ब्लुटुथ चल्ने डिभाइसमा विज्ञापन गर्ने"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"यो एपले नजिकै रहेका ब्लुटुथ चल्ने डिभाइसहरूमा विज्ञापन गर्न पाउँछ"</string>
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"नजिकै रहेका अल्ट्रा-वाइडब्यान्ड चल्ने डिभाइसहरूबिचको तुलनात्मक स्थान पत्ता लगाउने"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"यो एपलाई नजिकै रहेका अल्ट्रा-वाइडब्यान्ड चल्ने डिभाइसहरूबिचको तुलनात्मक स्थान पत्ता लगाउन दिनुहोस्"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"NFC भुक्तानी सेवासम्बन्धी रुचाइएको जानकारी"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"यसले एपलाई दर्ता गरिएका सहायता तथा मार्गको गन्तव्य जस्ता रुचाइएका NFC भुक्तानी सेवासम्बन्धी जानकारी प्राप्त गर्न दिन्छ।"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"नजिक क्षेत्र संचार नियन्त्रणहरू"</string>
@@ -584,28 +577,21 @@
<string name="biometric_error_generic" msgid="6784371929985434439">"प्रमाणित गर्ने क्रममा त्रुटि भयो"</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"स्क्रिन लक प्रयोग गर्नुहोस्"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"जारी राख्न आफ्नो स्क्रिन लक हाल्नुहोस्"</string>
- <!-- no translation found for fingerprint_acquired_partial (694598777291084823) -->
- <skip />
+ <string name="fingerprint_acquired_partial" msgid="694598777291084823">"फिंगरप्रिन्ट आंशिक रूपमा पत्ता लाग्यो"</string>
<string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"फिंगरप्रिन्ट प्रशोधन गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।"</string>
- <!-- no translation found for fingerprint_acquired_imager_dirty (5236744087471419479) -->
- <skip />
- <!-- no translation found for fingerprint_acquired_too_fast (1254724478300787385) -->
- <skip />
+ <string name="fingerprint_acquired_imager_dirty" msgid="5236744087471419479">"सेन्सर सफा गर्नुहोस्"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="1254724478300787385">"औँला धेरै छिटो सर्यो"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"औंला निकै सुस्त सारियो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
- <!-- no translation found for fingerprint_acquired_already_enrolled (2285166003936206785) -->
- <skip />
- <!-- no translation found for fingerprint_acquired_too_bright (3863560181670915607) -->
- <skip />
- <!-- no translation found for fingerprint_acquired_try_adjusting (3667006071003809364) -->
- <skip />
+ <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"अर्को फिंगरप्रिन्ट प्रयोग गरी हेर्नुहोस्"</string>
+ <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ज्यादै उज्यालो छ"</string>
+ <string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"सेन्सरमा सही तरिकाले औँला राखेर हेर्नुहोस्"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"फिंगरप्रिन्ट प्रमाणीकरण गरियो"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"अनुहार प्रमाणीकरण गरियो"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"अनुहार प्रमाणीकरण गरियो, कृपया पुष्टि गर्नुहोस् थिच्नुहोस्"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"औँठाछाप हार्डवेयर उपलब्ध छैन।"</string>
- <!-- no translation found for fingerprint_error_no_space (7285481581905967580) -->
- <skip />
+ <string name="fingerprint_error_no_space" msgid="7285481581905967580">"फिंगरप्रिन्ट सेटअप गर्न सकिएन"</string>
<string name="fingerprint_error_timeout" msgid="2946635815726054226">"फिंगरप्रिन्ट समय सकिएको छ। फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिन्ट सञ्चालन रद्द गरियो।"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"प्रयोगकर्ताले फिंगरप्रिन्टसम्बन्धी कारबाही रद्द गर्नुभयो।"</string>
@@ -723,7 +709,7 @@
<string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"DRM प्रमाणपत्रहरू प्रावधान र प्रयोग गर्ने निवेदनको अनुमति दिन्छ। साधारण अनुप्रयोगहरूको लागि कहिल्यै पनि आवश्यक पर्दैन।"</string>
<string name="permlab_handoverStatus" msgid="7620438488137057281">"Android Beam स्थानान्तरण अवस्था प्राप्त गर्नुहोस्"</string>
<string name="permdesc_handoverStatus" msgid="3842269451732571070">"यस आवेदनले वर्तमान Android Beam स्थानान्तरण बारेमा जानकारी प्राप्त गर्न अनुमति दिन्छ"</string>
- <string name="permlab_removeDrmCertificates" msgid="710576248717404416">"DRM प्रमाणपत्रहरू हटाउनुहोस्"</string>
+ <string name="permlab_removeDrmCertificates" msgid="710576248717404416">"DRM सर्टिफिकेट हटाउनुहोस्"</string>
<string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"DRM प्रमाणपत्रहरू हटाउन एपलाई अनुमति दिन्छ। सामान्य अनुप्रयोगहरूको लागि कहिल्यै आवश्यकता पर्दैन।"</string>
<string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"वाहक मेसेजिङ सेवामा आबद्ध हुनुहोस्"</string>
<string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"धारकलाई वाहक मेसेजिङ सेवाको उच्च-स्तरको इन्टरफेसमा आबद्ध हुन अनुमति दिनुहोस्। सामान्य एपहरूको लागि कहिल्यै आवश्यकता पर्दैन।"</string>
@@ -930,10 +916,10 @@
<string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"तपाईंले आफ्नो अनलक शैली <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले कोर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो Google खाता मार्फत साइन इन गरेर आफ्नो Android टिभी यन्त्र अनलक गर्न अनुरोध गरिनेछ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डपछि फेरि प्रयास गर्नुहोस्।"</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"तपाईँले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले तपाईँको अनलक ढाँचालाई कोर्नु भएको छ। पछि <xliff:g id="NUMBER_1">%2$d</xliff:g> अरू धेरै असफल कोसिसहरूपछि, तपाईँलाई तपाईँको फोन Google साइन इन प्रयोग गरेर अनलक गर्नको लागि सोधिने छ। \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा पुनः प्रयास गर्नुहोस्।"</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"तपाईँले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक ट्याब्लेटलाई अनलक गर्नको लागि गलत तरिकाले कोशिस गर्नुभएको छ। <xliff:g id="NUMBER_1">%2$d</xliff:g> अरू धेरै असफल कोसिसहरूपछि, ट्याब्लेट फ्याट्रि पूर्वनिर्धारितमा रिसेट हुने छ र सबै प्रयोगकर्ता डेटा हराउने छन्।"</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।"</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर डिफल्ट फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।"</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"तपाईंले गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक फोन अनलक गर्ने प्रयत्न गर्नुभयो। <xliff:g id="NUMBER_1">%2$d</xliff:g> बढी असफल प्रयत्नहरू पछि, फोन फ्याक्ट्रि पूर्वनिर्धारितमा रिसेट हुने छ र सबै प्रयोगकर्ता डेटा हराउने छन्।"</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"तपाईँले ट्यब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER">%d</xliff:g> पटक प्रयास गर्नु भएको छ। अब ट्याब्लेटलाई पूर्वनिर्धारित कार्यशालामा रिसेट गरिने छ।"</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER">%d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिनेछ।"</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"तपाईँले ट्यब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER">%d</xliff:g> पटक प्रयास गर्नु भएको छ। अब ट्याब्लेटलाई डिफल्ट कार्यशालामा रिसेट गरिने छ।"</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER">%d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर डिफल्ट फ्याक्ट्री सेटिङ लागू गरिनेछ।"</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"तपाईंले गलत तरिकाले फोन <xliff:g id="NUMBER">%d</xliff:g> पटक अनलक गर्ने प्रयत्न गर्नुभयो। अब फोन फ्याक्ट्रि पूर्वनिर्धारितमा रिसेट हुने छ।"</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"<xliff:g id="NUMBER">%d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"ढाँचा बिर्सनु भयो?"</string>
@@ -1302,8 +1288,8 @@
<string name="volume_icon_description_incall" msgid="4491255105381227919">"कला मात्रा"</string>
<string name="volume_icon_description_media" msgid="4997633254078171233">"मिडियाको भोल्युम"</string>
<string name="volume_icon_description_notification" msgid="579091344110747279">"सूचना भोल्युम"</string>
- <string name="ringtone_default" msgid="9118299121288174597">"पूर्वनिर्धारित रिङटोन"</string>
- <string name="ringtone_default_with_actual" msgid="2709686194556159773">"पूर्वनिर्धारित (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
+ <string name="ringtone_default" msgid="9118299121288174597">"डिफल्ट रिङटोन"</string>
+ <string name="ringtone_default_with_actual" msgid="2709686194556159773">"डिफल्ट (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
<string name="ringtone_silent" msgid="397111123930141876">"कुनै पनि होइन"</string>
<string name="ringtone_picker_title" msgid="667342618626068253">"रिङटोनहरू"</string>
<string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"अलार्मका आवाजहरू"</string>
@@ -1400,7 +1386,7 @@
<string name="share_remote_bugreport_action" msgid="7630880678785123682">"सेयर गर्नुहोस्"</string>
<string name="decline_remote_bugreport_action" msgid="4040894777519784346">"अस्वीकार गर्नुहोस्"</string>
<string name="select_input_method" msgid="3971267998568587025">"निवेश विधि छान्नुहोस्"</string>
- <string name="show_ime" msgid="6406112007347443383">"वास्तविक किबोर्ड सक्रिय हुँदा यसलाई स्क्रिनमा राख्नुहोस्"</string>
+ <string name="show_ime" msgid="6406112007347443383">"वास्तविक किबोर्ड सक्रिय हुँदा यसलाई स्क्रिनमा राखियोस्"</string>
<string name="hardware" msgid="1800597768237606953">"भर्चुअल किबोर्ड देखाउनुहोस्"</string>
<string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"फिजिकल किबोर्डलाई कन्फिगर गर्नुहोस्"</string>
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"भाषा र लेआउट चयन गर्न ट्याप गर्नुहोस्"</string>
@@ -1681,11 +1667,11 @@
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"तपाईँले गलत तरिकाले तपाईँको PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक टाइप गर्नु भएको छ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"तपाईँले तपाईँक पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत टाइप गर्नुभएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"तपाईँले तपाईँको अनलक प्याटर्न गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक खिच्नु भएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि कोसिस गर्नुहोस्।"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"तपाईँले ट्याब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोसिस गर्नु भएको छ। <xliff:g id="NUMBER_1">%2$d</xliff:g> पछि थप असफल प्रयासहरू, ट्याब्लेट पूर्वनिर्धारित कार्यशालामा रिसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"तपाईँले गलतसँग फोनलाई अनलक गर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोसिस गर्नु भयो। <xliff:g id="NUMBER_1">%2$d</xliff:g> पछि थप असफल कोसिसहरू, फोनलाई पूर्वनिर्धारित कार्यशालामा रिसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।"</string>
- <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"तपाईँले ट्यब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER">%d</xliff:g> पटक प्रयास गर्नु भएको छ। अब ट्याब्लेटलाई पूर्वनिर्धारित कार्यशालामा रिसेट गरिने छ।"</string>
- <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER">%d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर पूर्वनिर्धारित फ्याक्ट्री सेटिङ लागू गरिनेछ।"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"तपाईँले ट्याब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोसिस गर्नु भएको छ। <xliff:g id="NUMBER_1">%2$d</xliff:g> पछि थप असफल प्रयासहरू, ट्याब्लेट डिफल्ट कार्यशालामा रिसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर डिफल्ट फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।"</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"तपाईँले गलतसँग फोनलाई अनलक गर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोसिस गर्नु भयो। <xliff:g id="NUMBER_1">%2$d</xliff:g> पछि थप असफल कोसिसहरू, फोनलाई डिफल्ट कार्यशालामा रिसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।"</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"तपाईँले ट्यब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER">%d</xliff:g> पटक प्रयास गर्नु भएको छ। अब ट्याब्लेटलाई डिफल्ट कार्यशालामा रिसेट गरिने छ।"</string>
+ <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER">%d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर डिफल्ट फ्याक्ट्री सेटिङ लागू गरिनेछ।"</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"तपाईंले गलत तरिकाले फोन <xliff:g id="NUMBER">%d</xliff:g> पटक अनलक गर्ने प्रयत्न गर्नुभयो। अब फोन फ्याक्ट्रि पूर्वनिर्धारितमा रिसेट हुने छ।"</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"तपाईंले गलत तरिकाले आफ्नो अनलक प्याटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोर्नुभयो। <xliff:g id="NUMBER_1">%2$d</xliff:g> विफल प्रयत्नहरू पछि, तपाईंलाई आफ्नो ट्याब्लेट इमेल खाता प्रयोग गरेर अनलक गर्न सोधिने छ।\n\n फेरि प्रयास गर्नुहोस् <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डहरूमा।"</string>
<string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"तपाईंले आफ्नो अनलक शैली <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले कोर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो इमेल खाता प्रयोग गरेर आफ्नो Android टिभी यन्त्र अनलक गर्न अनुरोध गरिनेछ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डपछि फेरि प्रयास गर्नुहोस्।"</string>
@@ -1695,18 +1681,18 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"सिफारिस तहभन्दा आवाज ठुलो गर्नुहुन्छ?\n\nलामो समय सम्म उच्च आवाजमा सुन्दा तपाईँको सुन्ने शक्तिलाई हानी गर्न सक्छ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"पहुँच सम्बन्धी सर्टकट प्रयोग गर्ने हो?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"यो सर्टकट सक्रिय हुँदा, ३ सेकेन्डसम्म दुवै भोल्युम बटन थिच्नुले पहुँचसम्बन्धी कुनै सुविधा सुरु गर्ने छ।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"सर्वसुलभता कायम गर्ने सुविधाहरू प्रयोग गर्न सर्टकट अन गर्ने हो?"</string>
- <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"केही सेकेन्डसम्म दुवै भोल्युम बटन थिचिराख्नुभयो भने पहुँचसम्बन्धी सुविधाहरू सक्रिय हुन्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nहालका सुविधाहरू:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nतपाईं सेटिङ > पहुँचमा गएर चयन गरिएका सुविधाहरू परिवर्तन गर्न सक्नुहुन्छ।"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"एक्सेसिबिलिटीसम्बन्धी सुविधा प्रयोग गर्न सर्टकट अन गर्ने हो?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"केही सेकेन्डसम्म दुवै भोल्युम की थिचिराख्नुभयो भने पहुँचसम्बन्धी सुविधाहरू सक्रिय हुन्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nहालका सुविधाहरू:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nतपाईं सेटिङ > पहुँचमा गएर चयन गरिएका सुविधाहरू परिवर्तन गर्न सक्नुहुन्छ।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
<string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"<xliff:g id="SERVICE">%1$s</xliff:g> प्रयोग गर्न सर्टकट अन गर्ने हो?"</string>
- <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"केही सेकेन्डसम्म दुवै भोल्युम बटन थिचिराख्नुले <xliff:g id="SERVICE">%1$s</xliff:g> नामक पहुँचसम्बन्धी सुविधा सक्रिय गर्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nतपाईं सेटिङ > पहुँचमा गई यो सर्टकटमार्फत अर्को सुविधा खुल्ने बनाउन सक्नुहुन्छ।"</string>
+ <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"केही सेकेन्डसम्म दुवै भोल्युम की थिचिराख्नुले <xliff:g id="SERVICE">%1$s</xliff:g> नामक पहुँचसम्बन्धी सुविधा सक्रिय गर्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nतपाईं सेटिङ > पहुँचमा गई यो सर्टकटमार्फत अर्को सुविधा खुल्ने बनाउन सक्नुहुन्छ।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"सक्रिय गरियोस्"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"सक्रिय नगरियोस्"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"सक्रिय"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"निष्क्रिय"</string>
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> लाई तपाईंको यन्त्र पूर्ण रूपमा नियन्त्रण गर्न दिने हो?"</string>
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"तपाईंले <xliff:g id="SERVICE">%1$s</xliff:g> सक्रिय गर्नुभयो भने तपाईंको यन्त्रले डेटा इन्क्रिप्ट गर्ने सुविधाको स्तरोन्नति गर्न तपाईंको स्क्रिन लक सुविधाको प्रयोग गर्ने छैन।"</string>
- <string name="accessibility_service_warning_description" msgid="291674995220940133">"तपाईंलाई पहुँच राख्न आवश्यक पर्ने कुरामा सहयोग गर्ने एपमाथि पूर्ण नियन्त्रण गर्नु उपयुक्त हुन्छ तर अधिकांश अनुप्रयोगहरूका हकमा यस्तो नियन्त्रण उपयुक्त हुँदैन।"</string>
+ <string name="accessibility_service_warning_description" msgid="291674995220940133">"एक्सेसिबिलिटीसम्बन्धी आवश्यकतामा सहयोग गर्ने एपको पूर्ण नियन्त्रण गर्नु उपयुक्त हुन्छ तर अधिकांश एपका हकमा यस्तो नियन्त्रण उपयुक्त हुँदैन।"</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"स्क्रिन हेर्नुहोस् र नियन्त्रण गर्नुहोस्"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"यसले स्क्रिनमा देखिने सबै सामग्री पढ्न सक्छ र अन्य एपहरूमा उक्त सामग्री देखाउन सक्छ।"</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"कारबाहीहरू हेर्नुहोस् र तिनमा कार्य गर्नुहोस्"</string>
@@ -1727,7 +1713,7 @@
<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>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> प्रयोग गर्न दुवै भोल्युम कुञ्जीहरूलाई तीन सेकेन्डसम्म थिचिराख्नुहोस्"</string>
- <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"तपाईंले पहुँचको बटन ट्याप गर्दा प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस्:"</string>
+ <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"तपाईंले एक्सेसिबिलिटी बटन ट्याप गर्दा प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस्:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"तपाईंले पहुँचको इसारामार्फत प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस् (दुईवटा औँलाले स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्):"</string>
<string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"तपाईंले पहुँचको इसारामार्फत प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस् (तीनवटा औँलाले स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्):"</string>
<string name="accessibility_button_instructional_text" msgid="8853928358872550500">"एउटा सुविधाबाट अर्को सुविधामा जान पहुँच बटन टच एण्ड होल्ड गर्नुहोस्।"</string>
@@ -1945,6 +1931,8 @@
<string name="close_button_text" msgid="10603510034455258">"बन्द गर्नुहोस्"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"कलको जवाफ दिनु…"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"अस्वीकार गर्नुहोस्"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"फोन राख्नुहोस्"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"आगमन कल"</string>
@@ -1963,7 +1951,7 @@
<string name="language_selection_title" msgid="52674936078683285">"भाषा थप्नुहोस्"</string>
<string name="country_selection_title" msgid="5221495687299014379">"क्षेत्रको प्राथमिकता"</string>
<string name="search_language_hint" msgid="7004225294308793583">"भाषाको नाम टाइप गर्नुहोस्"</string>
- <string name="language_picker_section_suggested" msgid="6556199184638990447">"सुझाव दिइयो"</string>
+ <string name="language_picker_section_suggested" msgid="6556199184638990447">"सिफारिस गरिएको"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"सम्पूर्ण भाषाहरू"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"सबै क्षेत्रहरू"</string>
<string name="locale_search_menu" msgid="6258090710176422934">"खोज"</string>
@@ -2084,7 +2072,7 @@
<string name="notification_appops_microphone_active" msgid="581333393214739332">"माइक्रोफोन"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"तपाईंको स्क्रिनका अन्य एपहरूमा प्रदर्शन गरिँदै छ"</string>
<string name="notification_feedback_indicator" msgid="663476517711323016">"प्रतिक्रिया दिनुहोस्"</string>
- <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"सिस्टमले स्वतः यस सूचनालाई महत्त्वपूर्ण ठानी यसका लागि पूर्वनिर्धारित मोड सेट गरिदिएको छ। प्रतिक्रिया दिन ट्याप गर्नुहोस्।"</string>
+ <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"सिस्टमले स्वतः यस सूचनालाई महत्त्वपूर्ण ठानी यसका लागि डिफल्ट मोड सेट गरिदिएको छ। प्रतिक्रिया दिन ट्याप गर्नुहोस्।"</string>
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"यस सूचनालाई कम महत्त्वपूर्ण ठानी यसका लागि साइलेन्ट मोड सेट गरिएको छ। प्रतिक्रिया दिन ट्याप गर्नुहोस्।"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"यस सूचनालाई धेरै महत्त्वपूर्ण सूचनाका रूपमा सेट गरिएको छ। प्रतिक्रिया दिन ट्याप गर्नुहोस्।"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"यस सूचनालाई कम महत्त्वपूर्ण सूचनाका रूपमा सेट गरिएको छ। प्रतिक्रिया दिन ट्याप गर्नुहोस्।"</string>
@@ -2284,20 +2272,16 @@
<string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
<string name="window_magnification_prompt_title" msgid="2876703640772778215">"जुम इन गर्ने सुविधासम्बन्धी नयाँ सेटिङ"</string>
- <string name="window_magnification_prompt_content" msgid="8159173903032344891">"तपाईं अब स्क्रिनको जुनसुकै भाग जुम इन गर्न सक्नुहुन्छ"</string>
+ <string name="window_magnification_prompt_content" msgid="8159173903032344891">"तपाईं अब स्क्रिनको जुनसुकै भागमा जुम इन गर्न सक्नुहुन्छ"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"सेटिङमा गई यो सुविधा अन गर्नुहोस्"</string>
<string name="dismiss_action" msgid="1728820550388704784">"हटाउनुहोस्"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"डिभाइसको माइक्रोफोन अनब्लक गर्नुहोस्"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"डिभाइसको क्यामेरा अनब्लक गर्नुहोस्"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> का साथै सबै एप तथा सेवाहरूका लागि"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"अनब्लक गर्नुहोस्"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"सेन्सरसम्बन्धी गोपनीयता"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"एप जनाउने आइकन"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"एपको ब्रान्डिङ फोटो"</string>
- <string name="view_and_control_notification_title" msgid="4300765399209912240">"हेर्ने तथा नियन्त्रण गर्ने अनुमतिसम्बन्धी सेटिङ जाँच्नु…"</string>
- <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ले तपाईंको स्क्रिन हेर्न र नियन्त्रण गर्न सक्छ। समीक्षा गर्न ट्याप गर्नुहोस्।"</string>
+ <string name="view_and_control_notification_title" msgid="4300765399209912240">"हेराइ र नियन्त्रणसम्बन्धी सेटिङ जाँच्नुहोस्"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> तपाईंको स्क्रिन हेर्न र नियन्त्रण गर्न सक्छ। सेटिङ मिलाउन ट्याप गर्नुहोस्।"</string>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index a8e0c0e..0222f6f 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1108,8 +1108,8 @@
<item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> minuut geleden</item>
</plurals>
<plurals name="duration_hours_relative" formatted="false" msgid="420434788589102019">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> uur geleden</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> uur geleden</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> uur geleden, </item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> uur geleden, </item>
</plurals>
<plurals name="duration_days_relative" formatted="false" msgid="6056425878237482431">
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> dagen geleden</item>
@@ -1386,7 +1386,7 @@
<string name="share_remote_bugreport_action" msgid="7630880678785123682">"DELEN"</string>
<string name="decline_remote_bugreport_action" msgid="4040894777519784346">"WEIGEREN"</string>
<string name="select_input_method" msgid="3971267998568587025">"Invoermethode selecteren"</string>
- <string name="show_ime" msgid="6406112007347443383">"Op het scherm tonen terwijl het fysieke toetsenbord actief is"</string>
+ <string name="show_ime" msgid="6406112007347443383">"Toon op het scherm terwijl het fysieke toetsenbord actief is"</string>
<string name="hardware" msgid="1800597768237606953">"Virtueel toetsenbord tonen"</string>
<string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Fysiek toetsenbord instellen"</string>
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Tik om een taal en indeling te selecteren"</string>
@@ -1852,10 +1852,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Geüpdatet door je beheerder"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Verwijderd door je beheerder"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Batterijbesparing zet het donkere thema aan en beperkt achtergrondactiviteit, bepaalde visuele effecten en functies zoals Hey Google of zet dit uit\n\n"<annotation id="url">"Meer informatie"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Batterijbesparing zet het donkere thema aan en beperkt achtergrondactiviteit, bepaalde visuele effecten en functies zoals Hey Google of zet dit uit."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Databesparing beperkt het datagebruik door te voorkomen dat sommige apps gegevens sturen of ontvangen op de achtergrond. De apps die je open hebt, kunnen nog steeds data verbruiken, maar doen dit minder vaak. Afbeeldingen worden dan bijvoorbeeld niet weergegeven totdat je erop tikt."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Databesparing aanzetten?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aanzetten"</string>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Sluiten"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Beantwoorden"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Weigeren"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Ophangen"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Inkomend gesprek"</string>
@@ -1946,9 +1945,9 @@
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Aangepaste app-melding"</string>
<string name="user_creation_account_exists" msgid="2239146360099708035">"Toestaan dat <xliff:g id="APP">%1$s</xliff:g> een nieuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> maakt (er is al een gebruiker met dit account)?"</string>
<string name="user_creation_adding" msgid="7305185499667958364">"Toestaan dat <xliff:g id="APP">%1$s</xliff:g> een nieuwe gebruiker met <xliff:g id="ACCOUNT">%2$s</xliff:g> maakt?"</string>
- <string name="language_selection_title" msgid="52674936078683285">"Een taal toevoegen"</string>
+ <string name="language_selection_title" msgid="52674936078683285">"Taal toevoegen"</string>
<string name="country_selection_title" msgid="5221495687299014379">"Regiovoorkeur"</string>
- <string name="search_language_hint" msgid="7004225294308793583">"Typ een taalnaam"</string>
+ <string name="search_language_hint" msgid="7004225294308793583">"Typ de naam van een taal"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Voorgesteld"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Alle talen"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"Alle regio\'s"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Deze melding is verlaagd naar Stil. Tik om feedback te geven."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Deze melding is hoger geclassificeerd. Tik om feedback te geven."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Deze melding is lager geclassificeerd. Tik om feedback te geven."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Probeer verbeterde meldingen"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Activeer verbeterde meldingen om voorgestelde acties, antwoorden en meer te blijven ontvangen. Aanpasbare Android-meldingen worden niet meer ondersteund."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Aanzetten"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Niet nu"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Meer informatie"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Verbeterde meldingen kunnen alle meldingscontent lezen, waaronder persoonlijke informatie zoals contactnamen en berichten. Deze functie kan ook meldingen sluiten of acties uitvoeren voor knoppen in meldingen, zoals telefoongesprekken aannemen.\n\nDeze functie kan ook de prioriteitsmodus aan- of uitzetten en gerelateerde instellingen wijzigen."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Informatiemelding voor routinemodus"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"De batterij raakt mogelijk leeg voordat deze normaal gesproken wordt opgeladen"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Batterijbesparing is geactiveerd om de batterijduur te verlengen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 2536fa0..7948f64 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1160,7 +1160,7 @@
<string name="redo" msgid="7231448494008532233">"ପୁଣି କରନ୍ତୁ"</string>
<string name="autofill" msgid="511224882647795296">"ଅଟୋଫିଲ୍"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"ଟେକ୍ସଟ୍ ଚୟନ"</string>
- <string name="addToDictionary" msgid="8041821113480950096">"ଶବ୍ଦକୋଷରେ ଯୋଡ଼ନ୍ତୁ"</string>
+ <string name="addToDictionary" msgid="8041821113480950096">"ଶବ୍ଦକୋଷରେ ଯୋଗ କରନ୍ତୁ"</string>
<string name="deleteText" msgid="4200807474529938112">"ଡିଲିଟ୍ କରନ୍ତୁ"</string>
<string name="inputMethod" msgid="1784759500516314751">"ଇନପୁଟ୍ ପଦ୍ଧତି"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"ଟେକ୍ସଟ୍ କାର୍ଯ୍ୟ"</string>
@@ -1677,7 +1677,7 @@
<string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"ଆପଣ ଆପଣଙ୍କର ଅନଲକ୍ ପାଟର୍ନକୁ <xliff:g id="NUMBER_0">%1$d</xliff:g> ଥର ଭୁଲ ଭାବେ ଆଙ୍କିଛନ୍ତି। <xliff:g id="NUMBER_1">%2$d</xliff:g> ଥର ଅସଫଳ ଚେଷ୍ଟା ପରେ, ଏକ ଇମେଲ୍ ଆକାଉଣ୍ଟ ବ୍ୟବହାର କରି ଆପଣଙ୍କର Android TV ଡିଭାଇସ୍କୁ ଅନ୍ଲକ୍ କରିବା ପାଇଁ କୁହାଯିବ। \n\n<xliff:g id="NUMBER_2">%3$d</xliff:g> ସେକେଣ୍ଡ ମଧ୍ୟରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"ଆପଣଙ୍କ ଅନଲକ୍ ପାଟର୍ନକୁ ଆପଣ <xliff:g id="NUMBER_0">%1$d</xliff:g> ଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। ଆଉ <xliff:g id="NUMBER_1">%2$d</xliff:g>ଟି ଭୁଲ ପ୍ରୟାସ ପରେ ଏକ ଇମେଲ୍ ଆକାଉଣ୍ଟ ବ୍ୟବହାର କରି ନିଜ ଫୋନ୍କୁ ଅନଲକ୍ କରିବା ପାଇଁ କୁହାଯିବ।\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g> ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
- <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ବାହାର କରନ୍ତୁ"</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ମାତ୍ରା ବଢ଼ାଇ ସୁପାରିଶ ସ୍ତର ବଢ଼ାଉଛନ୍ତି? \n\n ଲମ୍ବା ସମୟ ପର୍ଯ୍ୟନ୍ତ ଉଚ୍ଚ ଶବ୍ଦରେ ଶୁଣିଲେ ଆପଣଙ୍କ ଶ୍ରବଣ ଶକ୍ତି ଖରାପ ହୋଇପାରେ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ଆକ୍ସେସବିଲିଟି ଶର୍ଟକଟ୍ ବ୍ୟବହାର କରିବେ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ସର୍ଟକଟ୍ ଚାଲୁ ଥିବା ବେଳେ, ଉଭୟ ଭଲ୍ୟୁମ୍ ବଟନ୍ 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇବା ଦ୍ୱାରା ଏକ ଆକ୍ସେସବିଲିଟି ଫିଚର୍ ଆରମ୍ଭ ହେବ।"</string>
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ବ୍ୟାଟେରୀ ସେଭର୍ ଗାଢ଼ା ଥିମକୁ ଚାଲୁ କରେ ଏବଂ ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ଇଫେକ୍ଟ ଏବଂ “Hey Google” ପରି ଫିଚରଗୁଡ଼ିକୁ ସୀମିତ କିମ୍ବା ବନ୍ଦ କରେ\n\n"<annotation id="url">"ଅଧିକ ଜାଣନ୍ତୁ"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"ବ୍ୟାଟେରୀ ସେଭର୍ ଗାଢ଼ା ଥିମକୁ ଚାଲୁ କରେ ଏବଂ ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ଇଫେକ୍ଟ ଏବଂ “Hey Google” ପରି ଫିଚରଗୁଡ଼ିକୁ ସୀମିତ କିମ୍ବା ବନ୍ଦ କରେ।"</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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"ଉତ୍ତର ଦିଅନ୍ତୁ"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"ଅଗ୍ରାହ୍ୟ କର"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"ସମାପ୍ତ କରନ୍ତୁ"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"ଇନକମିଂ କଲ୍"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ଏହି ବିଜ୍ଞପ୍ତିକୁ ନୀରବ ଭାବେ ଡିମୋଟ୍ କରାଯାଇଛି। ମତାମତ ପ୍ରଦାନ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"ଏହି ବିଜ୍ଞପ୍ତିର ରେଙ୍କ ଉପରକୁ କରାଯାଇଛି। ମତାମତ ପ୍ରଦାନ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"ଏହି ବିଜ୍ଞପ୍ତିର ରେଙ୍କ ତଳକୁ କରାଯାଇଛି। ମତାମତ ପ୍ରଦାନ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"ଉନ୍ନତ ବିଜ୍ଞପ୍ତି ବ୍ୟବହାରକରି ଦେଖ"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"ପ୍ରସ୍ତାବିତ କାର୍ଯ୍ୟ, ପ୍ରତ୍ୟୁତ୍ତର ଏବଂ ଆହୁରି ଅନେକ କିଛି ପାଇବା ଜାରି ରଖିବାକୁ, ଉନ୍ନତ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଚାଲୁ କରନ୍ତୁ। Android ଆଡେପ୍ଟିଭ୍ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଆଉ ସମର୍ଥିତ ନୁହେଁ।"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"ଚାଲୁ କରନ୍ତୁ"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"ବର୍ତ୍ତମାନ ନୁହେଁ"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"ଅଧିକ ଜାଣନ୍ତୁ"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"ଉନ୍ନତ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଯୋଗାଯୋଗ ନାମ ଏବଂ ମେସେଜଗୁଡ଼ିକ ପରି ବ୍ୟକ୍ତିଗତ ସୂଚନା ସମେତ ସମସ୍ତ ବିଜ୍ଞପ୍ତି ବିଷୟବସ୍ତୁକୁ ପଢ଼ିପାରିବ। ଏହି ଫିଚର୍ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଖାରଜ କରିପାରିବ କିମ୍ବା ଫୋନ୍ କଲଗୁଡ଼ିକର ଉତ୍ତର ଦେବା ପରି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକରେ ଥିବା ବଟନଗୁଡ଼ିକ ଉପରେ ପଦକ୍ଷେପ ମଧ୍ୟ ନେଇପାରିବ।\n\nଏହି ଫିଚର୍ ପ୍ରାଥମିକତା ମୋଡକୁ ଚାଲୁ କିମ୍ବା ବନ୍ଦ କରିପାରିବ ଏବଂ ସମ୍ବନ୍ଧିତ ସେଟିଂସକୁ ପରିବର୍ତ୍ତନ ମଧ୍ୟ କରିପାରିବ।"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ନିୟମିତ ମୋଡ୍ ସୂଚନା ବିଜ୍ଞପ୍ତି"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ସାଧାରଣ ଭାବରେ ଚାର୍ଜ୍ କରିବା ପୂର୍ବରୁ ବ୍ୟାଟେରୀ ସରିଯାଇପାରେ"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ବ୍ୟାଟେରୀର ସମୟକୁ ବଢ଼ାଇବା ପାଇଁ ବ୍ୟଟେରୀ ସେଭର୍କୁ କାର୍ଯ୍ୟକାରୀ କରାଯାଇଛି"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index fc8a56b..ac5af41 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -319,10 +319,8 @@
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"ਆਪਣੀ ਸਰੀਰਕ ਸਰਗਰਮੀ ਤੱਕ ਪਹੁੰਚ ਕਰੋ"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"ਕੈਮਰਾ"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"ਤਸਵੀਰਾਂ ਲੈਣ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰਨ"</string>
- <!-- no translation found for permgrouplab_nearby_devices (5529147543651181991) -->
- <skip />
- <!-- no translation found for permgroupdesc_nearby_devices (3213561597116913508) -->
- <skip />
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸ"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸਾਂ ਨੂੰ ਖੋਜੋ ਅਤੇ ਉਹਨਾਂ ਨਾਲ ਕਨੈਕਟ ਕਰੋ"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"ਕਾਲ ਲੌਗ"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"ਫ਼ੋਨ ਦੇ ਕਾਲ ਲੌਗ ਨੂੰ ਪੜ੍ਹੋ ਅਤੇ ਲਿਖੋ"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"ਫ਼ੋਨ"</string>
@@ -540,14 +538,10 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"ਐਪ ਨੂੰ ਨਜ਼ਦੀਕੀ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਨੂੰ ਖੋਜਣ ਅਤੇ ਉਹਨਾਂ ਨਾਲ ਜੋੜਾਬੱਧ ਕਰਨ ਦਿੰਦੀ ਹੈ"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"ਜੋੜਾਬੱਧ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਨਾਲ ਕਨੈਕਟ ਕਰੋ"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"ਐਪਾਂ ਨੂੰ ਜੋੜਾਬੱਧ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਨਾਲ ਕਨੈਕਟ ਕਰਨ ਦਿੰਦੀ ਹੈ"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
- <!-- no translation found for permlab_uwb_ranging (8141915781475770665) -->
- <skip />
- <!-- no translation found for permdesc_uwb_ranging (2519723069604307055) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"ਨਜ਼ਦੀਕੀ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ \'ਤੇ ਵਿਗਿਆਪਨ ਦਿਓ"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"ਐਪ ਨੂੰ ਨਜ਼ਦੀਕੀ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ \'ਤੇ ਵਿਗਿਆਪਨ ਦੇਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ"</string>
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"ਨਜ਼ਦੀਕੀ ਅਲਟ੍ਰਾ-ਵਾਈਡਬੈਂਡ ਡੀਵਾਈਸਾਂ ਵਿਚਾਲੇ ਸੰਬੰਧਿਤ ਸਥਿਤੀ ਨਿਰਧਾਰਿਤ ਕਰੋ"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ਐਪ ਨੂੰ ਨਜ਼ਦੀਕੀ ਅਲਟ੍ਰਾ-ਵਾਈਡਬੈਂਡ ਡੀਵਾਈਸਾਂ ਦੇ ਵਿਚਾਲੇ ਸੰਬੰਧਿਤ ਸਥਿਤੀ ਨੂੰ ਨਿਰਧਾਰਿਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ਤਰਜੀਹੀ NFC ਭੁਗਤਾਨਸ਼ੁਦਾ ਸੇਵਾ ਜਾਣਕਾਰੀ"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ਐਪ ਨੂੰ ਤਰਜੀਹੀ NFC ਭੁਗਤਾਨਸ਼ੁਦਾ ਸੇਵਾ ਜਾਣਕਾਰੀ ਪ੍ਰਾਪਤ ਕਰਨ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਕਿ ਰਜਿਸਟਰ ਕੀਤੇ ਸਾਧਨ ਅਤੇ ਮੰਜ਼ਿਲ ਰਸਤਾ।"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"ਨਜ਼ਦੀਕੀ ਖੇਤਰ ਸੰਚਾਰ ਤੇ ਨਿਯੰਤਰਣ ਪਾਓ"</string>
@@ -1700,7 +1694,7 @@
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"ਜੇਕਰ ਤੁਸੀਂ <xliff:g id="SERVICE">%1$s</xliff:g> ਚਾਲੂ ਕਰਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਇਨਕ੍ਰਿਪਸ਼ਨ ਦਾ ਵਿਸਤਾਰ ਕਰਨ ਲਈ ਤੁਹਾਡੇ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕਰੇਗਾ।"</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"ਪੂਰਾ ਕੰਟਰੋਲ ਉਹਨਾਂ ਐਪਾਂ ਲਈ ਢੁਕਵਾਂ ਹੈ ਜੋ ਪਹੁੰਚਯੋਗਤਾ ਸੰਬੰਧੀ ਲੋੜਾਂ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਕਰਦੀਆਂ ਹਨ, ਪਰ ਜ਼ਿਆਦਾਤਰ ਐਪਾਂ ਲਈ ਢੁਕਵਾਂ ਨਹੀਂ ਹੁੰਦਾ।"</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"ਸਕ੍ਰੀਨ ਨੂੰ ਦੇਖੋ ਅਤੇ ਕੰਟਰੋਲ ਕਰੋ"</string>
- <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"ਇਹ ਸਕ੍ਰੀਨ \'ਤੇ ਸਾਰੀ ਸਮੱਗਰੀ ਪੜ੍ਹ ਸਕਦੀ ਹੈ ਅਤੇ ਸਮੱਗਰੀ ਨੂੰ ਦੂਜੀਆਂ ਐਪਾਂ ਦੇ ਉੱਪਰ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰ ਸਕਦੀ ਹੈ।"</string>
+ <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"ਇਹ ਸਕ੍ਰੀਨ \'ਤੇ ਸਾਰੀ ਸਮੱਗਰੀ ਪੜ੍ਹ ਸਕਦੀ ਹੈ ਅਤੇ ਸਮੱਗਰੀ ਨੂੰ ਦੂਜੀਆਂ ਐਪਾਂ ਦੇ ਉੱਪਰ ਦਿਖਾ ਸਕਦੀ ਹੈ।"</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"ਕਾਰਵਾਈਆਂ ਦੇਖੋ ਅਤੇ ਕਰੋ"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ਇਹ ਕਿਸੇ ਐਪ ਜਾਂ ਹਾਰਡਵੇਅਰ ਸੈਂਸਰ ਦੇ ਨਾਲ ਤੁਹਾਡੀਆਂ ਅੰਤਰਕਿਰਿਆਵਾਂ ਨੂੰ ਟਰੈਕ ਕਰ ਸਕਦੀ ਹੈ, ਅਤੇ ਤੁਹਾਡੀ ਤਰਫ਼ੋਂ ਐਪਾਂ ਦੇ ਨਾਲ ਅੰਤਰਕਿਰਿਆ ਕਰ ਸਕਦੀ ਹੈ।"</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"ਕਰਨ ਦਿਓ"</string>
@@ -1858,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"ਬੈਟਰੀ ਸੇਵਰ ਗੂੜ੍ਹੇ ਥੀਮ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਸਰਗਰਮੀ, ਕੁਝ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਭਾਵਾਂ ਅਤੇ \"Ok Google\" ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਸੀਮਤ ਕਰਦਾ ਹੈ ਜਾਂ ਬੰਦ ਕਰਦਾ ਹੈ\n\n"<annotation id="url">"ਹੋਰ ਜਾਣੋ"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"ਬੈਟਰੀ ਸੇਵਰ ਗੂੜ੍ਹੇ ਥੀਮ ਨੂੰ ਚਾਲੂ ਕਰਦਾ ਹੈ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਸਰਗਰਮੀ, ਕੁਝ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਭਾਵਾਂ ਅਤੇ \"Ok Google\" ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਸੀਮਤ ਕਰਦਾ ਹੈ ਜਾਂ ਬੰਦ ਕਰਦਾ ਹੈ।"</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>
@@ -1937,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"ਬੰਦ ਕਰੋ"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"ਜਵਾਬ ਦਿਓ"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"ਸਮਾਪਤ ਕਰੋ"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"ਇਨਕਮਿੰਗ ਕਾਲ"</string>
@@ -1954,7 +1948,7 @@
<string name="user_creation_adding" msgid="7305185499667958364">"ਕੀ <xliff:g id="APP">%1$s</xliff:g> ਨੂੰ <xliff:g id="ACCOUNT">%2$s</xliff:g> ਨਾਲ ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਬਣਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣੀ ਹੈ?"</string>
<string name="language_selection_title" msgid="52674936078683285">"ਇੱਕ ਭਾਸ਼ਾ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="country_selection_title" msgid="5221495687299014379">"ਖੇਤਰ ਤਰਜੀਹ"</string>
- <string name="search_language_hint" msgid="7004225294308793583">"ਭਾਸ਼ਾ ਨਾਮ ਟਾਈਪ ਕਰੋ"</string>
+ <string name="search_language_hint" msgid="7004225294308793583">"ਭਾਸ਼ਾ ਦਾ ਨਾਮ ਟਾਈਪ ਕਰੋ"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"ਸੁਝਾਈਆਂ ਗਈਆਂ"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"ਸਾਰੀਆਂ ਭਾਸ਼ਾਵਾਂ"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"ਸਾਰੇ ਖੇਤਰ"</string>
@@ -2080,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ਇਸ ਸੂਚਨਾ ਦਾ ਦਰਜਾ ਘਟਾ ਕੇ ਸ਼ਾਂਤ \'ਤੇ ਸੈੱਟ ਕੀਤਾ ਗਿਆ। ਵਿਚਾਰ ਮੁਹੱਈਆ ਕਰਵਾਉਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"ਇਸ ਸੂਚਨਾ ਦਾ ਦਰਜਾ ਵਧਾ ਦਿੱਤਾ ਗਿਆ। ਵਿਚਾਰ ਮੁਹੱਈਆ ਕਰਵਾਉਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"ਇਸ ਸੂਚਨਾ ਦਾ ਦਰਜਾ ਘਟਾ ਦਿੱਤਾ ਗਿਆ। ਵਿਚਾਰ ਮੁਹੱਈਆ ਕਰਵਾਉਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"ਵਿਸਤ੍ਰਿਤ ਸੂਚਨਾਵਾਂ ਅਜ਼ਮਾਓ"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"ਸੁਝਾਈਆਂ ਗਈਆਂ ਕਾਰਵਾਈਆਂ, ਜਵਾਬਾਂ ਅਤੇ ਹੋਰ ਬਹੁਤ ਕੁਝ ਨੂੰ ਪ੍ਰਾਪਤ ਕਰਦੇ ਰਹਿਣ ਲਈ, ਵਿਸਤ੍ਰਿਤ ਸੂਚਨਾਵਾਂ ਨੂੰ ਚਾਲੂ ਕਰੋ। Android ਅਡੈਪਟਿਵ ਸੂਚਨਾਵਾਂ ਹੁਣ ਸਮਰਥਿਤ ਨਹੀਂ ਹਨ।"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"ਚਾਲੂ ਕਰੋ"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"ਹੁਣੇ ਨਹੀਂ"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"ਹੋਰ ਜਾਣੋ"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"ਵਿਸਤ੍ਰਿਤ ਸੂਚਨਾਵਾਂ ਸਾਰੀ ਸੂਚਨਾ ਸਮੱਗਰੀ ਨੂੰ ਪੜ੍ਹ ਸਕਦੀਆਂ ਹਨ, ਜਿਸ ਵਿੱਚ ਸੰਪਰਕ ਨਾਮ ਅਤੇ ਸੁਨੇਹੇ ਵਰਗੀ ਨਿੱਜੀ ਜਾਣਕਾਰੀ ਵੀ ਸ਼ਾਮਲ ਹੈ। ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਸੂਚਨਾਵਾਂ ਨੂੰ ਖਾਰਜ ਵੀ ਕਰ ਸਕਦੀ ਹੈ ਜਾਂ ਸੂਚਨਾਵਾਂ ਵਿੱਚ ਬਟਨਾਂ \'ਤੇ ਕਾਰਵਾਈਆਂ ਵੀ ਕਰ ਸਕਦੀ ਹੈ, ਜਿਵੇਂ ਕਿ ਫ਼ੋਨ ਕਾਲਾਂ ਦਾ ਜਵਾਬ ਦੇਣਾ।\n\nਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਤਰਜੀਹੀ ਮੋਡ ਨੂੰ ਚਾਲੂ ਜਾਂ ਬੰਦ ਵੀ ਕਰ ਸਕਦੀ ਹੈ ਅਤੇ ਸੰਬੰਧਿਤ ਸੈਟਿੰਗਾਂ ਨੂੰ ਬਦਲ ਸਕਦੀ ਹੈ।"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ਨਿਯਮਬੱਧ ਮੋਡ ਦੀ ਜਾਣਕਾਰੀ ਵਾਲੀ ਸੂਚਨਾ"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ਬੈਟਰੀ ਚਾਰਜ ਕਰਨ ਦੇ ਮਿੱਥੇ ਸਮੇਂ ਤੋਂ ਪਹਿਲਾਂ ਸ਼ਾਇਦ ਬੈਟਰੀ ਖਤਮ ਹੋ ਜਾਵੇ"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ਬੈਟਰੀ ਲਾਈਫ਼ ਵਧਾਉਣ ਲਈ ਬੈਟਰੀ ਸੇਵਰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
@@ -2276,17 +2264,13 @@
<string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
<string name="window_magnification_prompt_title" msgid="2876703640772778215">"ਨਵੀਆਂ ਵੱਡਦਰਸ਼ੀਕਰਨ ਸੈਟਿੰਗਾਂ"</string>
- <string name="window_magnification_prompt_content" msgid="8159173903032344891">"ਹੁਣ ਤੁਸੀਂ ਆਪਣੀ ਸਕ੍ਰੀਨ ਦਾ ਕੁਝ ਹਿੱਸਾ ਵੱਡਦਰਸ਼ੀ ਕਰ ਸਕਦੇ ਹੋ"</string>
+ <string name="window_magnification_prompt_content" msgid="8159173903032344891">"ਹੁਣ ਤੁਸੀਂ ਆਪਣੀ ਸਕ੍ਰੀਨ ਦਾ ਕੁਝ ਹਿੱਸਾ ਵੱਡਾ ਕਰ ਸਕਦੇ ਹੋ"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਚਾਲੂ ਕਰੋ"</string>
<string name="dismiss_action" msgid="1728820550388704784">"ਖਾਰਜ ਕਰੋ"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"ਡੀਵਾਈਸ ਦੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰੋ"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਨੂੰ ਅਣਬਲਾਕ ਕਰੋ"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> ਅਤੇ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"ਅਣਬਲਾਕ ਕਰੋ"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ਸੈਂਸਰ ਪਰਦੇਦਾਰੀ"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ਐਪਲੀਕੇਸ਼ਨ ਪ੍ਰਤੀਕ"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ਐਪਲੀਕੇਸ਼ਨ ਦਾ ਬ੍ਰਾਂਡ ਵਾਲਾ ਚਿੱਤਰ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 0d07cab..89279d4 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -544,10 +544,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Zezwala na wykrywanie i parowanie przez aplikację urządzeń Bluetooth w pobliżu"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"łączenie ze sparowanymi urządzeniami Bluetooth"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Zezwala na łączenie aplikacji ze sparowanymi urządzeniami Bluetooth"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"kierowanie informacji do urządzeń Bluetooth w pobliżu"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Zezwala na kierowanie przez aplikację informacji do urządzeń Bluetooth w pobliżu"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"określanie względnego położenia urządzeń ultraszerokopasmowych w pobliżu"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Zezwól na określanie przez aplikację względnego położenia urządzeń ultraszerokopasmowych w pobliżu"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacje o preferowanych usługach płatniczych NFC"</string>
@@ -1900,10 +1898,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Oszczędzanie baterii uruchamia ciemny motyw oraz wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne i inne funkcje, np. „OK Google”\n\n"<annotation id="url">"Więcej informacji"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Oszczędzanie baterii uruchamia ciemny motyw oraz wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne i inne funkcje, np. „OK Google”."</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>
@@ -1995,6 +1991,8 @@
<string name="close_button_text" msgid="10603510034455258">"Zamknij"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Odbierz"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Odrzuć"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Rozłącz"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Połączenie przychodzące"</string>
@@ -2142,18 +2140,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"To powiadomienie zostało zmienione na Ciche. Kliknij, by przesłać opinię."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Podniesiono ważność tego powiadomienia. Kliknij, by przesłać opinię."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Obniżono ważność tego powiadomienia. Kliknij, by przesłać opinię."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Wypróbuj ulepszone powiadomienia"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Aby nadal otrzymywać sugestie działań oraz odpowiedzi i inne podpowiedzi, włącz ulepszone powiadomienia. Powiadomienia adaptacyjne w Androidzie nie są już obsługiwane."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Włącz"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Nie teraz"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Więcej informacji"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Ulepszone powiadomienia mogą czytać wszystkie powiadomienia, w tym dane osobowe takie jak nazwy kontaktów i treść wiadomości. Funkcja będzie też mogła odrzucać powiadomienia oraz używać zawartych w nich przycisków, np. odbierać połączenia telefoniczne.\n\nMoże również włączać i wyłączać tryb Priorytet oraz zmieniać powiązane ustawienia."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Powiadomienie z informacją o trybie rutynowym"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Bateria może się wyczerpać przed zwykłą porą ładowania"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Włączono Oszczędzanie baterii, by wydłużyć czas pracy na baterii"</string>
@@ -2343,14 +2335,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Możesz teraz powiększyć część ekranu"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Włącz w Ustawieniach"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Odrzuć"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Odblokuj mikrofon urządzenia"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Odblokuj aparat urządzenia"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Dla aplikacji „<xliff:g id="APP">%s</xliff:g>” i wszystkich aplikacji oraz usług"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Odblokuj"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Poufność danych z czujników"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikona aplikacji"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Wizerunek marki aplikacji"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 69da283..102073e 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1852,10 +1852,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Atualizado pelo seu administrador"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Excluído pelo seu administrador"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"A Economia de bateria ativa o tema escuro e limita ou desativa as atividades em segundo plano, alguns efeitos visuais e recursos como o \"Ok Google\"\n\n"<annotation id="url">"Saiba mais"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"A Economia de bateria ativa o tema escuro e limita ou desativa as atividades em segundo plano, alguns efeitos visuais e recursos como o \"Ok Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você esteja usando no momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não sejam exibidas até que você toque nelas."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Ativar \"Economia de dados\"?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string>
@@ -1903,7 +1901,7 @@
<string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Durante a semana à noite"</string>
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
- <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormindo"</string>
+ <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Há um problema interno com seu dispositivo. Ele pode ficar instável até que você faça a redefinição para configuração original."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Há um problema interno com seu dispositivo. Entre em contato com o fabricante para saber mais detalhes."</string>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Fechar"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Atender"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Vídeo"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Recusar"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Desligar"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada recebida"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Esta notificação foi rebaixada a Silenciosa. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Esta notificação foi classificada com maior prioridade. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Esta notificação foi classificada com menor prioridade. Toque para enviar seu feedback."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Testar notif. aprimoradas"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Para continuar a receber sugestões de ações, respostas e muito mais, ative as notificações aprimoradas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Ativar"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Agora não"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saiba mais"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"As notificações aprimoradas podem ler todo o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Além disso, esse recurso pode dispensar notificações e usar os botões delas para, por exemplo, atender chamadas telefônicas.\n\nEle também pode ativar ou desativar o modo Prioridade e mudar as configurações relacionadas."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informação do modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A bateria pode acabar antes da recarga normal"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"A Economia de bateria foi ativada para aumentar a duração da carga"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 04e282f..019107a 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -538,8 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Permite que a app descubra e sincronize com dispositivos Bluetooth próximos"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"ligar-se a dispositivos Bluetooth sincronizados"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Permite que a app se ligue a dispositivos Bluetooth sincronizados"</string>
- <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"mostrar anúncios a dispositivos Bluetooth próximos"</string>
- <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite que a app apresente anúncios a dispositivos Bluetooth próximos"</string>
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"transmitir para dispositivos Bluetooth próximos"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite que a app transmita para dispositivos Bluetooth próximos"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"determinar posição relativa dispos. de banda ultralarga próximos"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permita que a app determine a posição relativa entre os dispositivos de banda ultralarga próximos"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informações de serviços de pagamento com NFC preferenciais"</string>
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"A Poupança de bateria ativa o tema escuro e limita ou desativa a atividade em segundo plano, alguns efeitos visuais e funcionalidades como \"Ok Google\".\n\n"<annotation id="url">"Saiba mais"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"A Poupança de bateria ativa o tema escuro e limita ou desativa a atividade em segundo plano, alguns efeitos visuais e funcionalidades como \"Ok Google\"."</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>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Fechar"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Atender"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Vídeo"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Recusar"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Desligar"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada recebida"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Esta notificação foi despromovida para Silenciosa. Toque para fornecer feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Esta notificação passou para uma classificação superior. Toque para fornecer feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Esta notificação passou para uma classificação inferior. Toque para fornecer feedback."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Experimente as not. melhoradas"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Para continuar a obter sugestões de ações, respostas e muito mais, ative as notificações melhoradas. As notificações adaptáveis do Android já não são suportadas."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Ativar"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Agora não"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saber mais"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"As notificações melhoradas podem ler todo o conteúdo das notificações, incluindo informações pessoais como nomes de contactos e mensagens. Esta funcionalidade também pode ignorar notificações ou acionar botões em notificações, como atender chamadas telefónicas.\n\nAlém disso, esta funcionalidades pode ativar ou desativar o modo Prioridade e alterar as definições relacionadas."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informações do Modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Pode ficar sem bateria antes do carregamento habitual"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Poupança de bateria ativada para prolongar a duração da bateria"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 69da283..102073e 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1852,10 +1852,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Atualizado pelo seu administrador"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Excluído pelo seu administrador"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"A Economia de bateria ativa o tema escuro e limita ou desativa as atividades em segundo plano, alguns efeitos visuais e recursos como o \"Ok Google\"\n\n"<annotation id="url">"Saiba mais"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"A Economia de bateria ativa o tema escuro e limita ou desativa as atividades em segundo plano, alguns efeitos visuais e recursos como o \"Ok Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você esteja usando no momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não sejam exibidas até que você toque nelas."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Ativar \"Economia de dados\"?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Ativar"</string>
@@ -1903,7 +1901,7 @@
<string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Durante a semana à noite"</string>
<string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Fim de semana"</string>
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"Evento"</string>
- <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormindo"</string>
+ <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Dormir"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> está silenciando alguns sons"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"Há um problema interno com seu dispositivo. Ele pode ficar instável até que você faça a redefinição para configuração original."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"Há um problema interno com seu dispositivo. Entre em contato com o fabricante para saber mais detalhes."</string>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Fechar"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Atender"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Vídeo"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Recusar"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Desligar"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada recebida"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Esta notificação foi rebaixada a Silenciosa. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Esta notificação foi classificada com maior prioridade. Toque para enviar seu feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Esta notificação foi classificada com menor prioridade. Toque para enviar seu feedback."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Testar notif. aprimoradas"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Para continuar a receber sugestões de ações, respostas e muito mais, ative as notificações aprimoradas. As Notificações adaptáveis do Android não estão mais disponíveis."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Ativar"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Agora não"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Saiba mais"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"As notificações aprimoradas podem ler todo o conteúdo das notificações, incluindo informações pessoais como nomes de contatos e mensagens. Além disso, esse recurso pode dispensar notificações e usar os botões delas para, por exemplo, atender chamadas telefônicas.\n\nEle também pode ativar ou desativar o modo Prioridade e mudar as configurações relacionadas."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificação de informação do modo rotina"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"A bateria pode acabar antes da recarga normal"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"A Economia de bateria foi ativada para aumentar a duração da carga"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 8c145bf2..d54829a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -541,10 +541,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Permite aplicației să descopere și să asocieze dispozitive Bluetooth din apropiere"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"să se conecteze la dispozitive Bluetooth asociate"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Permite aplicației să se conecteze la dispozitive Bluetooth asociate"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"să difuzeze anunțuri pe dispozitive Bluetooth din apropiere"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite aplicației să difuzeze anunțuri pe dispozitive Bluetooth din apropiere"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"să stabilească poziția relativă dintre dispozitivele Ultra-Wideband din apropiere"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permiteți-i aplicației să stabilească poziția relativă dintre dispozitivele Ultra-Wideband din apropiere"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informații despre serviciul de plăți NFC preferat"</string>
@@ -1877,10 +1875,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Economisirea bateriei activează Tema întunecată și limitează sau dezactivează activitatea din fundal, anumite efecte vizuale și funcții precum „Ok Google”\n\n"<annotation id="url">"Aflați mai multe"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Economisirea bateriei activează Tema întunecată și limitează sau dezactivează activitatea din fundal, anumite efecte vizuale și funcții precum „Ok Google”."</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>
@@ -1964,6 +1960,8 @@
<string name="close_button_text" msgid="10603510034455258">"Închideți"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Răspundeți"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Respingeți"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Încheiați"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Apel primit"</string>
@@ -2109,18 +2107,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Notificarea a fost mutată în jos la Silențioasă. Atingeți pentru a oferi feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Notificarea a fost mutată la un nivel superior. Atingeți pentru a oferi feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Notificarea a fost mutată la un nivel inferior. Atingeți pentru a oferi feedback."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Încercați notificările optimizate"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Ca să primiți în continuare acțiuni sugerate, răspunsuri și altele, activați notificările optimizate. Notificările adaptive Android nu mai sunt acceptate."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Activați"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Nu acum"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Aflați mai multe"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Notificările optimizate pot citi tot conținutul notificărilor, inclusiv informații cu caracter personal, precum mesajele și numele persoanelor de contact. În plus, funcția poate să închidă notificări sau să acționeze asupra butoanelor din notificări, inclusiv să răspundă la apeluri telefonice.\n\nÎn plus, funcția poate să activeze sau să dezactiveze modul Cu prioritate și să schimbe setările asociate."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificare pentru informații despre modul Rutină"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Bateria se poate descărca înainte de încărcarea obișnuită"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Economisirea bateriei este activată pentru a prelungi durata de funcționare a bateriei"</string>
@@ -2309,14 +2301,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Acum puteți mări o parte a ecranului"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Activați din Setări"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Respingeți"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Deblocați microfonul dispozitivului"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Deblocați camera dispozitivului"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Pentru <b><xliff:g id="APP">%s</xliff:g></b> și toate aplicațiile și serviciile"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Deblocați"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Confidențialitatea privind senzorii"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Pictograma aplicației"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imaginea de branding a aplicației"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 4612662..76b3991 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1350,7 +1350,7 @@
<string name="network_switch_metered_detail" msgid="1358296010128405906">"Устройство использует <xliff:g id="NEW_NETWORK">%1$s</xliff:g>, если подключение к сети <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> недоступно. Может взиматься плата за передачу данных."</string>
<string name="network_switch_metered_toast" msgid="501662047275723743">"Устройство отключено от сети <xliff:g id="NEW_NETWORK">%2$s</xliff:g> и теперь использует <xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g>"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобильный Интернет"</item>
+ <item msgid="2255670471736226365">"мобильный интернет"</item>
<item msgid="5520925862115353992">"Wi-Fi"</item>
<item msgid="1055487873974272842">"Bluetooth"</item>
<item msgid="1616528372438698248">"Ethernet"</item>
@@ -1426,7 +1426,7 @@
<string name="share_remote_bugreport_action" msgid="7630880678785123682">"ПРЕДОСТАВИТЬ ДОСТУП"</string>
<string name="decline_remote_bugreport_action" msgid="4040894777519784346">"ОТКЛОНИТЬ"</string>
<string name="select_input_method" msgid="3971267998568587025">"Выберите способ ввода"</string>
- <string name="show_ime" msgid="6406112007347443383">"Показывать на экране, когда физическая клавиатура включена"</string>
+ <string name="show_ime" msgid="6406112007347443383">"Не скрывать экранную клавиатуру, когда включена физическая"</string>
<string name="hardware" msgid="1800597768237606953">"Виртуальная клавиатура"</string>
<string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Настройка физической клавиатуры"</string>
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Нажмите, чтобы выбрать язык и раскладку"</string>
@@ -1898,10 +1898,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"В режиме энергосбережения включается тёмная тема. Кроме того, отключаются или ограничиваются фоновые процессы, некоторые визуальные эффекты и различные функции, например распознавание команды \"Окей, Google\".\n\n"<annotation id="url">"Подробнее…"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"В режиме энергосбережения включается тёмная тема. Кроме того, отключаются или ограничиваются фоновые процессы, некоторые визуальные эффекты и различные функции, например распознавание команды \"Окей, Google\"."</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>
@@ -1993,6 +1991,7 @@
<string name="close_button_text" msgid="10603510034455258">"Закрыть"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Ответить"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Видео"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Отклонить"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Завершить"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Входящий вызов"</string>
@@ -2010,9 +2009,9 @@
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Уведомление пользовательского приложения"</string>
<string name="user_creation_account_exists" msgid="2239146360099708035">"Разрешить приложению \"<xliff:g id="APP">%1$s</xliff:g>\" создать нового пользователя с аккаунтом <xliff:g id="ACCOUNT">%2$s</xliff:g> (пользователь с этим аккаунтом уже существует)?"</string>
<string name="user_creation_adding" msgid="7305185499667958364">"Разрешить приложению \"<xliff:g id="APP">%1$s</xliff:g>\" создать нового пользователя с аккаунтом <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="language_selection_title" msgid="52674936078683285">"Добавьте язык"</string>
+ <string name="language_selection_title" msgid="52674936078683285">"Добавить язык"</string>
<string name="country_selection_title" msgid="5221495687299014379">"Региональные настройки"</string>
- <string name="search_language_hint" msgid="7004225294308793583">"Введите язык"</string>
+ <string name="search_language_hint" msgid="7004225294308793583">"Введите название языка"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Рекомендуемые"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Все языки"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"Все регионы"</string>
@@ -2140,18 +2139,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Уровень важности этого уведомления был понижен до \"Без звука\". Нажмите, чтобы отправить отзыв."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Уровень важности этого уведомления был повышен. Нажмите, чтобы отправить отзыв."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Уровень важности этого уведомления был понижен. Нажмите, чтобы отправить отзыв."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Улучшенные уведомления"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Чтобы по-прежнему пользоваться рекомендуемыми действиями, ответами и другими подсказками, включите улучшенные уведомления. Адаптивные уведомления для Android больше не поддерживаются."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Включить"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Не сейчас"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Подробнее"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Улучшенным уведомлениям доступно содержимое всех уведомлений, в том числе личная информация, такая как имена контактов и сообщения. У этой функции также есть право закрывать уведомления и нажимать кнопки в них, например отвечать на звонки.\n\nКроме того, улучшенные уведомления могут включать и отключать режим \"Только важные\" и изменять связанные с ним настройки."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Уведомление о батарее"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батарея может разрядиться"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Чтобы увеличить время работы от батареи, был включен режим энергосбережения."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 5523d8f..c0577d6 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"බැටරි සුරැකුම අඳුරු තේමාව ක්රියාත්මක කර පසුබිම් ක්රියාකාරකම්, සමහර දෘශ්ය ප්රයෝග සහ “Hey Google” වැනි විශේෂාංග සීමා කරයි\n\n"<annotation id="url">"තව දැන ගන්න"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"බැටරි සුරැකුම අඳුරු තේමාව ක්රියාත්මක කර පසුබිම් ක්රියාකාරකම්, සමහර දෘශ්ය ප්රයෝග සහ “Hey Google” වැනි විශේෂාංග සීමා කරයි."</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>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"වසන්න"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"පිළිතුරු දෙ."</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"වීඩියෝ"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"ප්රතික්ෂේප ක"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"විසන්ධි කරන්න"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"එන ඇමතුම"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"මෙම දැනුම්දීම නිහඬ වෙත පහත දමන ලදී. ප්රතිපෝෂණය ලබා දීමට තට්ටු කරන්න."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"මෙම දැනුම්දීම ඉහළට ශ්රේණිගත කරන ලදී. ප්රතිපෝෂණය ලබා දීමට තට්ටු කරන්න."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"මෙම දැනුම්දීම පහළට ශ්රේණිගත කරන ලදී. ප්රතිපෝෂණය ලබා දීමට තට්ටු කරන්න."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"දියුණු කළ දැනුම්දීම් උත්සාහ ක."</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"යෝජිත ක්රියා, පිළිතුරු සහ තවත් දේ ලබා ගැනීම සඳහා, වැඩි දියුණු කළ දැනුම්දීම් ක්රියාත්මක කරන්න. Android අනුවර්තී දැනුම්දීම් තවදුරටත් සහාය නොදක්වයි."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"ක්රියාත්මක කරන්න"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"දැන් නොවේ"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"තව දැන ගන්න"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"වැඩිදියුණු කළ දැනුම්දීම්වලට සම්බන්ධතා නම් සහ පණිවිඩ වැනි පුද්ගලික තොරතුරු ඇතුළුව, සියලු දැනුම්දීම් අන්තර්ගතය කියවිය හැකිය. මෙම විශේෂාංගයට දැනුම්දීම් ඉවත දැමීමට හෝ දුරකථන ඇමතුම්වලට පිළිතුරු දීම වැනි, දැනුම්දීම්වල බොත්තම් මත ක්රියා සිදු කිරීමටද හැකිය.\n\nමෙම විශේෂාංගයට ප්රමුඛතා ප්රකාරය ක්රියාත්මක හෝ ක්රියාවිරහිත කිරීමට සහ අදාළ සැකසීම් වෙනස් කිරීමටද හැකිය."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"දිනචරියා ප්රකාර තතු දැනුම්දීම"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"බැටරිය සුපුරුදු ආරෝපණයට පෙර ඉවර විය හැක"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"බැටරි සුරැකුම බැටරි ආයු කාලය දීර්ඝ කිරීමට සක්රිය කෙරිණි"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index b2ee98a..806e5a0 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1898,10 +1898,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Aktualizoval správca"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Odstránil správca"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Šetrič batérie zapne tmavý motív a obmedzí alebo vypne aktivitu na pozadí, niektoré vizuálne efekty a funkcie, napríklad „Hey Google“\n\n"<annotation id="url">"Ďalšie informácie"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Šetrič batérie zapne tmavý motív a obmedzí alebo vypne aktivitu na pozadí, niektoré vizuálne efekty a funkcie, napríklad „Hey Google“."</string>
<string name="data_saver_description" msgid="4995164271550590517">"S cieľom znížiť spotrebu dát bráni šetrič dát niektorým aplikáciám odosielať alebo prijímať dáta na pozadí. Aplikácia, ktorú práve používate, môže využívať dáta, ale možno to bude robiť menej často. Môže to napríklad znamenať, že sa obrázky zobrazia, až keď na ne klepnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Chcete zapnúť šetrič dát?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Zapnúť"</string>
@@ -1993,6 +1991,7 @@
<string name="close_button_text" msgid="10603510034455258">"Zavrieť"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Prijať"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Odmietnuť"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Zložiť"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Prichádzajúci hovor"</string>
@@ -2140,18 +2139,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Toto upozornenie bolo znížené na Tiché. Klepnutím nám poskytnite spätnú väzbu."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Toto upozornenie bolo preradené vyššie. Klepnutím nám poskytnite spätnú väzbu."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Toto upozornenie bolo preradené nižšie. Klepnutím nám poskytnite spätnú väzbu."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Skúste zlepšené upozornenia"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Ak chcete ďalej dostávať navrhované akcie, odpovede a ďalší obsah, zapnite zlepšené upozornenia. Adaptívne upozornenia Androidu už nie sú podporované."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Zapnúť"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Teraz nie"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Ďalšie informácie"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Zlepšené upozornenia môžu čítať všetok obsah upozornení vrátane osobných údajov, ako sú mená kontaktov a správy. Táto funkcia tiež môže rušiť upozornenia alebo aktivovať tlačidlá v upozorneniach, napríklad na prijatie telefonických hovorov.\n\nTáto funkcia môže tiež zapnúť alebo vypnúť režim priority a zmeniť súvisiace nastavenia."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Upozornenie s informáciami o rutinnom režime"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Batéria sa môže vybiť pred obvyklým nabitím"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Bol aktivovaný šetrič batérie na predĺženie výdrže batérie"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index d62aee7..b5a73ee 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -544,8 +544,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Aplikaciji omogoča odkrivanje naprav Bluetooth v bližini in seznanjanje z njimi."</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"povezovanje s seznanjenimi napravami Bluetooth"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Aplikaciji omogoča povezovanje s seznanjenimi napravami Bluetooth."</string>
- <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"oglaševanje v napravah Bluetooth v bližini"</string>
- <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Aplikaciji dovoljuje oglaševanje v napravah Bluetooth v bližini."</string>
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"oddajanje napravam Bluetooth v bližini"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Aplikaciji dovoljuje oddajanje napravam Bluetooth v bližini."</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"določanje relativne oddaljenosti med napravami UWB v bližini"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Aplikaciji dovoli, da določi relativno oddaljenost med napravami UWB v bližini."</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Podatki o prednostni storitvi za plačevanje prek povezave NFC"</string>
@@ -1898,10 +1898,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Varčevanje z energijo baterije vklopi temno temo in omeji ali izklopi dejavnost v ozadju, nekatere vizualne učinke in druge funkcije, kot je »Hey Google«.\n\n"<annotation id="url">"Več o tem"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Varčevanje z energijo baterije vklopi temno temo in omeji ali izklopi dejavnost v ozadju, nekatere vizualne učinke in druge funkcije, kot je »Hey Google«."</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">"Vklop"</string>
@@ -1993,6 +1991,8 @@
<string name="close_button_text" msgid="10603510034455258">"Zapri"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Sprejmi"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Zavrni"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Prekini klic"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Dohodni klic"</string>
@@ -2140,18 +2140,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"To obvestilo je bilo uvrščeno nižje – med obvestila brez zvoka. Dotaknite se, če želite poslati povratne informacije."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"To obvestilo je bilo uvrščeno višje. Dotaknite se, če želite poslati povratne informacije."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"To obvestilo je bilo uvrščeno nižje. Dotaknite se, če želite poslati povratne informacije."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Preizkusite pametna obvestila"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Če želite še naprej prejemati predlagana dejanja, odgovore in drugo, vklopite pametna obvestila. Prilagodljiva obvestila Android niso več podprta."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Vklopi"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Ne zdaj"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Več o tem"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Pametna obvestila lahko preberejo vso vsebino obvestil, vključno z osebnimi podatki, kot so imena in sporočila stikov. Ta funkcija lahko tudi opusti obvestila in izvaja dejanja z gumbi v obvestilih, kot je sprejemanje telefonskih klicev.\n\nPoleg tega lahko ta funkcija vklopi ali izklopi prednostni način ter spremeni povezane nastavitve."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Rutinsko informativno obvestilo o načinu delovanja"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Baterija se bo morda izpraznila, preden jo običajno priključite na polnjenje"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Vklopilo se je varčevanje z energijo baterije za podaljšanje časa delovanja baterije"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 5575f16..30bcbaf 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -319,10 +319,8 @@
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"qasje në aktivitetin tënd fizik"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"bëj fotografi dhe regjistro video"</string>
- <!-- no translation found for permgrouplab_nearby_devices (5529147543651181991) -->
- <skip />
- <!-- no translation found for permgroupdesc_nearby_devices (3213561597116913508) -->
- <skip />
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Pajisjet në afërsi"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"të zbulojë dhe të lidhet me pajisjet në afërsi"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"Evidencat e telefonatave"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"lexo dhe shkruaj evidencën e telefonatave"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"Telefoni"</string>
@@ -540,14 +538,10 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Lejon që aplikacioni të zbulojë dhe të çiftohet me pajisjet me Bluetooth në afërsi"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"të lidhet me pajisjet e çiftuara me Bluetooth"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Lejon që aplikacioni të lidhet me pajisjet e çiftuara me Bluetooth"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
- <!-- no translation found for permlab_uwb_ranging (8141915781475770665) -->
- <skip />
- <!-- no translation found for permdesc_uwb_ranging (2519723069604307055) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"të reklamojë në pajisjet me Bluetooth në afërsi"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Lejon që aplikacioni të reklamojë në pajisjet me Bluetooth në afërsi"</string>
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"të përcaktojë pozicionin e përafërt mes pajisjeve në afërsi me brezin ultra të gjerë"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Lejo që aplikacioni të përcaktojë pozicionin e përafërt mes pajisjeve në afërsi me brezin ultra të gjerë"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacionet për shërbimin e preferuar të pagesës me NFC"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Lejon aplikacionin të marrë informacione për shërbimin e preferuar të pagesës me NFC si p.sh. ndihmat e regjistruara dhe destinacionin e itinerarit."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"kontrollo \"Komunikimin e fushës në afërsi\" NFC"</string>
@@ -1273,12 +1267,12 @@
<string name="new_app_action" msgid="547772182913269801">"Hap <xliff:g id="NEW_APP">%1$s</xliff:g>"</string>
<string name="new_app_description" msgid="1958903080400806644">"<xliff:g id="OLD_APP">%1$s</xliff:g> do të mbyllet pa u ruajtur"</string>
<string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> e ka kaluar kufirin e memories"</string>
- <string name="dump_heap_ready_notification" msgid="2302452262927390268">"Stiva e skedarëve fiktivë të <xliff:g id="PROC">%1$s</xliff:g> është gati"</string>
- <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Stiva e skedarëve fiktivë është mbledhur. Trokit për t\'i ndarë."</string>
- <string name="dump_heap_title" msgid="4367128917229233901">"Të ndahet stiva e skedarëve fiktivë?"</string>
- <string name="dump_heap_text" msgid="1692649033835719336">"Procesi <xliff:g id="PROC">%1$s</xliff:g> ka kaluar kufirin e tij të memories prej <xliff:g id="SIZE">%2$s</xliff:g>. Ke një stivë të skedarësh fiktivë që mund ta ndash me zhvilluesin e tij. Ki kujdes pasi kjo stivë skedarësh fiktivë mund të përmbajë çdo informacion personal ku ka qasje aplikacioni."</string>
- <string name="dump_heap_system_text" msgid="6805155514925350849">"Procesi <xliff:g id="PROC">%1$s</xliff:g> e ka kaluar kufirin e tij të memories prej <xliff:g id="SIZE">%2$s</xliff:g>. Ke një stivë skedarësh fiktivë që mund ta ndash. Ki kujdes pasi kjo stivë skedarësh fiktivë mund të përmbajë çdo informacion personal delikat ku ka qasje procesi dhe mund të përfshijë gjërat që ke shkruar ti."</string>
- <string name="dump_heap_ready_text" msgid="5849618132123045516">"Ke një stivë skedarësh fiktivë të procesit <xliff:g id="PROC">%1$s</xliff:g> që mund ta ndash. Ki kujdes pasi kjo stivë skedarësh fiktivë mund të përmbajë çdo informacion personal delikat ku ka qasje procesi dhe mund të përfshijë gjërat që ke shkruar ti."</string>
+ <string name="dump_heap_ready_notification" msgid="2302452262927390268">"Grumbulli i skedarëve fiktivë të <xliff:g id="PROC">%1$s</xliff:g> është gati"</string>
+ <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Grumbulli i skedarëve fiktivë është mbledhur. Trokit për t\'i ndarë."</string>
+ <string name="dump_heap_title" msgid="4367128917229233901">"Të ndahet grumbulli i skedarëve fiktivë?"</string>
+ <string name="dump_heap_text" msgid="1692649033835719336">"Procesi <xliff:g id="PROC">%1$s</xliff:g> ka kaluar kufirin e tij të memories prej <xliff:g id="SIZE">%2$s</xliff:g>. Ke një grumbull skedarësh fiktivë që mund ta ndash me zhvilluesin e tij. Ki kujdes pasi ky grumbull skedarësh fiktivë mund të përmbajë çdo informacion personal tëndin ku ka qasje aplikacioni."</string>
+ <string name="dump_heap_system_text" msgid="6805155514925350849">"Procesi <xliff:g id="PROC">%1$s</xliff:g> e ka kaluar kufirin e tij të memories prej <xliff:g id="SIZE">%2$s</xliff:g>. Ke një grumbull skedarësh fiktivë që mund ta ndash. Ki kujdes pasi ky grumbull skedarësh fiktivë mund të përmbajë çdo informacion personal delikat ku ka qasje procesi dhe mund të përfshijë gjërat që ke shkruar ti."</string>
+ <string name="dump_heap_ready_text" msgid="5849618132123045516">"Ke një grumbull skedarësh fiktivë të procesit <xliff:g id="PROC">%1$s</xliff:g> që mund ta ndash. Ki kujdes pasi ky grumbull skedarësh fiktivë mund të përmbajë çdo informacion personal delikat ku ka qasje procesi dhe mund të përfshijë gjërat që ke shkruar ti."</string>
<string name="sendText" msgid="493003724401350724">"Zgjidh një veprim për tekstin"</string>
<string name="volume_ringtone" msgid="134784084629229029">"Volumi i ziles"</string>
<string name="volume_music" msgid="7727274216734955095">"Volumi i medias"</string>
@@ -1713,7 +1707,7 @@
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"U krye"</string>
<string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Çaktivizo shkurtoren"</string>
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Përdor shkurtoren"</string>
- <string name="color_inversion_feature_name" msgid="326050048927789012">"Kthimi i ngjyrës"</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>
<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>
@@ -1858,10 +1852,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Përditësuar nga administratori"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Fshirë nga administratori"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Në rregull"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"\"Kursyesi i baterisë\" aktivizon \"Temën e errët\" dhe kufizon ose çaktivizon aktivitetin në sfond, disa efekte vizuale dhe veçoritë si \"Ok Google\"\n\n"<annotation id="url">"Mëso më shumë"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"\"Kursyesi i baterisë\" aktivizon \"Temën e errët\" dhe kufizon ose çaktivizon aktivitetin në sfond, disa efekte vizuale dhe veçoritë si \"Ok Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Për të ndihmuar në reduktimin e përdorimit të të dhënave, \"Kursyesi i të dhënave\" pengon që disa aplikacione të dërgojnë apo të marrin të dhëna në sfond. Një aplikacion që po përdor aktualisht mund të ketë qasje te të dhënat, por këtë mund ta bëjë më rrallë. Kjo mund të nënkuptojë, për shembull, se imazhet nuk shfaqen kur troket mbi to."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Të aktivizohet \"Kursyesi i të dhënave\"?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivizo"</string>
@@ -1937,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Mbyll"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Përgjigju"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Refuzo"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Mbyll"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Telefonatë hyrëse"</string>
@@ -2080,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Ky njoftim është ulur në nivel si në heshtje. Trokit për të dhënë komente."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Ky njoftim është renditur më lart. Trokit për të dhënë komente."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Ky njoftim është renditur më poshtë. Trokit për të dhënë komente."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Provo njoftimet e përmirësuara"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Për të vazhduar të marrësh sugjerime për veprimet, përgjigjet etj., aktivizo njoftimet e përmirësuara. \"Njoftimet me përshtatje të Android\" nuk mbështeten më."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Aktivizo"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Jo tani"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Mëso më shumë"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Njoftimet e përmirësuara mund të lexojnë të gjithë përmbajtjen e njoftimeve, duke përfshirë edhe informacionet personale si emrat e kontakteve dhe mesazhet. Kjo veçori mund të heqë po ashtu njoftimet ose të veprojë mbi butonat te njoftimet, si p.sh. t\'u përgjigjet telefonatave.\n\nKjo veçori mund të aktivizojë ose të çaktivizojë po ashtu modalitetin \"Me përparësi\" dhe të ndryshojë cilësimet përkatëse."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Njoftimi i informacionit të \"Modalitetit rutinë\""</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Bateria mund të mbarojë përpara ngarkimit të zakonshëm"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"\"Kursyesi i baterisë\" u aktivizua për të rritur kohëzgjatjen e baterisë"</string>
@@ -2279,14 +2267,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Tani mund të zmadhosh pjesë të ekranit tënd"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Aktivizo te \"Cilësimet\""</string>
<string name="dismiss_action" msgid="1728820550388704784">"Hiq"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Zhblloko mikrofonin e pajisjes"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Zhblloko kamerën e pajisjes"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Për <b><xliff:g id="APP">%s</xliff:g></b> dhe të gjitha aplikacionet dhe shërbimet"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Zhblloko"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privatësia e sensorit"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ikona e aplikacionit"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imazhi i vendosjes së aplikacionit të markës"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index d404f8c..96fa82d 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1875,10 +1875,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Уштеда батерије укључује Тамну тему и ограничава или искључује активности у позадини, неке визуелне ефекте и функције, на пример, „Хеј Google“.\n\n"<annotation id="url">"Сазнајте више"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Уштеда батерије укључује Тамну тему и ограничава или искључује активности у позадини, неке визуелне ефекте и функције, на пример, „Хеј Google“."</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>
@@ -1962,6 +1960,7 @@
<string name="close_button_text" msgid="10603510034455258">"Затвори"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Одговори"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Видео"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Одбиј"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Прекини везу"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Долазни позив"</string>
@@ -2107,18 +2106,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Ово обавештење је деградирано у Нечујно. Додирните да бисте навели повратне информације."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Ово обавештење је рангирано више. Додирните да бисте навели повратне информације."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Ово обавештење је рангирано ниже. Додирните да бисте навели повратне информације."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Пробајте побољшана обавештења"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Укључите побољшана обавештења да бисте и даље добијали препоручене радње, одговоре и друго. Прилагодљива обавештења за Android више нису подржана."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Укључи"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Не сада"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Сазнајте више"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Побољшана обавештења могу да читају садржај свих обавештења, укључујући личне податке, попут имена контаката и порука. Ова функција може и да одбацује обавештења или активира дугмад у обавештењима, попут јављања на телефонске позиве.\n\nОва функција може и да укључи или искључи Приоритетни режим и да мења повезана подешавања."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Обавештење о информацијама Рутинског режима"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батерија ће се можда испразнити пре уобичајеног пуњења"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Уштеда батерије је активирана да би се продужило трајање батерије"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 4dc2ecf..b9efba0 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"I batterisparläget aktiveras mörkt tema och bakgrundsaktivitet som vissa visuella effekter och funktioner som ”Hey Google” begränsas eller inaktiveras\n\n"<annotation id="url">"Läs mer"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"I batterisparläget aktiveras mörkt tema och bakgrundsaktivitet som vissa visuella effekter och funktioner som ”Hey Google” 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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Stäng"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Svara"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Avvisa"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Lägg på"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Inkommande samtal"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Den här aviseringen har ändrats till Tyst. Tryck för att lämna feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Den här aviseringen har fått högre rankning. Tryck för att lämna feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Den här aviseringen har fått lägre rankning. Tryck för att lämna feedback."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Testa förbättrade aviseringar"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Aktivera förbättrade aviseringar om du vill fortsätta att få rekommenderade åtgärder, svar och annat. Anpassade aviseringar för Android stöds inte längre."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Aktivera"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Inte nu"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Läs mer"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Förbättrade aviseringar kan läsa allt innehåll i aviseringar, inklusive personliga uppgifter som namn på kontakter och meddelanden. Funktionen kan även stänga aviseringar eller använda åtgärdsknappar i aviseringar, till exempel för att svara på telefonsamtal.\n\nFunktionen kan även aktivera och inaktivera prioritetsläget och ändra tillhörande inställningar."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Avisering om rutinläge"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Batteriet kan ta slut innan du brukar ladda det"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Batterisparläget har aktiverats för att utöka batteritiden"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 0156787..ea069e1 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Kiokoa Betri huwasha Mandhari meusi na kudhibiti au kuzima shughuli za chinichini, baadhi ya madoido yanayoonekana na vipengele kama vile \"Ok Google\"\n\n"<annotation id="url">"Pata maelezo zaidi"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Kiokoa Betri huwasha Mandhari meusi na kudhibiti au kuzima shughuli za chinichini, baadhi ya madoido yanayoonekana na vipengele kama vile \"Ok Google\"."</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>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Funga"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Jibu"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Kataa"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Kata simu"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Simu uliyopigiwa"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Arifa hii ilishushwa hadhi kuwa Kimya. Gusa ili utoe maoni."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Arifa hii imeorodheshwa katika nafasi ya juu. Gusa ili utoe maoni."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Arifa hii imeorodheshwa katika nafasi ya chini. Gusa ili utoe maoni."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Jaribu arifa zilizoboreshwa"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Ili uendelee kupata vitendo, majibu na mambo mengine yanayopendekezwa, washa arifa zilizoboreshwa. Arifa Zinazojirekebisha za Android hazitumiki tena."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Washa"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Si sasa"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Pata maelezo zaidi"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Kipengele cha Arifa zilizoboreshwa kinaweza kusoma maudhui yote ya arifa, zikiwemo taarifa binafsi kama vile majina ya anwani na ujumbe. Kipengele hiki kinaweza pia kuondoa arifa au kuchukua hatua kwenye vitufe katika arifa, kama vile kujibu simu.\n\nKipengele hiki pia kinaweza kuwasha au kuzima hali ya Kipaumbele na kubadilisha mipangilio inayohusiana."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Arifa ya maelezo ya Hali ya Kawaida"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Huenda betri itakwisha chaji mapema"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Imewasha Kiokoa Betri ili kurefusha muda wa matumizi ya betri"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index e625c8bb..0b989c5 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"பேட்டரி சேமிப்பான் டார்க் தீமினை ஆன் செய்து பின்னணிச் செயல்பாடு, சில விஷுவல் எஃபெக்ட்கள், “Ok Google” போன்ற அம்சங்களை ஆஃப் செய்யும் அல்லது கட்டுப்படுத்தும்\n\n"<annotation id="url">"மேலும் அறிக"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"பேட்டரி சேமிப்பான் டார்க் தீமினை ஆன் செய்து பின்னணிச் செயல்பாடு, சில விஷுவல் எஃபெக்ட்கள், “Ok Google” போன்ற அம்சங்களை ஆஃப் செய்யும் அல்லது கட்டுப்படுத்தும்."</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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"மூடு"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"பதிலளி"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"நிராகரி"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"துண்டி"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"உள்வரும் அழைப்பு"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"இந்த அறிவிப்பின் முக்கியத்துவம் நிசப்த நிலைக்குக் குறைத்து அமைக்கப்பட்டது. கருத்து தெரிவிக்க தட்டவும்."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"இந்த அறிவிப்பின் முக்கியத்துவம் உயர்த்தப்பட்டது. கருத்து தெரிவிக்க தட்டவும்."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"இந்த அறிவிப்பின் முக்கியத்துவம் குறைக்கப்பட்டது. கருத்து தெரிவிக்க தட்டவும்."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"மேம்பட்ட அறிவிப்புகளை முயல்க"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"பரிந்துரைக்கப்பட்ட செயல்பாடுகள், பதில்கள் மற்றும் பலவற்றையும் தொடர்ந்து பெற மேம்பட்ட அறிவிப்புகளை ஆன் செய்யவும். Android சூழலுக்கேற்ற அறிவிப்புகள் இனி ஆதரிக்கப்படாது."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"ஆன் செய்"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"இப்போது வேண்டாம்"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"மேலும் அறிக"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"மேம்பட்ட அறிவிப்புகளால் அனைத்து அறிவிப்புகளின் உள்ளடக்கத்தையும் (தொடர்புகளின் பெயர்கள், மெசேஜ்கள் போன்ற தனிப்பட்ட தகவல்கள் உட்பட) படிக்க முடியும். இந்த அம்சத்தால் அறிவிப்புகளை நிராகரிக்கவோ அறிவிப்புகளிலுள்ள பட்டன்களை இயக்கவோ (அழைப்புகளுக்குப் பதிலளிப்பது போன்றவை) முடியும்.\n\nஇந்த அம்சத்தால் முன்னுரிமைப் பயன்முறையை இயக்கவோ முடக்கவோ முடியும் மற்றும் தொடர்புடைய அமைப்புகளை மாற்றவும் முடியும்."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"வழக்கமான பேட்டரி சேமிப்பானுக்கான விவர அறிவிப்பு"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"வழக்கமாகச் சார்ஜ் செய்வதற்கு முன்பே பேட்டரி தீர்ந்துபோகக்கூடும்"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"பேட்டரி நிலையை நீட்டிக்க பேட்டரி சேமிப்பான் இயக்கப்பட்டுள்ளது"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index faed275..882af55 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -251,7 +251,7 @@
<string name="global_action_bug_report" msgid="5127867163044170003">"బగ్ నివేదిక"</string>
<string name="global_action_logout" msgid="6093581310002476511">"సెషన్ను ముగించు"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"స్క్రీన్షాట్"</string>
- <string name="bugreport_title" msgid="8549990811777373050">"బగ్ నివేదిక"</string>
+ <string name="bugreport_title" msgid="8549990811777373050">"బగ్ రిపోర్ట్"</string>
<string name="bugreport_message" msgid="5212529146119624326">"ఇది ఇ-మెయిల్ సందేశం రూపంలో పంపడానికి మీ ప్రస్తుత పరికర స్థితి గురించి సమాచారాన్ని సేకరిస్తుంది. బగ్ నివేదికను ప్రారంభించడం మొదలుకొని పంపడానికి సిద్ధం చేసే వరకు ఇందుకు కొంత సమయం పడుతుంది; దయచేసి ఓపిక పట్టండి."</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"ప్రభావశీల నివేదిక"</string>
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"చాలా సందర్భాల్లో దీన్ని ఉపయోగించండి. ఇది నివేదిక ప్రోగ్రెస్ను ట్రాక్ చేయడానికి, సమస్య గురించి మరిన్ని వివరాలను నమోదు చేయడానికి మరియు స్క్రీన్షాట్లు తీయడానికి మిమ్మల్ని అనుమతిస్తుంది. ఇది నివేదించడానికి ఎక్కువ సమయం పట్టే తక్కువ వినియోగ విభాగాలను విడిచిపెట్టవచ్చు."</string>
@@ -315,14 +315,12 @@
<string name="permgroupdesc_storage" msgid="6351503740613026600">"మీ పరికరంలోని ఫోటోలు, మీడియా మరియు ఫైల్లను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouplab_microphone" msgid="2480597427667420076">"మైక్రోఫోన్"</string>
<string name="permgroupdesc_microphone" msgid="1047786732792487722">"ఆడియోను రికార్డ్ చేయడానికి"</string>
- <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"భౌతిక కార్యకలాపం"</string>
+ <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"ఫిజికల్ యాక్టివిటీ"</string>
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"భౌతిక కార్యకలాపాన్ని యాక్సెస్ చేయండి"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"కెమెరా"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"చిత్రాలను తీయడానికి మరియు వీడియోను రికార్డ్ చేయడానికి"</string>
- <!-- no translation found for permgrouplab_nearby_devices (5529147543651181991) -->
- <skip />
- <!-- no translation found for permgroupdesc_nearby_devices (3213561597116913508) -->
- <skip />
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"సమీపంలోని పరికరాలు"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"సమీప పరికరాలను కనుగొనండి అలాగే కనెక్ట్ చేయండి"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"కాల్ లాగ్లు"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"ఫోన్ కాల్ లాగ్ని చదవండి మరియు రాయండి"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"ఫోన్"</string>
@@ -540,14 +538,10 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"సమీపంలోని బ్లూటూత్ పరికరాలను కనుగొనడానికి, పెయిర్ చేయడానికి యాప్ను అనుమతిస్తుంది"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"పెయిర్ చేసిన బ్లూటూత్ పరికరాలకు కనెక్ట్ అవ్వండి"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"పెయిర్ చేసిన బ్లూటూత్ పరికరాలకు కనెక్ట్ అవ్వడానికి యాప్ను అనుమతిస్తుంది"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
- <!-- no translation found for permlab_uwb_ranging (8141915781475770665) -->
- <skip />
- <!-- no translation found for permdesc_uwb_ranging (2519723069604307055) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"సమీపంలోని బ్లూటూత్ పరికరాలలో అడ్వర్టయిజ్ చేయండి"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"సమీపంలోని బ్లూటూత్ పరికరాలలో అడ్వర్టయిజ్ చేయడానికి యాప్కు అనుమతిని ఇస్తుంది"</string>
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"సమీపంలోని అల్ట్రా-వైడ్బ్యాండ్ పరికరాల మధ్య సాపేక్ష స్థానాన్ని నిర్ణయించండి"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"సమీపంలోని అల్ట్రా-వైడ్బ్యాండ్ పరికరాల మధ్య సాపేక్ష స్థానాన్ని నిర్ణయించడానికి యాప్ను అనుమతించండి"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ప్రాధాన్యత ఇవ్వబడిన NFC చెల్లింపు సేవల సమాచారం"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ప్రాధాన్యత ఇవ్వబడిన NFC చెల్లింపు సేవల సమాచారాన్ని, అంటే రిజిస్టర్ చేయబడిన సహాయక సాధనాలు, మార్గం, గమ్యస్థానం వంటి వాటిని పొందేందుకు యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"సమీప క్షేత్ర కమ్యూనికేషన్ను నియంత్రించడం"</string>
@@ -1485,7 +1479,7 @@
<string name="forward_intent_to_work" msgid="3620262405636021151">"మీరు మీ కార్యాలయ ప్రొఫైల్లో ఈ యాప్ను ఉపయోగిస్తున్నారు"</string>
<string name="input_method_binding_label" msgid="1166731601721983656">"ఇన్పుట్ పద్ధతి"</string>
<string name="sync_binding_label" msgid="469249309424662147">"సమకాలీకరణ"</string>
- <string name="accessibility_binding_label" msgid="1974602776545801715">"యాక్సెస్ సామర్థ్యం"</string>
+ <string name="accessibility_binding_label" msgid="1974602776545801715">"యాక్సెసిబిలిటీ"</string>
<string name="wallpaper_binding_label" msgid="1197440498000786738">"వాల్పేపర్"</string>
<string name="chooser_wallpaper" msgid="3082405680079923708">"వాల్పేపర్ను మార్చండి"</string>
<string name="notification_listener_binding_label" msgid="2702165274471499713">"నోటిఫికేషన్ పరిశీలన"</string>
@@ -1569,7 +1563,7 @@
<string name="description_target_unlock_tablet" msgid="7431571180065859551">"అన్లాక్ చేయడానికి స్వైప్ చేయండి."</string>
<string name="action_bar_home_description" msgid="1501655419158631974">"హోమ్కు నావిగేట్ చేయండి"</string>
<string name="action_bar_up_description" msgid="6611579697195026932">"పైకి నావిగేట్ చేయండి"</string>
- <string name="action_menu_overflow_description" msgid="4579536843510088170">"మరిన్ని ఎంపికలు"</string>
+ <string name="action_menu_overflow_description" msgid="4579536843510088170">"మరిన్ని ఆప్షన్లు"</string>
<string name="action_bar_home_description_format" msgid="5087107531331621803">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s, %2$s, %3$s"</string>
<string name="storage_internal" msgid="8490227947584914460">"షేర్ చేయబడిన అంతర్గత నిల్వ"</string>
@@ -1696,13 +1690,13 @@
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ఆన్ చేయకండి"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"ఆన్"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"ఆఫ్"</string>
- <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g>కి మీ పరికరంపై పూర్తి నియంత్రణను ఇవ్వాలనుకుంటున్నారా?"</string>
+ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g>కి మీ పరికరంపై పూర్తి కంట్రోల్ను ఇవ్వాలనుకుంటున్నారా?"</string>
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"మీరు <xliff:g id="SERVICE">%1$s</xliff:g>ని ఆన్ చేస్తే, డేటా ఎన్క్రిప్షన్ను మెరుగుపరచడానికి మీ పరికరం మీ స్క్రీన్ లాక్ను ఉపయోగించదు."</string>
- <string name="accessibility_service_warning_description" msgid="291674995220940133">"అవసరమైన యాక్సెస్ సామర్ధ్యం కోసం యాప్లకు పూర్తి నియంత్రణ ఇవ్వడం తగిన పనే అయినా, అన్ని యాప్లకు అలా ఇవ్వడం సరికాదు."</string>
- <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"స్క్రీన్ను చూసి, నియంత్రించండి"</string>
+ <string name="accessibility_service_warning_description" msgid="291674995220940133">"అవసరమైన యాక్సెసిబిలిటీ కోసం యాప్లకు పూర్తి కంట్రోల్ ఇవ్వడం తగిన పనే అయినా, అన్ని యాప్లకు అలా ఇవ్వడం సరికాదు."</string>
+ <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"స్క్రీన్ను చూసి, కంట్రోల్ చేయడం"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"స్క్రీన్పై ఉండే కంటెంట్ మొత్తాన్ని చదవగలుగుతుంది మరియు ఇతర యాప్లలో కూడా ఈ కంటెంట్ను ప్రదర్శిస్తుంది."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"చర్యలను చూసి, అమలు చేయండి"</string>
- <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"మీరు యాప్తో చేసే పరస్పర చర్యలను లేదా హార్డ్వేర్ సెన్సార్ను ట్రాక్ చేస్తూ మీ తరఫున యాప్లతో పరస్పరం సమన్వయం చేస్తుంది."</string>
+ <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"మీరు ఒక యాప్తో చేసే ఇంటరాక్షన్లను లేదా హార్డ్వేర్ సెన్సార్ను ట్రాక్ చేస్తూ మీ తరఫున యాప్లతో ఇంటరాక్ట్ చేయగలదు."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"అనుమతించు"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"నిరాకరించు"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ఫీచర్ని ఉపయోగించడం ప్రారంభించడానికి, దాన్ని ట్యాప్ చేయండి:"</string>
@@ -1715,7 +1709,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>
- <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"కాంతిని మరింత డిమ్ చేసే"</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>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ని ఉపయోగించడానికి వాల్యూమ్ కీలు రెండింటినీ 3 సెకన్లు నొక్కి ఉంచండి"</string>
@@ -1858,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"బ్యాటరీ సేవర్ ముదురు రంగు రూపంను ఆన్ చేస్తుంది అలాగే బ్యాక్గ్రౌండ్ యాక్టివిటీని, కొన్ని విజువల్ ఎఫెక్ట్లు, అలాగే “Ok Google” వంటి ఫీచర్లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది\n\n"<annotation id="url">"మరింత తెలుసుకోండి"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"బ్యాటరీ సేవర్ ముదురు రంగు రూపంను ఆన్ చేస్తుంది అలాగే బ్యాక్గ్రౌండ్ యాక్టివిటీని, కొన్ని విజువల్ ఎఫెక్ట్లు, అలాగే “Ok Google” వంటి ఫీచర్లను పరిమితం చేస్తుంది లేదా ఆఫ్ చేస్తుంది."</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>
@@ -1931,12 +1923,14 @@
<string name="usb_midi_peripheral_name" msgid="490523464968655741">"Android USB పెరిఫెరల్ పోర్ట్"</string>
<string name="usb_midi_peripheral_manufacturer_name" msgid="7557148557088787741">"Android"</string>
<string name="usb_midi_peripheral_product_name" msgid="2836276258480904434">"USB పెరిఫెరల్ పోర్ట్"</string>
- <string name="floating_toolbar_open_overflow_description" msgid="2260297653578167367">"మరిన్ని ఎంపికలు"</string>
+ <string name="floating_toolbar_open_overflow_description" msgid="2260297653578167367">"మరిన్ని ఆప్షన్లు"</string>
<string name="floating_toolbar_close_overflow_description" msgid="3949818077708138098">"అతివ్యాప్తిని మూసివేస్తుంది"</string>
<string name="maximize_button_text" msgid="4258922519914732645">"గరిష్టీకరించు"</string>
<string name="close_button_text" msgid="10603510034455258">"మూసివేయి"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"సమాధానం ఇవ్వు"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"తిరస్కరించండి"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"కాల్ ముగించు"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"ఇన్కమింగ్ కాల్"</string>
@@ -1985,7 +1979,7 @@
<string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="6577581216125805905">"డెమోను ప్రారంభిస్తోంది..."</string>
<string name="demo_restarting_message" msgid="1160053183701746766">"పరికరాన్ని రీసెట్ చేస్తోంది..."</string>
- <string name="suspended_widget_accessibility" msgid="6331451091851326101">"<xliff:g id="LABEL">%1$s</xliff:g> నిలిపివేయబడింది"</string>
+ <string name="suspended_widget_accessibility" msgid="6331451091851326101">"<xliff:g id="LABEL">%1$s</xliff:g> డిజేబుల్ చేయబడింది"</string>
<string name="conference_call" msgid="5731633152336490471">"కాన్ఫరెన్స్ కాల్"</string>
<string name="tooltip_popup_title" msgid="7863719020269945722">"సాధనం చిట్కా"</string>
<string name="app_category_game" msgid="4534216074910244790">"గేమ్లు"</string>
@@ -2080,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ఈ నోటిఫికేషన్ స్థాయి నిశ్శబ్దంగా ఉండేలా తగ్గించబడింది. ఫీడ్బ్యాక్ను అందించడానికి ట్యాప్ చేయండి."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"ఈ నోటిఫికేషన్కు ఎక్కువ ర్యాంక్ ఇవ్వబడింది. ఫీడ్బ్యాక్ను అందించడానికి ట్యాప్ చేయండి."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"ఈ నోటిఫికేషన్కు తక్కువ ర్యాంక్ ఇవ్వబడింది. ఫీడ్బ్యాక్ను అందించడానికి ట్యాప్ చేయండి."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"మెరుగైన నోటిఫికేషన్ల ట్రై"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"సూచించిన చర్యలు, రిప్లయిలు, అలాగే మరిన్ని పొందడం కొనసాగించడానికి మెరుగైన నోటిఫికేషన్లను ఆన్ చేయండి. Android అనుకూల నోటిఫికేషన్లు ఇకపై సపోర్ట్ చేయవు."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"ఆన్ చేయి"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"ఇప్పుడు కాదు"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"మరింత తెలుసుకోండి"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"కాంటాక్ట్ పేర్లు, మెసేజ్లు వంటి వ్యక్తిగత సమాచారంతో సహా మెరుగైన నోటిఫికేషన్లు అన్ని నోటిఫికేషన్ కంటెంట్ను చదవగలవు. ఈ ఫీచర్ నోటిఫికేషన్లను తీసివేయవచ్చు లేదా ఫోన్ కాల్లకు సమాధానం ఇవ్వడం వంటి నోటిఫికేషన్లలోని బటన్లపై చర్యలు తీసుకోవచ్చు.\n\nఈ ఫీచర్ ప్రాధాన్యత మోడ్ను కూడా ఆన్ లేదా ఆఫ్ చేయవచ్చు, ఇది సంబంధిత సెట్టింగ్లను మార్చగలదు."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"రొటీన్ మోడ్ సమాచార నోటిఫికేషన్"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"మామూలుగా ఛార్జ్ చేసేలోపు బ్యాటరీ ఖాళీ కావచ్చు"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"బ్యాటరీ జీవితకాలాన్ని పెంచడానికి బ్యాటరీ సేవర్ యాక్టివేట్ చేయబడింది"</string>
@@ -2279,17 +2267,13 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"మీరు ఇప్పుడు మీ స్క్రీన్ కొంత భాగాన్ని మాగ్నిఫై చేయవచ్చు"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"సెట్టింగ్లలో ఆన్ చేయండి"</string>
<string name="dismiss_action" msgid="1728820550388704784">"విస్మరించు"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"పరికరం మైక్రోఫోన్ను అన్బ్లాక్ చేయండి"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"పరికరం కెమెరాను అన్బ్లాక్ చేయండి"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> యాప్, ఇతర యాప్లు, సర్వీస్ల కోసం"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"అన్బ్లాక్ చేయండి"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"సెన్సార్ గోప్యత"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"యాప్ చిహ్నం"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"యాప్ బ్రాండింగ్ ఇమేజ్"</string>
<string name="view_and_control_notification_title" msgid="4300765399209912240">"యాక్సెస్ సెట్టింగ్లను చెక్ చేయండి"</string>
- <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> మీ స్క్రీన్ను చూడవచ్చు, నియంత్రించవచ్చు. రివ్యూ చేయడానికి ట్యాప్ చేయండి."</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> మీ స్క్రీన్ను చూడవచ్చు, కంట్రోల్ చేయవచ్చు. రివ్యూ చేయడానికి ట్యాప్ చేయండి."</string>
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 1c00654..e0001c2 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"โหมดประหยัดแบตเตอรี่จะเปิดธีมมืดและจำกัดหรือปิดกิจกรรมในเบื้องหลัง เอฟเฟกต์ภาพบางอย่าง และฟีเจอร์อย่างเช่น \"Ok Google\"\n\n"<annotation id="url">"ดูข้อมูลเพิ่มเติม"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"โหมดประหยัดแบตเตอรี่จะเปิดธีมมืดและจำกัดหรือปิดกิจกรรมในเบื้องหลัง เอฟเฟกต์ภาพบางอย่าง และฟีเจอร์อย่างเช่น \"Ok Google\""</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>
@@ -1931,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"ปิด"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"รับสาย"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"ปฏิเสธ"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"วางสาย"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"สายเรียกเข้า"</string>
@@ -2074,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"การแจ้งเตือนนี้มีการลดระดับเป็นแบบปิดเสียง แตะเพื่อแสดงความคิดเห็น"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"การแจ้งเตือนนี้มีการเพิ่มระดับ แตะเพื่อแสดงความคิดเห็น"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"การแจ้งเตือนนี้มีการลดระดับ แตะเพื่อแสดงความคิดเห็น"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"ลองใช้การแจ้งเตือนที่เพิ่มประสิทธิภาพ"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"หากต้องการรับการดําเนินการ การตอบ และอื่นๆ ที่แนะนําต่อไป ให้เปิดการแจ้งเตือนที่เพิ่มประสิทธิภาพ ไม่รองรับการแจ้งเตือนแบบปรับอัตโนมัติใน Android อีกต่อไป"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"เปิด"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"ไว้ทีหลัง"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"ดูข้อมูลเพิ่มเติม"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"การแจ้งเตือนที่เพิ่มประสิทธิภาพจะอ่านเนื้อหาการแจ้งเตือนทั้งหมดได้ รวมถึงข้อมูลส่วนบุคคล เช่น ชื่อผู้ติดต่อและข้อความ ฟีเจอร์นี้ยังปิดการแจ้งเตือนหรือดำเนินการกับปุ่มต่างๆ ในการแจ้งเตือนได้ด้วย เช่น การรับสายเรียกเข้า\n\nอีกทั้งสามารถเปิดหรือปิดโหมดลำดับความสำคัญสูงและเปลี่ยนแปลงการตั้งค่าที่เกี่ยวข้อง"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"การแจ้งเตือนข้อมูลโหมดกิจวัตร"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"แบตเตอรี่อาจหมดก่อนการชาร์จปกติ"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"เปิดใช้งานโหมดประหยัดแบตเตอรี่แล้วเพื่อยืดอายุการใช้งานแบตเตอรี่"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index c05fe90..adaa774 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Ino-on ng Pantipid ng Baterya ang Madilim na tema at nililimitahan o ino-off nito ang aktibidad sa background, ilang visual effect, at mga feature gaya ng “Hey Google”\n\n"<annotation id="url">"Matuto pa"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Ino-on ng Pantipid ng Baterya ang Madilim na tema at nililimitahan o ino-off nito ang aktibidad sa background, ilang visual effect, at mga feature gaya ng “Hey Google.”"</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>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Isara"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Sagutin"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Tanggihan"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Ibaba"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Papasok na tawag"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Na-demote sa Naka-silent ang notification na ito. I-tap para magbigay ng feedback."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Itinaas ang ranggo ng notification na ito. I-tap para magbigay ng feedback."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Ibinaba ang ranggo ng notification na ito. I-tap para magbigay ng feedback."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Subukan ang enhanced notification"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Para patuloy na makakuha ng mga iminumungkahing pagkilos, sagot, at higit pa, i-on ang mga pinahusay na notification. Hindi na sinusuportahan ang Mga Adaptive na Notification ng Android."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"I-on"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Hindi ngayon"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Matuto pa"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Puwedeng basahin ng mga pinahusay na notification ang lahat ng notification, kabilang ang personal na impormasyon gaya ng mga pangalan ng contact at mga mensahe. Magagawa ring ng feature na ito na i-dismiss ang mga notification o gumawa ng mga pagkilos sa mga button sa mga notification, gaya ng pagsagot sa mga tawag sa telepono.\n\nPuwede ring i-on o i-off ng feature na ito ang Priority mode at baguhin ang mga kaugnay na setting."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notification ng impormasyon ng Routine Mode"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Maaaring maubos ang baterya bago ang karaniwang pag-charge"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Na-activate ang Pantipid ng Baterya para patagalin ang buhay ng baterya"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 87ca8e0..bd85322 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -324,7 +324,7 @@
<string name="permgrouplab_calllog" msgid="7926834372073550288">"Arama kayıtları"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"telefon arama kaydını okuma ve yazma"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"Telefon"</string>
- <string name="permgroupdesc_phone" msgid="270048070781478204">"telefon çağrıları yapma ve yönetme"</string>
+ <string name="permgroupdesc_phone" msgid="270048070781478204">"telefon aramaları yapma ve yönetme"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Vücut sensörleri"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"hayati belirtilerinizle ilgili sensör verilerine erişme"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Pencere içeriğini alma"</string>
@@ -353,9 +353,9 @@
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Uygulamaya, kullanıcı müdahalesi olmadan kısayolları Ana Ekrana ekleme izni verir."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"kısayolların yüklemesini kaldırma"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Uygulamaya, kullanıcının müdahalesi olmadan kısayolları Ana Ekrandan kaldırma izni verir."</string>
- <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"giden çağrıları yeniden yönlendirme"</string>
+ <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"giden aramaları yeniden yönlendirme"</string>
<string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Uygulamaya, giden bir çağrının numarası çevrilirken çağrıyı farklı bir numaraya yönlendirme ya da tamamen kapatma seçeneğiyle birlikte numarayı görme izni verir."</string>
- <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"telefon çağrılarını yanıtla"</string>
+ <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"telefon aramalarını yanıtla"</string>
<string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Uygulamanın gelen bir telefon çağrısına yanıt vermesine olanak tanır."</string>
<string name="permlab_receiveSms" msgid="505961632050451881">"kısa mesajları al (SMS)"</string>
<string name="permdesc_receiveSms" msgid="1797345626687832285">"Uygulamaya SMS iletilerini alma ve işleme izni verir. Bu izin, uygulamanın cihazınıza gönderilen iletileri takip edip size göstermeden silebileceği anlamına gelir."</string>
@@ -423,8 +423,8 @@
<string name="permdesc_readCallLog" msgid="8964770895425873433">"Bu uygulama, çağrı geçmişinizi okuyabilir."</string>
<string name="permlab_writeCallLog" msgid="670292975137658895">"çağrı günlüğüne yaz"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Uygulamaya tabletinizin çağrı günlüğünde (gelen ve giden çağrılarla ilgili veriler dahil olmak üzere) değişiklik yapma izni verir. Kötü amaçlı uygulamalar bu izni kullanarak çağrı günlüğünüzü silebilir veya değiştirebilir."</string>
- <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Uygulamaya, Android TV cihazınızın çağrı günlüğünde (gelen ve giden çağrılarla ilgili veriler dahil olmak üzere) değişiklik yapma izni verir. Kötü amaçlı uygulamalar bu izni kullanarak çağrı günlüğünüzü silebilir veya değiştirebilir."</string>
- <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Uygulamaya telefonunuzun çağrı günlüğünde (gelen ve giden çağrılarla ilgili veriler dahil olmak üzere) değişiklik yapma izni verir. Kötü amaçlı uygulamalar bu izni kullanarak çağrı günlüğünüzü silebilir veya değiştirebilir."</string>
+ <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Uygulamaya, Android TV cihazınızın arama günlüğünde (gelen ve giden aramalarla ilgili veriler dahil olmak üzere) değişiklik yapma izni verir. Kötü amaçlı uygulamalar bu izni kullanarak arama günlüğünüzü silebilir veya değiştirebilir."</string>
+ <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Uygulamaya telefonunuzun arama günlüğünde (gelen ve giden aramalarla ilgili veriler dahil olmak üzere) değişiklik yapma izni verir. Kötü amaçlı uygulamalar bu izni kullanarak arama günlüğünüzü silebilir veya değiştirebilir."</string>
<string name="permlab_bodySensors" msgid="3411035315357380862">"vücut sensörlerine erişme (nabız takip cihazları gibi)"</string>
<string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Uygulamanın, nabzınız gibi fiziksel durumunuzu izleyen sensörlerin gönderdiği verilere erişmesine izin verir."</string>
<string name="permlab_readCalendar" msgid="6408654259475396200">"Takvim etkinlikleri ve ayrıntılarını okuma"</string>
@@ -465,12 +465,12 @@
<string name="permdesc_vibrate" msgid="8733343234582083721">"Uygulamaya, titreşimi denetleme izni verir."</string>
<string name="permdesc_vibrator_state" msgid="7050024956594170724">"Uygulamanın titreşim durumuna erişimesine izni verir."</string>
<string name="permlab_callPhone" msgid="1798582257194643320">"telefon numaralarına doğrudan çağrı yap"</string>
- <string name="permdesc_callPhone" msgid="5439809516131609109">"Uygulamaya sizin müdahaleniz olmadan telefon numaralarına çağrı yapma izni verir. Bu durum beklenmeyen ödemelere veya çağrılara neden olabilir. Ancak bu iznin, uygulamanın acil numaralara çağrı yapmasına olanak sağlamadığını unutmayın. Kötü amaçlı uygulamalar onayınız olmadan çağrılar yaparak sizi zarara sokabilir."</string>
+ <string name="permdesc_callPhone" msgid="5439809516131609109">"Uygulamaya sizin müdahaleniz olmadan telefon numaralarını arama izni verir. Bu durum beklenmeyen ödemelere veya aramalara neden olabilir. Ancak bu iznin, uygulamanın acil numaraları aramasına olanak sağlamadığını unutmayın. Kötü amaçlı uygulamalar onayınız olmadan aramalar yaparak sizi zarara sokabilir."</string>
<string name="permlab_accessImsCallService" msgid="442192920714863782">"IMS çağrı hizmetine erişme"</string>
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Uygulamanın, sizin müdahaleniz olmadan telefon etmek için IMS hizmetini kullanmasına izin verir."</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"telefonun durumunu ve kimliğini okuma"</string>
<string name="permdesc_readPhoneState" msgid="7229063553502788058">"Uygulamaya cihazdaki telefon özelliklerine erişme izni verir. Bu izin, uygulamanın telefon numarasını ve cihaz kimliğini, etkin bir çağrı olup olmadığını ve çağrıda bağlanılan karşı tarafın numarasını öğrenmesine olanak sağlar."</string>
- <string name="permlab_manageOwnCalls" msgid="9033349060307561370">"çağrıları sistem üzerinden yönlendir"</string>
+ <string name="permlab_manageOwnCalls" msgid="9033349060307561370">"aramaları sistem üzerinden yönlendir"</string>
<string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Uygulamanın, çağrı deneyimini iyileştirmek için çağrılarını sistem üzerinden yönlendirmesine olanak tanır."</string>
<string name="permlab_callCompanionApp" msgid="3654373653014126884">"aramaları sistemde görüp denetleme."</string>
<string name="permdesc_callCompanionApp" msgid="8474168926184156261">"Uygulamanın cihazda devam eden aramaları görmesini ve denetlemesini sağlar. Bu bilgiler arasında aramaların yapıldığı numaralar ve aramaların durumu gibi bilgiler yer alır."</string>
@@ -671,8 +671,8 @@
<string name="permdesc_sdcardRead" msgid="6872973242228240382">"Uygulamaya, paylaşılan depolama alanınızın içeriğini okuma izni verir."</string>
<string name="permlab_sdcardWrite" msgid="4863021819671416668">"paylaşılan depolama alanımın içeriğini değiştir veya sil"</string>
<string name="permdesc_sdcardWrite" msgid="8376047679331387102">"Uygulamanın paylaşılan depolama alanınıza içerik yazmasına izin verir."</string>
- <string name="permlab_use_sip" msgid="8250774565189337477">"SIP çağrıları yapma/alma"</string>
- <string name="permdesc_use_sip" msgid="3590270893253204451">"Uygulamanın SIP çağrıları yapmasına ve almasına izin verir."</string>
+ <string name="permlab_use_sip" msgid="8250774565189337477">"SIP aramaları yapma/alma"</string>
+ <string name="permdesc_use_sip" msgid="3590270893253204451">"Uygulamanın SIP aramaları yapmasına ve almasına izin verir."</string>
<string name="permlab_register_sim_subscription" msgid="1653054249287576161">"yeni telekomünikasyon SIM bağlantılarını kaydettir"</string>
<string name="permdesc_register_sim_subscription" msgid="4183858662792232464">"Uygulamanın yeni telekomünikasyon SIM bağlantıları kaydettirmesine izin verir."</string>
<string name="permlab_register_call_provider" msgid="6135073566140050702">"yeni telekomünikasyon bağlantıları kaydettir"</string>
@@ -903,7 +903,7 @@
<string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"Durdur"</string>
<string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Geri sar"</string>
<string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"İleri sar"</string>
- <string name="emergency_calls_only" msgid="3057351206678279851">"Yalnızca acil çağrılar için"</string>
+ <string name="emergency_calls_only" msgid="3057351206678279851">"Yalnızca acil aramalar için"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Ağ kilitli"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"SIM kart PUK kilidi devrede."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"Kullanıcı Rehberi\'ne bakın veya Müşteri Hizmetleri\'ne başvurun."</string>
@@ -1694,8 +1694,8 @@
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"<xliff:g id="SERVICE">%1$s</xliff:g> hizmetini açarsanız cihazınız veri şifrelemeyi geliştirmek için ekran kilidinizi kullanmaz."</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"Erişebilirlik ihtiyaçlarınıza yardımcı olan uygulamalara tam kontrol verilmesi uygundur ancak diğer pek çok uygulama için uygun değildir."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Ekranı görüntüleme ve kontrol etme"</string>
- <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Ekrandaki tüm içeriği okuyabilir ve içeriği diğer uygulamaların üzerinde gösterebilir"</string>
- <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"İşlemleri görüntüleyin ve gerçekleştirin"</string>
+ <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Ekrandaki tüm içeriği okuyabilir ve içeriği diğer uygulamaların üzerinde gösterebilir."</string>
+ <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"İşlemleri görüntüleme ve gerçekleştirme"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Bir uygulama veya donanım sensörüyle etkileşimlerinizi takip edebilir ve sizin adınıza uygulamalarla etkileşimde bulunabilir."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"İzin ver"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Reddet"</string>
@@ -1852,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Pil Tasarrufu, Koyu temayı açıp arka plan etkinliğini, bazı görsel efektleri ve \"Ok Google\" gibi özellikleri kapatır veya kısıtlar.\n\n"<annotation id="url">"Daha fazla bilgi"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Pil Tasarrufu, Koyu temayı açıp arka plan etkinliğini, bazı görsel efektleri ve \"Ok Google\" gibi özellikleri kapatır veya kısıtlar."</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. Şu anda kullandığınız 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>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Kapat"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Yanıtla"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Reddet"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Kapat"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Gelen çağrı"</string>
@@ -2056,8 +2055,8 @@
<string name="harmful_app_warning_title" msgid="8794823880881113856">"Zararlı uygulama tespit edildi"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> uygulaması, <xliff:g id="APP_2">%2$s</xliff:g> dilimlerini göstermek istiyor"</string>
<string name="screenshot_edit" msgid="7408934887203689207">"Düzenle"</string>
- <string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"Çağrılar ve bildirimler titreşim yapacak"</string>
- <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"Çağrılar ve bildirimlerin sesi kapalı olacak"</string>
+ <string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"Aramalar ve bildirimler titreşim yapacak"</string>
+ <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"Aramalar ve bildirimlerin sesi kapalı olacak"</string>
<string name="notification_channel_system_changes" msgid="2462010596920209678">"Sistem değişiklikleri"</string>
<string name="notification_channel_do_not_disturb" msgid="7832584281883687653">"Rahatsız Etmeyin"</string>
<string name="zen_upgrade_notification_visd_title" msgid="2001148984371968620">"Yeni: Rahatsız Etmeyin ayarı bildirimleri gizliyor"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Bu bildirimin önem derecesi, \"Sessiz\" seviyesine düşürüldü. Geri bildirimde bulunmak için dokunun."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Bu bildirimin önem derecesi yükseltildi. Geri bildirimde bulunmak için dokunun."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Bu bildirimin önem derecesi düşürüldü. Geri bildirimde bulunmak için dokunun."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Gelişmiş bildirimleri deneyin"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Önerilen işlemleri, yanıtları ve diğer içerikleri almaya devam etmek için gelişmiş bildirimleri açın. Android Uyarlamalı Bildirimler artık desteklenmiyor."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Aç"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Şimdi değil"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Daha fazla bilgi"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Gelişmiş bildirimler, kişiler ve mesajlar gibi kişisel bilgiler dahil olmak üzere tüm bildirim içeriklerini okuyabilir. Bu özellik ayrıca bildirimleri kapatabilir veya bildirimlerdeki düğmeler üzerinde telefon çağrılarını yanıtlamak gibi işlemler yapabilir.\n\nBu özellik ayrıca Öncelik modunu açıp kapatabilir ve ilgili ayarları değiştirebilir."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Rutin Modu bilgi bildirimi"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Pil normal şarjdan önce bitebilir"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Pilin ömrünü uzatmak için Pil Tasarrufu etkinleştirildi"</string>
@@ -2271,7 +2264,7 @@
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
<string name="window_magnification_prompt_title" msgid="2876703640772778215">"Yeni büyütme ayarları"</string>
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Artık ekranınızın bir bölümünü büyütebilirsiniz"</string>
- <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Ayarlar\'da aç"</string>
+ <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Ayarlar\'da etkinleştir"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Kapat"</string>
<string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Cihaz mikrofonunun engellemesini kaldır"</string>
<string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Cihaz kamerasının engellemesini kaldır"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 7a74489..9401d50 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -544,10 +544,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Дозволяє додатку знаходити поблизу пристрої Bluetooth і створювати з ними пару"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"підключатися до пристроїв із Bluetooth, з якими є пара"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Дозволяє додатку підключатися до пристроїв із Bluetooth, з якими створено пару"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"показувати рекламу на пристроях із Bluetooth поблизу"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Додаток зможе показувати рекламу на пристроях із Bluetooth поблизу"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"визначення відстані між пристроями поблизу з надширокосмуговим зв’язком"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"З цим дозволом додаток може визначати відстань між розташованими поблизу пристроями з надширокосмуговим зв’язком"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Використання інформації з платіжного NFC-сервісу"</string>
@@ -1741,7 +1739,7 @@
<string name="accessibility_service_warning_description" msgid="291674995220940133">"Повний доступ доречний для додатків, які надають спеціальні можливості, але його не варто відкривати для більшості інших додатків."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Перегляд і контроль екрана"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Цей сервіс може переглядати всі дані на екрані й показувати вміст над іншими додатками."</string>
- <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Переглянути й виконати дії"</string>
+ <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Перегляд і виконання дій"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Цей сервіс може відстежувати вашу взаємодію з додатком чи апаратним датчиком, а також взаємодіяти з додатками від вашого імені."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Дозволити"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Заборонити"</string>
@@ -1900,10 +1898,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"У режимі енергозбереження вмикається темна тема й обмежуються чи вимикаються фонова робота додатків, деякі візуальні ефекти та інші функції, як-от \"Ok Google\"\n\n"<annotation id="url">"Докладніше"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"У режимі енергозбереження вмикається темна тема й обмежуються чи вимикаються фонова робота додатків, деякі візуальні ефекти та інші функції, як-от \"Ok Google\"."</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>
@@ -1995,6 +1991,8 @@
<string name="close_button_text" msgid="10603510034455258">"Закрити"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Відповісти"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Відхилити"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Завершити"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Вхідний виклик"</string>
@@ -2142,18 +2140,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Важливість цього сповіщення знижено до рівня \"Без звуку\". Натисніть, щоб надіслати відгук."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Важливість цього сповіщення підвищено. Натисніть, щоб надіслати відгук."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Важливість цього сповіщення знижено. Натисніть, щоб надіслати відгук."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Спробуйте покращені сповіщення"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Щоб і надалі отримувати пропозиції дій, відповідей тощо, увімкніть покращені сповіщення. Адаптивні сповіщення Android більше не підтримуються."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Увімкнути"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Не зараз"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Докладніше"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Покращені сповіщення дають змогу читати весь вміст сповіщень, зокрема особисту інформацію, як-от імена контактів і повідомлення. Крім того, ви зможете відхиляти сповіщення або натискати кнопки в них, щоб виконувати певні дії, наприклад відповідати на телефонні дзвінки.\n\nЦя функція також дає змогу вмикати й вимикати режим пріоритетності та змінювати відповідні налаштування."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Сповіщення про послідовнсть дій"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Акумулятор може розрядитися раніше ніж зазвичай"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Режим енергозбереження активовано для збільшення часу роботи акумулятора"</string>
@@ -2343,14 +2335,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Тепер можна збільшувати частину екрана"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Увімкнути в налаштуваннях"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Закрити"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Розблокуйте мікрофон пристрою"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Розблокуйте камеру пристрою"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Для <b><xliff:g id="APP">%s</xliff:g></b> та всіх додатків і сервісів"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Розблокувати"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Конфіденційність датчиків"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Значок додатка"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Зображення фірмової символіки додатка"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 854741b..9291cd2 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -319,10 +319,8 @@
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"اپنی جسمانی سرگرمی تک رسائی حاصل کریں"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"کیمرا"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"تصاویر لیں اور ویڈیو ریکارڈ کریں"</string>
- <!-- no translation found for permgrouplab_nearby_devices (5529147543651181991) -->
- <skip />
- <!-- no translation found for permgroupdesc_nearby_devices (3213561597116913508) -->
- <skip />
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"قریبی آلات"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"قریبی آلات دریافت کریں اور ان سے منسلک ہوں"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"کال لاگز"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"فون کال لاگ پڑھ کر لکھیں"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"فون"</string>
@@ -540,14 +538,10 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"ایپ کو قریبی بلوٹوتھ آلات دریافت کرنے اور ان کا جوڑا بنانے کی اجازت دیں"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"جوڑا بنائے ہوئے بلوٹوتھ آلات سے منسلک کریں"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"ایپ کو جوڑا بنائے ہوئے بلوٹوتھ آلات سے منسلک کرنے کی اجازت دیں"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
- <!-- no translation found for permlab_uwb_ranging (8141915781475770665) -->
- <skip />
- <!-- no translation found for permdesc_uwb_ranging (2519723069604307055) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"قریبی بلوٹوتھ آلات پر تشہیر کریں"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"اس سے ایپ کو قریبی بلوٹوتھ آلات پر تشہیر کرنے کی اجازت ملتی ہے"</string>
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"قریبی الٹرا وائڈ بینڈ آلات کے مابین متعلقہ پوزیشن کا تعین کریں"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ایپ کو قریبی الٹرا وائڈ بینڈ آلات کے مابین متعلقہ پوزیشن کا تعین کرنے کی اجازت دیں"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ترجیح شدہ NFC ادائیگی کی سروس کی معلومات"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ایپ کو رجسٹرشدہ ایڈز اور روٹ ڈسٹنیشن جیسی ترجیح شدہ nfc ادائیگی سروس کی معلومات حاصل کرنے کی اجازت دیتا ہے۔"</string>
<string name="permlab_nfc" msgid="1904455246837674977">"Near Field کمیونیکیشن کنٹرول کریں"</string>
@@ -1858,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"بیٹری سیور گہری تھیم کو آن کرتی ہے اور پس منظر کی سرگرمی، کچھ بصری اثرات اور \"Hey Google\" جیسی خصوصیات کو محدود یا آف کرتی ہے\n\n"<annotation id="url">"مزید جانیں"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"بیٹری سیور گہری تھیم کو آن کرتی ہے اور پس منظر کی سرگرمی، کچھ بصری اثرات اور \"Hey Google\" جیسی خصوصیات کو محدود یا آف کرتی ہے۔"</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>
@@ -1937,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"بند کریں"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"جواب"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"مسترد کریں"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"منقطع کر دیں"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"اِن کمنگ کال"</string>
@@ -2080,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"اس اطلاع کو خاموش پر ڈیموٹ کیا گيا۔ تاثرات فراہم کرنے کے ليے تھپتھپائیں۔"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"اس اطلاع کو اعلی درجہ دیا گیا۔ تاثرات فراہم کرنے کے ليے تھپتھپائیں۔"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"اس اطلاع کو کم درجہ دیا گیا۔ تاثرات فراہم کرنے کے ليے تھپتھپائیں۔"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"بہتر کردہ اطلاعات آزمائیں"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"تجویز کردہ کارروائیاں، جوابات اور مزید بہت کچھ حاصل کرنا جاری رکھنے کے لیے بہتر کردہ اطلاعات آن کریں۔ Android اڈاپٹیو اطلاعات اب تعاون یافتہ نہیں ہیں۔"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"آن کریں"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"ابھی نہیں"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"مزید جانیں"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"بہتر کردہ اطلاعات رابطوں کے نام اور پیغامات جیسی ذاتی معلومات سمیت تمام اطلاعی مواد پڑھ سکتی ہیں۔ یہ خصوصیت اطلاعات کو برخاست بھی کر سکتی ہے یا کالز کا جواب دینے جیسے اطلاعات میں نظر آنے والے بٹنوں سے کارروائیاں کر سکتی ہے۔\n\nیہ خصوصیت ترجیحی موڈ کو آن یا آف بھی کر سکتی ہے اور متعلقہ ترتیبات کو تبدیل کر سکتی ہے۔"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"روٹین موڈ معلومات کی اطلاع"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"معمول چارج سے پہلے بیٹری ختم ہو سکتی ہے"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"بیٹری لائف کو بڑھانے کے لیے بیٹری سیور کو فعال کر دیا گیا ہے"</string>
@@ -2279,17 +2267,13 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"اب آپ اپنی اسکرین کے حصے کو بڑا کر سکتے ہیں"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"ترتیبات میں آن کریں"</string>
<string name="dismiss_action" msgid="1728820550388704784">"برخاست کریں"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"آلے کا مائیکروفون غیر مسدود کریں"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"آلے کا کیمرا غیر مسدود کریں"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"<b><xliff:g id="APP">%s</xliff:g></b> اور سبھی ایپس اور سروسز کے لیے"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"غیر مسدود کریں"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"سینسر کی رازداری"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"ایپلیکیشن کا آئیکن"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"ایپلیکیشن کی برانڈنگ تصویر"</string>
<string name="view_and_control_notification_title" msgid="4300765399209912240">"رسائی کی ترتیبات چیک کریں"</string>
- <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> آپ کی اسکرین کو دیکھ اور کنٹرول کر سکتی ہیں۔ جائزے کے لیے تھپتھپائیں۔"</string>
+ <string name="view_and_control_notification_content" msgid="8003766498562604034">"<xliff:g id="SERVICE_NAME">%s</xliff:g> آپ کی اسکرین کو دیکھ اور کنٹرول کر سکتی ہیں۔ جائزے کے لیے تھپتھپائیں۔"</string>
</resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index a40e083..3b135ec 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -319,10 +319,8 @@
<string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"jismoniy harakatlar axborotiga ruxsat"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"suratga olish va video yozib olish"</string>
- <!-- no translation found for permgrouplab_nearby_devices (5529147543651181991) -->
- <skip />
- <!-- no translation found for permgroupdesc_nearby_devices (3213561597116913508) -->
- <skip />
+ <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Yaqin-atrofdagi qurilmalar"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"yaqin-atrofdagi qurilmalarni aniqlash va ulanish"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"Chaqiruvlar jurnali"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"telefon chaqiruvlari jurnalini o‘qish va unga yozish"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"Telefon"</string>
@@ -542,10 +540,8 @@
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Ilovaga juftlangan Bluetooth qurilmalariga ulanish uchun ruxsat beradi"</string>
<string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"atrofdagi Bluetooth qurilmalariga reklama berish"</string>
<string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Ilovaga yaqin-atrofdagi Bluetooth qurilmalariga reklama yuborish imkonini beradi"</string>
- <!-- no translation found for permlab_uwb_ranging (8141915781475770665) -->
- <skip />
- <!-- no translation found for permdesc_uwb_ranging (2519723069604307055) -->
- <skip />
+ <string name="permlab_uwb_ranging" msgid="8141915781475770665">"yaqin atrofdagi ultra keng polosali qurilmalarining nisbiy joylashishini aniqlash"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Ilovaga yaqin atrofdagi ultra keng polosali qurilmalarining nisbiy joylashishini aniqlashga ruxsat beradi"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Asosiy NFC toʻlov xizmati haqidagi axborot"</string>
<string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Bu ilovaga asosiy NFC toʻlov xizmati haqidagi axborotni olish imkonini beradi (masalan, qayd qilingan AID identifikatorlari va marshrutning yakuniy manzili)."</string>
<string name="permlab_nfc" msgid="1904455246837674977">"NFC modulini boshqarish"</string>
@@ -1856,10 +1852,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Administrator tomonidan yangilangan"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Administrator tomonidan o‘chirilgan"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Quvvat tejash funksiyasi Tungi mavzuni va cheklovlarni yoqadi va fondagi harakatlar, vizual effektlar va “Ok Google” kabi boshqa funksiyalarni faolsizlantiradi yoki cheklaydi.\n\n"<annotation id="url">"Batafsil"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Quvvat tejash funksiyasi Tungi mavzuni va cheklovlarni yoqadi va fondagi harakatlar, vizual effektlar va “Ok Google” kabi boshqa funksiyalarni faolsizlantiradi yoki cheklaydi."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Trafik tejash rejimida ayrim ilovalar uchun orqa fonda internetdan foydalanish imkoniyati cheklanadi. Siz ishlatayotgan ilova zaruratga qarab internet-trafik sarflashi mumkin, biroq cheklangan miqdorda. Masalan, rasmlar ustiga bosmaguningizcha ular yuklanmaydi."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Trafik tejash yoqilsinmi?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Yoqish"</string>
@@ -1935,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"Yopish"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Javob berish"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"Rad etish"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Tugatish"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Kiruvchi chaqiruv"</string>
@@ -2078,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Bu bildirishnoma darajasi Tovushsiz darajaga tushirildi Fikr-mulohaza bildirish uchun bosing."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Bu bildirishnoma darajasi oshirildi. Fikr-mulohaza bildirish uchun bosing."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Bu bildirishnoma darajasi pasaytirildi. Fikr-mulohaza bildirish uchun bosing."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Yangicha bildirishnomalar"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Amallar, javoblar va boshqa takliflarni olishda davom etish uchun yaxshilangan bildirishnomalarni yoqing. Android moslashuvchan bildirishnomalari endi ishlamaydi."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Yoqish"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Hozir emas"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Batafsil"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Yangicha bildirishnomalar funksiyasi barcha bildirishnomalarni, jumladan, shaxsiy maʼlumotlarni (kontakt nomlari va xabarlar) oʻqiy oladi. Shuningdek, bu funksiya bildirishnomalarni yopishi yoki telefon chaqiruvlariga javob berish kabi bildirishnomalarda tugmalar bilan amallar bajarishi mumkin.\n\nBu funksiya Faqat muhim rejimini yoqishi va faolsizlantirishi yoki unga aloqador sozlamalarni oʻzgartirishi ham mumkin."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Kun tartibi rejimi haqidagi bildirishnoma"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Batareya quvvati odatdagidan ertaroq tugashi mumkin"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Batareya quvvatini uzoqroq vaqtga yetkazish uchun quvvat tejash rejimi yoqildi"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 41edab4..77d81d8 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -531,17 +531,15 @@
<string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"Cho phép ứng dụng kết nối và ngắt kết nối thiết bị Android TV khỏi mạng WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"Cho phép ứng dụng kết nối điện thoại và ngắt kết nối điện thoại khỏi mạng WiMAX."</string>
<string name="permlab_bluetooth" msgid="586333280736937209">"ghép nối với thiết bị Bluetooth"</string>
- <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Cho phép ứng dụng xem cấu hình của Bluetooth trên máy tính bảng và tạo và chấp nhận các kết nối với các thiết bị được ghép nối."</string>
- <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Cho phép ứng dụng xem cấu hình của Bluetooth trên thiết bị Android TV, đồng thời tạo và chấp nhận các kết nối với thiết bị được ghép nối."</string>
- <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Cho phép ứng dụng xem cấu hình của Bluetooth trên điện thoại, tạo và chấp nhận các kết nối với các thiết bị được ghép nối."</string>
+ <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Cho phép ứng dụng xem cấu hình của Bluetooth trên máy tính bảng và tạo và chấp nhận các kết nối với các thiết bị đã ghép nối."</string>
+ <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Cho phép ứng dụng xem cấu hình của Bluetooth trên thiết bị Android TV, đồng thời tạo và chấp nhận các kết nối với thiết bị đã ghép nối."</string>
+ <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Cho phép ứng dụng xem cấu hình của Bluetooth trên điện thoại, tạo và chấp nhận các kết nối với các thiết bị đã ghép nối."</string>
<string name="permlab_bluetooth_scan" msgid="5402587142833124594">"khám phá và ghép nối với thiết bị Bluetooth ở gần"</string>
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Cho phép ứng dụng khám phá và ghép nối với các thiết bị Bluetooth ở gần"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"kết nối với các thiết bị Bluetooth đã ghép nối"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"Cho phép ứng dụng kết nối với thiết bị Bluetooth đã ghép nối"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"quảng cáo trên các thiết bị Bluetooth ở gần"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Cho phép ứng dụng quảng cáo trên các thiết bị Bluetooth ở gần"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"xác định khoảng cách tương đối giữa các thiết bị ở gần dùng Băng tần siêu rộng"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Cho phép ứng dụng xác định khoảng cách tương đối giữa các thiết bị ở gần dùng Băng tần siêu rộng"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Thông tin về dịch vụ thanh toán qua công nghệ giao tiếp tầm gần (NFC) được ưu tiên"</string>
@@ -1388,8 +1386,8 @@
<string name="share_remote_bugreport_action" msgid="7630880678785123682">"CHIA SẺ"</string>
<string name="decline_remote_bugreport_action" msgid="4040894777519784346">"TỪ CHỐI"</string>
<string name="select_input_method" msgid="3971267998568587025">"Chọn phương thức nhập"</string>
- <string name="show_ime" msgid="6406112007347443383">"Hiển thị bàn phím ảo trên màn hình trong khi bàn phím vật lý đang hoạt động"</string>
- <string name="hardware" msgid="1800597768237606953">"Hiển thị bàn phím ảo"</string>
+ <string name="show_ime" msgid="6406112007347443383">"Hiện bàn phím ảo trên màn hình trong khi bàn phím vật lý đang hoạt động"</string>
+ <string name="hardware" msgid="1800597768237606953">"Hiện bàn phím ảo"</string>
<string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Định cấu hình bàn phím vật lý"</string>
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Nhấn để chọn ngôn ngữ và bố cục"</string>
<string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Tính năng Tiết kiệm pin sẽ bật Giao diện tối, đồng thời tắt hoặc hạn chế hoạt động chạy trong nền, một số hiệu ứng hình ảnh và các tính năng như lệnh “Ok Google”\n\n"<annotation id="url">"Tìm hiểu thêm"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Tính năng Tiết kiệm pin sẽ bật Giao diện tối, đồng thời tắt hoặc hạn chế hoạt động chạy trong nền, một số hiệu ứng hình ảnh và các tính năng như lệnh “Ok Google”."</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>
@@ -1933,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Đóng"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Trả lời"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Từ chối"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Kết thúc"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Cuộc gọi đến"</string>
@@ -2076,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Hệ thống đã hạ mức ưu tiên của thông báo này xuống thành Im lặng. Hãy nhấn để chia sẻ ý kiến phản hồi."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Hệ thống đã nâng mức ưu tiên của thông báo này. Hãy nhấn để chia sẻ ý kiến phản hồi."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Hệ thống đã hạ mức ưu tiên của thông báo này. Hãy nhấn để chia sẻ ý kiến phản hồi."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Dùng thử thông báo nâng cao"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Để tiếp tục nhận các thao tác đề xuất, câu trả lời và nhiều nội dung khác, hãy bật thông báo nâng cao. Thông báo thích ứng trên Android không được hỗ trợ nữa."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Bật"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Để sau"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Tìm hiểu thêm"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Thông báo nâng cao có thể đọc mọi nội dung thông báo, bao gồm cả thông tin cá nhân như tên liên hệ và tin nhắn. Tính năng này cũng có thể đóng các thông báo hoặc thực hiện thao tác đối với các nút trong thông báo, chẳng hạn như trả lời cuộc gọi điện thoại.\n\nTính năng này cũng có thể bật hoặc tắt Chế độ ưu tiên và thay đổi các chế độ cài đặt liên quan."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Thông báo cung cấp thông tin về chế độ sạc thông thường"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Pin có thể hết trước khi sạc bình thường"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Trình tiết kiệm pin được kích hoạt để kéo dài thời lượng pin"</string>
@@ -2275,14 +2266,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Giờ đây, bạn có thể phóng to một phần màn hình"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Bật trong phần Cài đặt"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Đóng"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Bỏ chặn micrô của thiết bị"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Bỏ chặn máy ảnh của thiết bị"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Cho <b><xliff:g id="APP">%s</xliff:g></b> cũng như mọi ứng dụng và dịch vụ"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Bỏ chặn"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Quyền riêng tư khi sử dụng cảm biến"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Biểu tượng ứng dụng"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Hình ảnh thương hiệu của ứng dụng"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 8d8485d..1d4ee44 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"允许该应用发现附近的蓝牙设备并与其配对"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"连接到已配对的蓝牙设备"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"允许该应用连接到已配对的蓝牙设备"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"向附近的蓝牙设备广播"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"允许该应用向附近的蓝牙设备广播"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"确定附近超宽带设备之间的相对位置"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"允许应用确定附近超宽带设备之间的相对位置"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"首选 NFC 付款服务信息"</string>
@@ -1388,7 +1386,7 @@
<string name="share_remote_bugreport_action" msgid="7630880678785123682">"分享"</string>
<string name="decline_remote_bugreport_action" msgid="4040894777519784346">"拒绝"</string>
<string name="select_input_method" msgid="3971267998568587025">"选择输入法"</string>
- <string name="show_ime" msgid="6406112007347443383">"在连接到实体键盘时保持显示"</string>
+ <string name="show_ime" msgid="6406112007347443383">"开启后,连接到实体键盘时,它会一直显示在屏幕上"</string>
<string name="hardware" msgid="1800597768237606953">"显示虚拟键盘"</string>
<string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"配置实体键盘"</string>
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"点按即可选择语言和布局"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"省电模式会开启深色主题,并限制或关闭后台活动、部分视觉效果和“Ok Google”等功能\n\n"<annotation id="url">"了解详情"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"省电模式会开启深色主题,并限制或关闭后台活动、部分视觉效果和“Ok Google”等功能。"</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>
@@ -1933,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"关闭"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>:<xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"接听"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"拒接"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"挂断"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"来电"</string>
@@ -2076,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"系统已将此通知的重要性降低为“静音”。点按即可提供反馈。"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"系统已提升此通知的重要性。点按即可提供反馈。"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"系统已降低此通知的重要性。点按即可提供反馈。"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"试用增强型通知"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"如需继续接收建议的操作、回复等内容,请开启增强型通知功能。系统不再支持 Android 自动调节通知功能。"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"开启"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"以后再说"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"了解详情"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"增强型通知功能可以读取所有通知内容,包括联系人姓名和消息等个人信息。该功能也能关闭通知或触发通知中的按钮,例如接听来电。\n\n此外,该功能还能开启或关闭“优先”模式,以及更改相关设置。"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"日常安排模式信息通知"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"电池电量可能会在您平时的充电时间之前耗尽"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"已启用省电模式以延长电池续航时间"</string>
@@ -2275,14 +2267,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"现在您可以放大屏幕上的部分内容"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"在“设置”中开启"</string>
<string name="dismiss_action" msgid="1728820550388704784">"关闭"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"取消禁用设备麦克风"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"取消禁用设备摄像头"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"针对<b><xliff:g id="APP">%s</xliff:g></b>及所有应用和服务"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"取消禁用"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"传感器隐私权"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"应用图标"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"应用品牌图片"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 68e06b13..9c6b851 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -445,7 +445,7 @@
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"即使您不使用此應用程式,它仍可隨時存取位置。"</string>
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"更改音效設定"</string>
<string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"允許應用程式修改全域音頻設定,例如音量和用於輸出的喇叭。"</string>
- <string name="permlab_recordAudio" msgid="1208457423054219147">"錄製音效"</string>
+ <string name="permlab_recordAudio" msgid="1208457423054219147">"錄音"</string>
<string name="permdesc_recordAudio" msgid="5857246765327514062">"此應用程式在使用期間可使用麥克風錄音。"</string>
<string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"在背景錄音"</string>
<string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"此應用程式可隨時使用麥克風錄音。"</string>
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"允許應用程式探索並配對附近的藍牙裝置"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"連接附近的藍牙裝置"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"允許應用程式連接已配對的藍牙裝置"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"向附近的藍牙裝置宣傳"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"允許應用程式向附近的藍牙裝置宣傳"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"判斷附近超寬頻裝置之間的相對位置"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"允許應用程式判斷附近超寬頻裝置之間的相對位置"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"由用戶允許授權的 NFC 付款服務資訊"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"「省電模式」會開啟深色主題背景並限制或關閉背景活動、部分視覺效果,以及「Ok Google」啟動字詞等功能。\n\n"<annotation id="url">"瞭解詳情"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"「省電模式」會開啟深色主題背景並限制或關閉背景活動、部分視覺效果,以及「Ok Google」啟動字詞等功能。"</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>
@@ -1933,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"關閉"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>:<xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"接聽"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"視像"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"拒接"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"掛斷"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"來電"</string>
@@ -2076,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"此通知的重要性已降低為「靜音」。輕按即可提供意見。"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"此通知的重要性已提升。輕按即可提供意見。"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"此通知的重要性已降級。輕按即可提供意見。"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"試用強化通知"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"如要繼續收到建議操作和回覆等內容,請開啟強化通知功能。系統已不再支援 Android 自動調整通知功能。"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"開啟"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"暫時不要"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"瞭解詳情"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"強化通知功能可讀取所有通知內容 (包括聯絡人姓名和訊息等個人資料),以及關閉通知或針對通知中的按鈕採取行動,例如接聽來電。\n\n此功能亦可開啟或關閉「優先」模式及變更相關設定。"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"「日常安排模式」資料通知"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"電量可能會在日常充電前耗盡"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"「省電模式」已啟用,以便延長電池壽命"</string>
@@ -2275,14 +2266,10 @@
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"您現在可以放大部分畫面"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"在「設定」中開啟"</string>
<string name="dismiss_action" msgid="1728820550388704784">"關閉"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"解除封鎖裝置麥克風"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"解除封鎖裝置相機"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"適用於「<b><xliff:g id="APP">%s</xliff:g></b>」和所有應用程式及服務"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"解除封鎖"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"感應器私隱"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"應用程式圖示"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"應用程式品牌形象"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 78f326b..60cfa7e 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -350,9 +350,9 @@
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"在已鎖定的裝置上以全螢幕活動的形式顯示通知"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"允許應用程式在已鎖定的裝置上以全螢幕活動的形式顯示通知"</string>
<string name="permlab_install_shortcut" msgid="7451554307502256221">"安裝捷徑"</string>
- <string name="permdesc_install_shortcut" msgid="4476328467240212503">"允許應用程式自動新增主螢幕捷徑。"</string>
+ <string name="permdesc_install_shortcut" msgid="4476328467240212503">"允許應用程式自動新增主畫面捷徑。"</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"解除安裝捷徑"</string>
- <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"允許應用程式自動移除主螢幕捷徑。"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"允許應用程式自動移除主畫面捷徑。"</string>
<string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"重設撥號路徑"</string>
<string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"允許應用程式在撥打電話期間查看撥出的電話號碼,並可選擇改撥其他號碼或中斷通話。"</string>
<string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"接聽電話"</string>
@@ -538,10 +538,8 @@
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"允許應用程式探索鄰近藍牙裝置並進行配對"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"與已配對的藍牙裝置連線"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"允許應用程式與已配對的藍牙裝置連線"</string>
- <!-- no translation found for permlab_bluetooth_advertise (2781147747928853177) -->
- <skip />
- <!-- no translation found for permdesc_bluetooth_advertise (6085174451034210183) -->
- <skip />
+ <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"向附近的藍牙裝置廣播"</string>
+ <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"允許應用程式向附近的藍牙裝置廣播"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"判斷附近超寬頻裝置間的相對位置"</string>
<string name="permdesc_uwb_ranging" msgid="2519723069604307055">"允許應用程式判斷附近超寬頻裝置間的相對位置"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"首選 NFC 付費服務資訊"</string>
@@ -1204,7 +1202,7 @@
<string name="whichSendToApplicationNamed" msgid="3385686512014670003">"透過「%1$s」傳送"</string>
<string name="whichSendToApplicationLabel" msgid="3543240188816513303">"傳送"</string>
<string name="whichHomeApplication" msgid="8276350727038396616">"選取主畫面應用程式"</string>
- <string name="whichHomeApplicationNamed" msgid="5855990024847433794">"使用「%1$s」做為主螢幕"</string>
+ <string name="whichHomeApplicationNamed" msgid="5855990024847433794">"使用「%1$s」做為主畫面"</string>
<string name="whichHomeApplicationLabel" msgid="8907334282202933959">"擷取圖片"</string>
<string name="whichImageCaptureApplication" msgid="2737413019463215284">"使用以下應用程式擷取圖片:"</string>
<string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"使用「%1$s」擷取圖片"</string>
@@ -1718,7 +1716,7 @@
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"輕觸無障礙工具按鈕後,選擇你想使用的功能:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"選擇要搭配無障礙手勢 (用兩指從螢幕底部向上滑動) 使用的功能:"</string>
<string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"選擇要搭配無障礙手勢 (用三指從螢幕底部向上滑動) 使用的功能:"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"如要切換不同的功能,請輕觸並按住無障礙工具按鈕。"</string>
+ <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"如要切換不同的功能,請按住無障礙工具按鈕。"</string>
<string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"如要切換不同的功能,請用兩指向上滑動並按住。"</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"如要切換不同的功能,請用三指向上滑動並按住。"</string>
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"放大"</string>
@@ -1854,10 +1852,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 (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"省電模式會開啟深色主題並限制或關閉背景活動、部分視覺效果,以及「Ok Google」啟動字詞等功能\n\n"<annotation id="url">"瞭解詳情"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"省電模式會開啟深色主題並限制或關閉背景活動、部分視覺效果,以及「Ok Google」啟動字詞等功能。"</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>
@@ -1933,6 +1929,8 @@
<string name="close_button_text" msgid="10603510034455258">"關閉"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>:<xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"接聽"</string>
+ <!-- no translation found for call_notification_answer_video_action (2086030940195382249) -->
+ <skip />
<string name="call_notification_decline_action" msgid="3700345945214000726">"拒接"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"掛斷"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"來電"</string>
@@ -2076,18 +2074,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"這則通知的重要性已降低為「靜音」。輕觸即可提供意見。"</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"這則通知的重要性順序已調高。輕觸即可提供意見。"</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"這則通知的重要性順序已調降。輕觸即可提供意見。"</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"試試看加強型通知"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"如要繼續收到建議的操作、回覆等內容,請開啟加強型通知功能。系統已不再支援 Android 自動調整通知功能。"</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"開啟"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"暫時不要"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"瞭解詳情"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"加強型通知功能可讀取所有通知內容,包括聯絡人名稱和訊息內文等個人資訊。這項功能也能關閉通知或操作通知中的按鈕,例如接聽來電。\n\n此外,這項功能還可以開啟或關閉「優先」模式及變更相關設定。"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"日常安排模式資訊通知"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"電池電力可能會在你平常的充電時間前耗盡"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"已啟用省電模式以延長電池續航力"</string>
@@ -2123,7 +2115,7 @@
<string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"沒有建議的分享對象"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"應用程式清單"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"這個應用程式未取得錄製內容的權限,但可以透過這部 USB 裝置錄製音訊。"</string>
- <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"主螢幕"</string>
+ <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"主畫面"</string>
<string name="accessibility_system_action_back_label" msgid="4205361367345537608">"返回"</string>
<string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"最近使用的應用程式"</string>
<string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"通知"</string>
@@ -2271,18 +2263,14 @@
<string name="config_pdp_reject_user_authentication_failed" msgid="4531693033885744689"></string>
<string name="config_pdp_reject_service_not_subscribed" msgid="8190338397128671588"></string>
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
- <string name="window_magnification_prompt_title" msgid="2876703640772778215">"放大功能推出新設定"</string>
+ <string name="window_magnification_prompt_title" msgid="2876703640772778215">"支援新的放大功能"</string>
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"你現在可以放大局部畫面了"</string>
<string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"在「設定」中開啟"</string>
<string name="dismiss_action" msgid="1728820550388704784">"關閉"</string>
- <!-- no translation found for sensor_privacy_start_use_mic_notification_content_title (2420858361276370367) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_camera_notification_content_title (7287720213963466672) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_notification_content_text (7595608891015777346) -->
- <skip />
- <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (7089318886628390827) -->
- <skip />
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"將裝置麥克風解除封鎖"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"將裝置相機解除封鎖"</string>
+ <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"允許「<xliff:g id="APP">%s</xliff:g>」<b></b>及所有應用程式和服務使用"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"解除封鎖"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"感應器隱私權"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"應用程式圖示"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"應用程式品牌圖片"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 1eb8438..e2a52f1 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1852,10 +1852,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Kubuyekezwe umlawuli wakho"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Kususwe umlawuli wakho"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"KULUNGILE"</string>
- <!-- no translation found for battery_saver_description_with_learn_more (7963058670863485450) -->
- <skip />
- <!-- no translation found for battery_saver_description (7695751399533397741) -->
- <skip />
+ <string name="battery_saver_description_with_learn_more" msgid="7963058670863485450">"Isilondolozi Sebhethri sivula Itimu Emnyama futhi sikhawulele noma sivale umsebenzi wendawo engemuva, izakhi ezithile ezibonakalayo, nezakhi ezinjenge-\"Ok Google\"\n\n"<annotation id="url">"Funda kabanzi"</annotation></string>
+ <string name="battery_saver_description" msgid="7695751399533397741">"Isilondolozi Sebhethri sivula Itimu Emnyama futhi sikhawulele noma sivale umsebenzi wendawo engemuva, izakhi ezithile ezibonakalayo, nezakhi ezinjenge-\"Ok Google\"."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Ukusiza ukwehlisa ukusetshenziswa kwedatha, iseva yedatha igwema ezinye izinhlelo zokusebenza ukuthi zithumele noma zamukele idatha ngasemuva. Uhlelo lokusebenza olisebenzisa okwamanje lingafinyelela idatha, kodwa lingenza kanjalo kancane. Lokhu kungachaza, isibonelo, ukuthi izithombe azibonisi uze uzithephe."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Vula iseva yedatha?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Vula"</string>
@@ -1931,6 +1929,7 @@
<string name="close_button_text" msgid="10603510034455258">"Vala"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
<string name="call_notification_answer_action" msgid="5999246836247132937">"Phendula"</string>
+ <string name="call_notification_answer_video_action" msgid="2086030940195382249">"Ividiyo"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Yenqaba"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Vala Ucingo"</string>
<string name="call_notification_incoming_text" msgid="6143109825406638201">"Ikholi engenayo"</string>
@@ -1946,7 +1945,7 @@
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Isaziso sohlelo lokusebenza olungokwezifiso"</string>
<string name="user_creation_account_exists" msgid="2239146360099708035">"Vumela i-<xliff:g id="APP">%1$s</xliff:g> ukuthi idale umsebenzisi omusha nge-<xliff:g id="ACCOUNT">%2$s</xliff:g> (Umsebenzisi onale akhawunti usevele ukhona) ?"</string>
<string name="user_creation_adding" msgid="7305185499667958364">"Vumela i-<xliff:g id="APP">%1$s</xliff:g> ukuthi idale umsebenzisi omusha nge-<xliff:g id="ACCOUNT">%2$s</xliff:g> ?"</string>
- <string name="language_selection_title" msgid="52674936078683285">"Engeza ulwimi"</string>
+ <string name="language_selection_title" msgid="52674936078683285">"Engeza ulimi"</string>
<string name="country_selection_title" msgid="5221495687299014379">"Okuncamelayo kwesifunda"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Thayipha igama lolimi"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Okuphakanyisiwe"</string>
@@ -2074,18 +2073,12 @@
<string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Lesi saziso sehliselwe esikhundleni Sokuthula. Thepha ukuze unikeze impendulo."</string>
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Lesi saziso sibekwe ezingeni eliphakeme. Thepha ukuze unikeze impendulo."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Lesi saziso sibekwe ezingeni eliphansi. Thepha ukuze unikeze impendulo."</string>
- <!-- no translation found for nas_upgrade_notification_title (4224351129445073051) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_content (7036860187157134706) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_enable_action (4823652531622744798) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_disable_action (7561210256700811433) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_action (7011130656195423947) -->
- <skip />
- <!-- no translation found for nas_upgrade_notification_learn_more_content (6276343083934111208) -->
- <skip />
+ <string name="nas_upgrade_notification_title" msgid="4224351129445073051">"Zama izaziso ezigqanyisiwe"</string>
+ <string name="nas_upgrade_notification_content" msgid="7036860187157134706">"Ukuze uqhubeke nokuthola izenzo eziphakanyisiwe, izimpendulo nokuningi, vula izaziso ezigqanyisiwe. Izaziso ze-Androin Ezivumelana Nezimo azisasekelwe."</string>
+ <string name="nas_upgrade_notification_enable_action" msgid="4823652531622744798">"Vula"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="7561210256700811433">"Hhayi manje"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Funda kabanzi"</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="6276343083934111208">"Izaziso ezigqanyisiwe zingafunda konke okuqukethwe yizaziso zakho, kuhlanganise nolwazi lomuntu siqu njengamagama oxhumana nabo nemilayezo. Lesi sakhi singacashisa izaziso noma sithathe izinyathelo ezinkinobheni ezisezazisweni, njengokuphendula amakholi wefoni.\n\nLesi sici singavula noma sivale nemodi Yokubalulekile futhi sishintshe amasethingi ahambisanayo."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Isaziso solwazi lwe-Routine Mode"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Ibhethri lingaphela ngaphambi kokushaja okuvamile"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Isilondolozi sebhethri siyasebenza ngaphandle kwempilo yebhethri"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index bd1d848..f097009 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1176,6 +1176,9 @@
<!-- Alternative color applied to surfaces on top of colorBackground. @hide -->
<attr name="colorSurfaceHeader" format="color" />
+ <!-- Color applied to effects. -->
+ <attr name="effectColor" format="color" />
+
<!-- The type of the edge effect. The default is glow. -->
<attr name="edgeEffectType">
<!-- Use a colored glow at the edge. -->
@@ -6540,13 +6543,8 @@
<!-- The radius of the ripple when fully expanded. By default, the
radius is computed based on the size of the ripple's container. -->
<attr name="radius" />
- <!-- The style of the ripple drawable is solid by default -->
- <attr name="rippleStyle">
- <!-- Solid is the default style -->
- <enum name="solid" value="0" />
- <!-- Patterned style-->
- <enum name="patterned" value="1" />
- </attr>
+ <!-- Secondary color of the ripple effect. -->
+ <attr name="effectColor" />
</declare-styleable>
<declare-styleable name="ScaleDrawable">
@@ -9577,7 +9575,7 @@
<!-- Perceptual luminance of a color, in accessibility friendly color space. From 0 to 100. -->
<attr name="lStar" format="float"/>
- <declare-styleable name="DisplayHasherService">
+ <declare-styleable name="DisplayHashingService">
<!-- The throttle duration for display hash requests
@hide @SystemApi -->
<attr name="throttleDurationMillis" format="integer" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c51b2d8..3b155de 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -826,7 +826,7 @@
that created the task, and therefore there will only be one instance of this activity
in a task. In constrast to the {@code singleTask} launch mode, this activity can be
started in multiple instances in different tasks if the
- {@code FLAG_ACTIVITY_MULTIPLE_TASK} is set.-->
+ {@code FLAG_ACTIVITY_MULTIPLE_TASK} or {@code FLAG_ACTIVITY_NEW_DOCUMENT} is set.-->
<enum name="singleInstancePerTask" value="4" />
</attr>
<!-- Specify the orientation an activity should be run in. If not
@@ -1880,6 +1880,9 @@
apps due to inconsistencies in MediaStore collection and lower file system.
When the flag is set, app should scan the file after file path operations to ensure
consistency of MediaStore collection.
+ <p> The flag can be set to false if the app doesn't do many bulk file path operations or if
+ app prefers the system to ensure the consistency of the MediaStore collection for file path
+ operations without scanning the file.
<p> The default value is {@code true} if
<ul>
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index bec0ad1..4c5a008 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -17,9 +17,9 @@
<!-- Colors specific to DeviceDefault themes. These are mostly pass-throughs to enable
overlaying new theme colors. -->
<resources>
- <color name="primary_device_default_dark">@color/system_neutral1_800</color>
+ <color name="primary_device_default_dark">@color/system_neutral1_900</color>
<color name="primary_device_default_light">@color/system_neutral1_50</color>
- <color name="primary_device_default_settings">@color/system_neutral1_800</color>
+ <color name="primary_device_default_settings">@color/system_neutral1_900</color>
<color name="primary_device_default_settings_light">@color/primary_device_default_light</color>
<color name="primary_dark_device_default_dark">@color/primary_device_default_dark</color>
<color name="primary_dark_device_default_light">@color/primary_device_default_light</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 906a740..f24d663 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1772,6 +1772,9 @@
<!-- Add algorithm here -->
</string-array>
+ <!-- Boolean indicating if placing the phone face down will result in a screen off. -->
+ <bool name="config_flipToScreenOffEnabled">true</bool>
+
<!-- Boolean indicating if current platform supports bluetooth SCO for off call
use cases -->
<bool name="config_bluetooth_sco_off_call">true</bool>
@@ -4697,6 +4700,11 @@
-->
<color name="config_letterboxBackgroundColor">#000</color>
+ <!-- Horizonal position of a center of the letterboxed app window.
+ 0 corresponds to the left side of the screen and 1 to the right side. If given value < 0
+ or > 1, it is ignored and central positionis used (0.5). -->
+ <item name="config_letterboxHorizontalPositionMultiplier" format="float" type="dimen">0.5</item>
+
<!-- If true, hide the display cutout with display area -->
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index f6a67d2..2a76c57 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3099,6 +3099,7 @@
<!-- @hide @SystemApi -->
<public name="throttleDurationMillis" />
<public name="showInInputMethodPicker" />
+ <public name="effectColor" />
</staging-public-group>
<staging-public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 90c0691d..5715fab 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -275,6 +275,7 @@
<java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
<java-symbol type="bool" name="config_avoidGfxAccel" />
<java-symbol type="bool" name="config_bluetooth_address_validation" />
+ <java-symbol type="bool" name="config_flipToScreenOffEnabled" />
<java-symbol type="bool" name="config_bluetooth_sco_off_call" />
<java-symbol type="bool" name="config_bluetooth_le_peripheral_mode_supported" />
<java-symbol type="bool" name="config_bluetooth_hfp_inband_ringing_support" />
@@ -4185,6 +4186,7 @@
<java-symbol type="dimen" name="config_letterboxBackgroundWallaperDarkScrimAlpha" />
<java-symbol type="integer" name="config_letterboxBackgroundType" />
<java-symbol type="color" name="config_letterboxBackgroundColor" />
+ <java-symbol type="dimen" name="config_letterboxHorizontalPositionMultiplier" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
@@ -4339,4 +4341,6 @@
<java-symbol type="drawable" name="ic_accessibility_24dp" />
<java-symbol type="string" name="view_and_control_notification_title" />
<java-symbol type="string" name="view_and_control_notification_content" />
+
+ <java-symbol type="layout" name="notification_expand_button"/>
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 1d4beae..41bedb2 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -234,9 +234,6 @@
<item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
<item name="colorForeground">@color/foreground_device_default_dark</item>
<item name="colorForegroundInverse">@color/foreground_device_default_light</item>
-
- <!-- Ripple style-->
- <item name="rippleStyle">solid</item>
</style>
<style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_amp_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_amp_24.xml
new file mode 100644
index 0000000..1f57318
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_amp_24.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="#d14d2c">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11.17,19.5h-0.83l0.82,-5.83 -4.18,0.01 5.85,-9.17h0.83l-0.84,5.84h4.17l-5.82,9.15z"/>
+</vector>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_calculate_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_calculate_24.xml
new file mode 100644
index 0000000..70aac32
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_calculate_24.xml
@@ -0,0 +1,25 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="#269e5c">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM19,19H5V5h14V19z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M6.25,7.72h5v1.5h-5z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M13,15.75h5v1.5h-5z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M13,13.25h5v1.5h-5z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M8,18l1.5,0l0,-2l2,0l0,-1.5l-2,0l0,-2l-1.5,0l0,2l-2,0l0,1.5l2,0z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M14.09,10.95l1.41,-1.41l1.41,1.41l1.06,-1.06l-1.41,-1.42l1.41,-1.41l-1.06,-1.06l-1.41,1.41l-1.41,-1.41l-1.06,1.06l1.41,1.41l-1.41,1.42z"/>
+</vector>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_custom_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_custom_24.xml
new file mode 100644
index 0000000..39f9689
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_custom_24.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="#d14d2c">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22,9L22,7h-2L20,5c0,-1.1 -0.9,-2 -2,-2L4,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-2h2v-2h-2v-2h2v-2h-2L20,9h2zM18,19L4,19L4,5h14v14zM6,13h5v4L6,17v-4zM12,7h4v3h-4L12,7zM6,7h5v5L6,12L6,7zM12,11h4v6h-4v-6z"/>
+</vector>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_timer_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_timer_24.xml
new file mode 100644
index 0000000..9cae545
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_timer_24.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M15,3L9,3L9,1h6v2zM11,14h2L13,8h-2v6zM21,13.01c0,4.97 -4.02,9 -9,9s-9,-4.03 -9,-9 4.03,-9 9,-9c2.12,0 4.07,0.74 5.62,1.98l1.42,-1.42c0.51,0.42 0.98,0.9 1.41,1.41L19.03,7.4C20.26,8.93 21,10.89 21,13.01zM19,13.01c0,-3.87 -3.13,-7 -7,-7s-7,3.13 -7,7 3.13,7 7,7 7,-3.13 7,-7z"/>
+</vector>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml
index 1ced825..98fc581 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml
@@ -25,6 +25,13 @@
android:paddingTop="8dp"
android:paddingBottom="8dp">
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="8dp"/>
+
<TextView
android:id="@+id/title"
android:layout_width="0dp"
@@ -34,16 +41,18 @@
<TextView
android:id="@+id/amount"
- android:layout_width="0dp"
- android:layout_weight="0.7"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
android:gravity="right"
+ android:maxLines="1"
android:textAppearance="@style/TextAppearanceBody"/>
<TextView
android:id="@+id/percent"
- android:layout_width="64dp"
+ android:layout_width="76dp"
android:layout_height="wrap_content"
android:gravity="right"
+ android:maxLines="1"
android:textAppearance="@style/TextAppearanceBody"/>
</LinearLayout>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml
index e58a08f..24d193c4 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml
@@ -43,10 +43,40 @@
android:paddingEnd="10dp">
<include layout="@layout/battery_consumer_info_layout"/>
-
</LinearLayout>
+
</androidx.cardview.widget.CardView>
+
+ <LinearLayout
+ android:id="@+id/headings"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="2dp"
+ android:paddingBottom="4dp">
+ <FrameLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ <TextView
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:gravity="end"
+ android:paddingEnd="10dp"
+ android:text="Total"/>
+ <TextView
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:gravity="end"
+ android:paddingEnd="30dp"
+ android:text="Apps"/>
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="@android:color/darker_gray"/>
+
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/battery_consumer_data_view"
android:layout_width="match_parent"
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml
new file mode 100644
index 0000000..2dbe48b
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <color name="battery_consumer_bg_power_profile">#ffffff</color>
+ <color name="battery_consumer_bg_measured_energy">#fff5eb</color>
+</resources>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
index 8df3de3..f7d7098 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
@@ -18,32 +18,21 @@
import android.content.Context;
import android.os.BatteryConsumer;
-import android.os.BatteryStats;
import android.os.BatteryUsageStats;
-import android.os.Process;
import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
-
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatteryStatsHelper;
+import android.util.DebugUtils;
import java.util.ArrayList;
import java.util.List;
public class BatteryConsumerData {
- private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar";
- private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media";
- private static final String PACKAGE_SYSTEMUI = "com.android.systemui";
- private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER,
- PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI};
-
- // Unit conversion:
- // mAh = uC * (1/1000)(milli/micro) * (1/3600)(hours/second)
- private static final double UC_2_MAH = (1.0 / 1000) * (1.0 / 3600);
enum EntryType {
- POWER,
+ POWER_MODELED,
+ POWER_MEASURED,
+ POWER_CUSTOM,
DURATION,
}
@@ -52,260 +41,155 @@
public EntryType entryType;
public double value;
public double total;
+ public boolean isSystemBatteryConsumer;
}
private final BatteryConsumerInfoHelper.BatteryConsumerInfo mBatteryConsumerInfo;
private final List<Entry> mEntries = new ArrayList<>();
- public BatteryConsumerData(Context context, BatteryStatsHelper batteryStatsHelper,
+ public BatteryConsumerData(Context context,
List<BatteryUsageStats> batteryUsageStatsList, String batteryConsumerId) {
BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0);
- BatteryUsageStats powerProfileModeledUsageStats = batteryUsageStatsList.get(1);
- List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
- BatteryStats batteryStats = batteryStatsHelper.getStats();
+ BatteryUsageStats modeledBatteryUsageStats = batteryUsageStatsList.get(1);
- double totalPowerMah = 0;
- double totalSmearedPowerMah = 0;
- double totalPowerExcludeSystemMah = 0;
- double totalScreenPower = 0;
- double totalProportionalSmearMah = 0;
- double totalCpuPowerMah = 0;
- double totalSystemServiceCpuPowerMah = 0;
- double totalUsagePowerMah = 0;
- double totalWakeLockPowerMah = 0;
- double totalMobileRadioPowerMah = 0;
- double totalWifiPowerMah = 0;
- double totalBluetoothPowerMah = 0;
- double totalGpsPowerMah = 0;
- double totalCameraPowerMah = 0;
- double totalFlashlightPowerMah = 0;
- double totalSensorPowerMah = 0;
- double totalAudioPowerMah = 0;
- double totalVideoPowerMah = 0;
+ BatteryConsumer requestedBatteryConsumer = getRequestedBatteryConsumer(batteryUsageStats,
+ batteryConsumerId);
+ BatteryConsumer requestedModeledBatteryConsumer = getRequestedBatteryConsumer(
+ modeledBatteryUsageStats, batteryConsumerId);
- long totalCpuTimeMs = 0;
- long totalCpuFgTimeMs = 0;
- long totalWakeLockTimeMs = 0;
- long totalWifiRunningTimeMs = 0;
- long totalBluetoothRunningTimeMs = 0;
- long totalGpsTimeMs = 0;
- long totalCameraTimeMs = 0;
- long totalFlashlightTimeMs = 0;
- long totalAudioTimeMs = 0;
- long totalVideoTimeMs = 0;
-
- BatterySipper requestedBatterySipper = null;
- for (BatterySipper sipper : usageList) {
- if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
- totalScreenPower = sipper.sumPower();
- }
-
- if (batteryConsumerId(sipper).equals(batteryConsumerId)) {
- requestedBatterySipper = sipper;
- }
-
- totalPowerMah += sipper.sumPower();
- totalSmearedPowerMah += sipper.totalSmearedPowerMah;
- totalProportionalSmearMah += sipper.proportionalSmearMah;
-
- if (!isSystemSipper(sipper)) {
- totalPowerExcludeSystemMah += sipper.totalSmearedPowerMah;
- }
-
- totalCpuPowerMah += sipper.cpuPowerMah;
- totalSystemServiceCpuPowerMah += sipper.systemServiceCpuPowerMah;
- totalUsagePowerMah += sipper.usagePowerMah;
- totalWakeLockPowerMah += sipper.wakeLockPowerMah;
- totalMobileRadioPowerMah += sipper.mobileRadioPowerMah;
- totalWifiPowerMah += sipper.wifiPowerMah;
- totalBluetoothPowerMah += sipper.bluetoothPowerMah;
- totalGpsPowerMah += sipper.gpsPowerMah;
- totalCameraPowerMah += sipper.cameraPowerMah;
- totalFlashlightPowerMah += sipper.flashlightPowerMah;
- totalSensorPowerMah += sipper.sensorPowerMah;
- totalAudioPowerMah += sipper.audioPowerMah;
- totalVideoPowerMah += sipper.videoPowerMah;
-
- totalCpuTimeMs += sipper.cpuTimeMs;
- totalCpuFgTimeMs += sipper.cpuFgTimeMs;
- totalWakeLockTimeMs += sipper.wakeLockTimeMs;
- totalWifiRunningTimeMs += sipper.wifiRunningTimeMs;
- totalBluetoothRunningTimeMs += sipper.bluetoothRunningTimeMs;
- totalGpsTimeMs += sipper.gpsTimeMs;
- totalCameraTimeMs += sipper.cameraTimeMs;
- totalFlashlightTimeMs += sipper.flashlightTimeMs;
- totalAudioTimeMs += sipper.audioTimeMs;
- totalVideoTimeMs += sipper.videoTimeMs;
- }
-
- BatteryConsumer requestedBatteryConsumer = null;
-
- for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
- if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
- requestedBatteryConsumer = consumer;
- }
- }
-
- double totalModeledCpuPowerMah = 0;
- BatteryConsumer requestedBatteryConsumerPowerProfileModeled = null;
- for (BatteryConsumer consumer : powerProfileModeledUsageStats.getUidBatteryConsumers()) {
- if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
- requestedBatteryConsumerPowerProfileModeled = consumer;
- }
-
- totalModeledCpuPowerMah += consumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU);
- }
-
- if (requestedBatterySipper == null) {
+ if (requestedBatteryConsumer == null || requestedModeledBatteryConsumer == null) {
mBatteryConsumerInfo = null;
return;
}
- if (requestedBatteryConsumer == null) {
- for (BatteryConsumer consumer : batteryUsageStats.getSystemBatteryConsumers()) {
- if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
- requestedBatteryConsumer = consumer;
- break;
- }
- }
- }
-
mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(
- context.getPackageManager(), requestedBatterySipper);
- long totalScreenMeasuredChargeUC =
- batteryStats.getScreenOnMeasuredBatteryConsumptionUC();
- long uidScreenMeasuredChargeUC =
- requestedBatterySipper.uidObj.getScreenOnMeasuredBatteryConsumptionUC();
+ context.getPackageManager(), requestedBatteryConsumer);
- addEntry("Total power", EntryType.POWER,
- requestedBatterySipper.totalSmearedPowerMah, totalSmearedPowerMah);
- maybeAddMeasuredEnergyEntry(requestedBatterySipper.drainType, batteryStats);
+ double[] totalPowerByComponentMah = new double[BatteryConsumer.POWER_COMPONENT_COUNT];
+ double[] totalModeledPowerByComponentMah =
+ new double[BatteryConsumer.POWER_COMPONENT_COUNT];
+ long[] totalDurationByComponentMs = new long[BatteryConsumer.TIME_COMPONENT_COUNT];
+ final int customComponentCount =
+ requestedBatteryConsumer.getCustomPowerComponentCount();
+ double[] totalCustomPowerByComponentMah = new double[customComponentCount];
- addEntry("... excluding system", EntryType.POWER,
- requestedBatterySipper.totalSmearedPowerMah, totalPowerExcludeSystemMah);
- addEntry("Screen, smeared", EntryType.POWER,
- requestedBatterySipper.screenPowerMah, totalScreenPower);
- if (uidScreenMeasuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE
- && totalScreenMeasuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE) {
- final double measuredCharge = UC_2_MAH * uidScreenMeasuredChargeUC;
- final double totalMeasuredCharge = UC_2_MAH * totalScreenMeasuredChargeUC;
- addEntry("Screen, measured", EntryType.POWER,
- measuredCharge, totalMeasuredCharge);
- }
- addEntry("Other, smeared", EntryType.POWER,
- requestedBatterySipper.proportionalSmearMah, totalProportionalSmearMah);
- addEntry("Excluding smeared", EntryType.POWER,
- requestedBatterySipper.totalPowerMah, totalPowerMah);
- if (requestedBatteryConsumer != null) {
- addEntry("CPU", EntryType.POWER,
- requestedBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU),
- totalCpuPowerMah);
- if (requestedBatteryConsumerPowerProfileModeled != null) {
- addEntry("CPU (modeled)", EntryType.POWER,
- requestedBatteryConsumerPowerProfileModeled.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU),
- totalModeledCpuPowerMah);
- }
- } else {
- addEntry("CPU (sipper)", EntryType.POWER,
- requestedBatterySipper.cpuPowerMah, totalCpuPowerMah);
- }
- addEntry("System services", EntryType.POWER,
- requestedBatterySipper.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah);
- addEntry("Wake lock", EntryType.POWER,
- requestedBatterySipper.wakeLockPowerMah, totalWakeLockPowerMah);
- addEntry("Mobile radio", EntryType.POWER,
- requestedBatterySipper.mobileRadioPowerMah, totalMobileRadioPowerMah);
- addEntry("WiFi", EntryType.POWER,
- requestedBatterySipper.wifiPowerMah, totalWifiPowerMah);
- addEntry("Bluetooth", EntryType.POWER,
- requestedBatterySipper.bluetoothPowerMah, totalBluetoothPowerMah);
- addEntry("GPS", EntryType.POWER,
- requestedBatterySipper.gpsPowerMah, totalGpsPowerMah);
- addEntry("Camera", EntryType.POWER,
- requestedBatterySipper.cameraPowerMah, totalCameraPowerMah);
- addEntry("Flashlight", EntryType.POWER,
- requestedBatterySipper.flashlightPowerMah, totalFlashlightPowerMah);
- addEntry("Sensors", EntryType.POWER,
- requestedBatterySipper.sensorPowerMah, totalSensorPowerMah);
- addEntry("Audio", EntryType.POWER,
- requestedBatterySipper.audioPowerMah, totalAudioPowerMah);
- addEntry("Video", EntryType.POWER,
- requestedBatterySipper.videoPowerMah, totalVideoPowerMah);
+ computeTotalPower(batteryUsageStats, totalPowerByComponentMah);
+ computeTotalPower(modeledBatteryUsageStats, totalModeledPowerByComponentMah);
+ computeTotalPowerForCustomComponent(batteryUsageStats, totalCustomPowerByComponentMah);
+ computeTotalDuration(batteryUsageStats, totalDurationByComponentMs);
- addEntry("CPU time", EntryType.DURATION,
- requestedBatterySipper.cpuTimeMs, totalCpuTimeMs);
- addEntry("CPU foreground time", EntryType.DURATION,
- requestedBatterySipper.cpuFgTimeMs, totalCpuFgTimeMs);
- addEntry("Wake lock time", EntryType.DURATION,
- requestedBatterySipper.wakeLockTimeMs, totalWakeLockTimeMs);
- addEntry("WiFi running time", EntryType.DURATION,
- requestedBatterySipper.wifiRunningTimeMs, totalWifiRunningTimeMs);
- addEntry("Bluetooth time", EntryType.DURATION,
- requestedBatterySipper.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs);
- addEntry("GPS time", EntryType.DURATION,
- requestedBatterySipper.gpsTimeMs, totalGpsTimeMs);
- addEntry("Camera time", EntryType.DURATION,
- requestedBatterySipper.cameraTimeMs, totalCameraTimeMs);
- addEntry("Flashlight time", EntryType.DURATION,
- requestedBatterySipper.flashlightTimeMs, totalFlashlightTimeMs);
- addEntry("Audio time", EntryType.DURATION,
- requestedBatterySipper.audioTimeMs, totalAudioTimeMs);
- addEntry("Video time", EntryType.DURATION,
- requestedBatterySipper.videoTimeMs, totalVideoTimeMs);
- }
-
- private boolean isSystemSipper(BatterySipper sipper) {
- final int uid = sipper.uidObj == null ? -1 : sipper.getUid();
- if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) {
- return true;
- } else if (sipper.mPackages != null) {
- for (final String packageName : sipper.mPackages) {
- for (final String systemPackage : PACKAGES_SYSTEM) {
- if (systemPackage.equals(packageName)) {
- return true;
- }
- }
+ for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) {
+ final String metricTitle = getPowerMetricTitle(component);
+ final int powerModel = requestedBatteryConsumer.getPowerModel(component);
+ if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
+ addEntry(metricTitle, EntryType.POWER_MODELED,
+ requestedBatteryConsumer.getConsumedPower(component),
+ totalPowerByComponentMah[component],
+ mBatteryConsumerInfo.isSystemBatteryConsumer);
+ } else {
+ addEntry(metricTitle + " (measured)", EntryType.POWER_MEASURED,
+ requestedBatteryConsumer.getConsumedPower(component),
+ totalPowerByComponentMah[component],
+ mBatteryConsumerInfo.isSystemBatteryConsumer);
+ addEntry(metricTitle + " (modeled)", EntryType.POWER_MODELED,
+ requestedModeledBatteryConsumer.getConsumedPower(component),
+ totalModeledPowerByComponentMah[component],
+ mBatteryConsumerInfo.isSystemBatteryConsumer);
}
}
- return false;
+ for (int component = 0; component < customComponentCount; component++) {
+ final String name = requestedBatteryConsumer.getCustomPowerComponentName(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
+ addEntry(name + " (custom)", EntryType.POWER_CUSTOM,
+ requestedBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component),
+ totalCustomPowerByComponentMah[component],
+ mBatteryConsumerInfo.isSystemBatteryConsumer);
+ }
+
+ for (int component = 0; component < BatteryConsumer.TIME_COMPONENT_COUNT; component++) {
+ final String metricTitle = getTimeMetricTitle(component);
+ addEntry(metricTitle, EntryType.DURATION,
+ requestedBatteryConsumer.getUsageDurationMillis(component),
+ totalDurationByComponentMs[component],
+ mBatteryConsumerInfo.isSystemBatteryConsumer);
+ }
}
- private void addEntry(String title, EntryType entryType, double amount, double totalAmount) {
+ private BatteryConsumer getRequestedBatteryConsumer(BatteryUsageStats batteryUsageStats,
+ String batteryConsumerId) {
+ for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
+ if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
+ return consumer;
+ }
+ }
+ for (BatteryConsumer consumer : batteryUsageStats.getSystemBatteryConsumers()) {
+ if (batteryConsumerId(consumer).equals(batteryConsumerId)) {
+ return consumer;
+ }
+ }
+ return null;
+ }
+
+ static String getPowerMetricTitle(int componentId) {
+ final String componentName = DebugUtils.constantToString(BatteryConsumer.class,
+ "POWER_COMPONENT_", componentId);
+ return componentName.charAt(0) + componentName.substring(1).toLowerCase().replace('_', ' ')
+ + " power";
+ }
+
+ static String getTimeMetricTitle(int componentId) {
+ final String componentName = DebugUtils.constantToString(BatteryConsumer.class,
+ "TIME_COMPONENT_", componentId);
+ return componentName.charAt(0) + componentName.substring(1).toLowerCase().replace('_', ' ')
+ + " time";
+ }
+
+ private void computeTotalPower(BatteryUsageStats batteryUsageStats,
+ double[] powerByComponentMah) {
+ for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
+ for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT;
+ component++) {
+ powerByComponentMah[component] += consumer.getConsumedPower(component);
+ }
+ }
+ }
+
+ private void computeTotalDuration(BatteryUsageStats batteryUsageStats,
+ long[] durationByComponentMs) {
+ for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
+ for (int component = 0; component < BatteryConsumer.TIME_COMPONENT_COUNT;
+ component++) {
+ durationByComponentMs[component] += consumer.getUsageDurationMillis(component);
+ }
+ }
+ }
+
+ private void computeTotalPowerForCustomComponent(
+ BatteryUsageStats batteryUsageStats, double[] powerByComponentMah) {
+ for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
+ final int customComponentCount = consumer.getCustomPowerComponentCount();
+ for (int component = 0;
+ component < Math.min(customComponentCount, powerByComponentMah.length);
+ component++) {
+ powerByComponentMah[component] += consumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
+ }
+ }
+ }
+
+ private void addEntry(String title, EntryType entryType, double amount, double totalAmount,
+ boolean isSystemBatteryConsumer) {
Entry entry = new Entry();
entry.title = title;
entry.entryType = entryType;
entry.value = amount;
entry.total = totalAmount;
+ entry.isSystemBatteryConsumer = isSystemBatteryConsumer;
mEntries.add(entry);
}
- private void maybeAddMeasuredEnergyEntry(BatterySipper.DrainType drainType,
- BatteryStats batteryStats) {
- switch (drainType) {
- case AMBIENT_DISPLAY:
- final long totalDozeMeasuredChargeUC =
- batteryStats.getScreenDozeMeasuredBatteryConsumptionUC();
- if (totalDozeMeasuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE) {
- final double measuredCharge = UC_2_MAH * totalDozeMeasuredChargeUC;
- addEntry("Measured ambient display power", EntryType.POWER, measuredCharge,
- measuredCharge);
- }
- break;
- case SCREEN:
- final long totalScreenMeasuredChargeUC =
- batteryStats.getScreenOnMeasuredBatteryConsumptionUC();
- if (totalScreenMeasuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE) {
- final double measuredCharge = UC_2_MAH * totalScreenMeasuredChargeUC;
- addEntry("Measured screen power", EntryType.POWER, measuredCharge,
- measuredCharge);
- }
- break;
- }
- }
-
public BatteryConsumerInfoHelper.BatteryConsumerInfo getBatteryConsumerInfo() {
return mBatteryConsumerInfo;
}
@@ -314,13 +198,9 @@
return mEntries;
}
- public static String batteryConsumerId(BatterySipper sipper) {
- return sipper.drainType + "|" + sipper.userId + "|" + sipper.getUid();
- }
-
public static String batteryConsumerId(BatteryConsumer consumer) {
if (consumer instanceof UidBatteryConsumer) {
- return BatterySipper.DrainType.APP + "|"
+ return "APP|"
+ UserHandle.getUserId(((UidBatteryConsumer) consumer).getUid()) + "|"
+ ((UidBatteryConsumer) consumer).getUid();
} else if (consumer instanceof SystemBatteryConsumer) {
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java
index 8ee6c604..6288e0b 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java
@@ -18,14 +18,14 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.os.BatteryConsumer;
import android.os.Process;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
+import android.util.DebugUtils;
import androidx.annotation.NonNull;
-import com.android.internal.os.BatterySipper;
-
-import java.util.Locale;
-
class BatteryConsumerInfoHelper {
private static final String SYSTEM_SERVER_PACKAGE_NAME = "android";
@@ -37,111 +37,79 @@
public ApplicationInfo iconInfo;
public CharSequence packages;
public CharSequence details;
+ public boolean isSystemBatteryConsumer;
}
@NonNull
public static BatteryConsumerInfo makeBatteryConsumerInfo(PackageManager packageManager,
- @NonNull BatterySipper sipper) {
+ @NonNull BatteryConsumer batteryConsumer) {
BatteryConsumerInfo info = new BatteryConsumerInfo();
- info.id = BatteryConsumerData.batteryConsumerId(sipper);
- sipper.sumPower();
- info.powerMah = sipper.totalSmearedPowerMah;
- switch (sipper.drainType) {
- case APP: {
- int uid = sipper.getUid();
- info.details = String.format("UID: %d", uid);
- String packageWithHighestDrain = sipper.packageWithHighestDrain;
- if (uid == Process.ROOT_UID) {
- info.label = "<root>";
- } else {
- String[] packages = packageManager.getPackagesForUid(uid);
- String primaryPackageName = null;
- if (uid == Process.SYSTEM_UID) {
- primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME;
- } else if (packages != null) {
- for (String name : packages) {
- primaryPackageName = name;
- if (name.equals(packageWithHighestDrain)) {
- break;
- }
- }
- }
+ info.id = BatteryConsumerData.batteryConsumerId(batteryConsumer);
+ info.powerMah = batteryConsumer.getConsumedPower();
- if (primaryPackageName != null) {
- try {
- ApplicationInfo applicationInfo =
- packageManager.getApplicationInfo(primaryPackageName, 0);
- info.label = applicationInfo.loadLabel(packageManager);
- info.iconInfo = applicationInfo;
- } catch (PackageManager.NameNotFoundException e) {
- info.label = primaryPackageName;
+ if (batteryConsumer instanceof UidBatteryConsumer) {
+ final UidBatteryConsumer uidBatteryConsumer = (UidBatteryConsumer) batteryConsumer;
+ int uid = uidBatteryConsumer.getUid();
+ info.details = String.format("UID: %d", uid);
+ String packageWithHighestDrain = uidBatteryConsumer.getPackageWithHighestDrain();
+ if (uid == Process.ROOT_UID) {
+ info.label = "<root>";
+ } else {
+ String[] packages = packageManager.getPackagesForUid(uid);
+ String primaryPackageName = null;
+ if (uid == Process.SYSTEM_UID) {
+ primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME;
+ } else if (packages != null) {
+ for (String name : packages) {
+ primaryPackageName = name;
+ if (name.equals(packageWithHighestDrain)) {
+ break;
}
- } else if (packageWithHighestDrain != null) {
- info.label = packageWithHighestDrain;
- }
-
- if (packages != null && packages.length > 0) {
- StringBuilder sb = new StringBuilder();
- if (primaryPackageName != null) {
- sb.append(primaryPackageName);
- }
- for (String packageName : packages) {
- if (packageName.equals(primaryPackageName)) {
- continue;
- }
-
- if (sb.length() != 0) {
- sb.append(", ");
- }
- sb.append(packageName);
- }
-
- info.packages = sb;
}
}
- break;
+
+ if (primaryPackageName != null) {
+ try {
+ ApplicationInfo applicationInfo =
+ packageManager.getApplicationInfo(primaryPackageName, 0);
+ info.label = applicationInfo.loadLabel(packageManager);
+ info.iconInfo = applicationInfo;
+ } catch (PackageManager.NameNotFoundException e) {
+ info.label = primaryPackageName;
+ }
+ } else if (packageWithHighestDrain != null) {
+ info.label = packageWithHighestDrain;
+ }
+
+ if (packages != null && packages.length > 0) {
+ StringBuilder sb = new StringBuilder();
+ if (primaryPackageName != null) {
+ sb.append(primaryPackageName);
+ }
+ for (String packageName : packages) {
+ if (packageName.equals(primaryPackageName)) {
+ continue;
+ }
+
+ if (sb.length() != 0) {
+ sb.append(", ");
+ }
+ sb.append(packageName);
+ }
+
+ info.packages = sb;
+ }
}
- case USER:
- info.label = "User";
- info.details = String.format(Locale.getDefault(), "User ID: %d", sipper.userId);
- break;
- case AMBIENT_DISPLAY:
- info.label = "Ambient display";
- break;
- case BLUETOOTH:
- info.label = "Bluetooth";
- break;
- case CAMERA:
- info.label = "Camera";
- break;
- case CELL:
- info.label = "Cell";
- break;
- case FLASHLIGHT:
- info.label = "Flashlight";
- break;
- case IDLE:
- info.label = "Idle";
- break;
- case MEMORY:
- info.label = "Memory";
- break;
- case OVERCOUNTED:
- info.label = "Overcounted";
- break;
- case PHONE:
- info.label = "Phone";
- break;
- case SCREEN:
- info.label = "Screen";
- break;
- case UNACCOUNTED:
- info.label = "Unaccounted";
- break;
- case WIFI:
- info.label = "WiFi";
- break;
+ } else if (batteryConsumer instanceof SystemBatteryConsumer) {
+ final SystemBatteryConsumer systemBatteryConsumer =
+ (SystemBatteryConsumer) batteryConsumer;
+ final int drainType = systemBatteryConsumer.getDrainType();
+ String name = DebugUtils.constantToString(SystemBatteryConsumer.class, "DRAIN_TYPE_",
+ drainType);
+ info.label = name.charAt(0) + name.substring(1).toLowerCase().replace('_', ' ');
+ info.isSystemBatteryConsumer = true;
}
+
// Default the app icon to System Server. This includes root, dex2oat and other UIDs.
if (info.iconInfo == null) {
try {
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java
index bb11fd5..4922087 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java
@@ -18,10 +18,11 @@
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.BatteryStats;
+import android.os.BatteryStatsManager;
+import android.os.BatteryUsageStats;
import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.UserManager;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -37,8 +38,6 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.frameworks.core.batterystatsviewer.BatteryConsumerInfoHelper.BatteryConsumerInfo;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settingslib.utils.AsyncLoaderCompat;
import java.util.ArrayList;
@@ -99,44 +98,39 @@
private static class BatteryConsumerListLoader extends
AsyncLoaderCompat<List<BatteryConsumerInfo>> {
- private final BatteryStatsHelper mStatsHelper;
private final int mPickerType;
- private final UserManager mUserManager;
+ private final BatteryStatsManager mBatteryStatsManager;
private final PackageManager mPackageManager;
BatteryConsumerListLoader(Context context, int pickerType) {
super(context);
- mUserManager = context.getSystemService(UserManager.class);
- mStatsHelper = new BatteryStatsHelper(context, false /* collectBatteryBroadcast */);
+ mBatteryStatsManager = context.getSystemService(BatteryStatsManager.class);
mPickerType = pickerType;
- mStatsHelper.create((Bundle) null);
- mStatsHelper.clearStats();
mPackageManager = context.getPackageManager();
}
@Override
public List<BatteryConsumerInfo> loadInBackground() {
+ final BatteryUsageStats batteryUsageStats = mBatteryStatsManager.getBatteryUsageStats();
+
List<BatteryConsumerInfo> batteryConsumerList = new ArrayList<>();
-
- mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
-
- final List<BatterySipper> usageList = mStatsHelper.getUsageList();
- for (BatterySipper sipper : usageList) {
- switch (mPickerType) {
- case PICKER_TYPE_APP:
- if (sipper.drainType != BatterySipper.DrainType.APP) {
- continue;
- }
- break;
- case PICKER_TYPE_DRAIN:
- default:
- if (sipper.drainType == BatterySipper.DrainType.APP) {
- continue;
- }
- }
-
- batteryConsumerList.add(
- BatteryConsumerInfoHelper.makeBatteryConsumerInfo(mPackageManager, sipper));
+ switch (mPickerType) {
+ case PICKER_TYPE_APP:
+ for (UidBatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) {
+ batteryConsumerList.add(
+ BatteryConsumerInfoHelper.makeBatteryConsumerInfo(mPackageManager,
+ consumer));
+ }
+ break;
+ case PICKER_TYPE_DRAIN:
+ default:
+ for (SystemBatteryConsumer consumer :
+ batteryUsageStats.getSystemBatteryConsumers()) {
+ batteryConsumerList.add(
+ BatteryConsumerInfoHelper.makeBatteryConsumerInfo(mPackageManager,
+ consumer));
+ }
+ break;
}
batteryConsumerList.sort(
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
index 4ead8ee..74d3fb3 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
@@ -18,13 +18,10 @@
import android.content.Context;
import android.content.SharedPreferences;
-import android.os.BatteryStats;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.UserManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -41,7 +38,6 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settingslib.utils.AsyncLoaderCompat;
import java.util.Collections;
@@ -50,24 +46,24 @@
public class BatteryStatsViewerActivity extends ComponentActivity {
private static final int BATTERY_STATS_REFRESH_RATE_MILLIS = 60 * 1000;
- public static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId";
- public static final int LOADER_BATTERY_STATS_HELPER = 0;
- public static final int LOADER_BATTERY_USAGE_STATS = 1;
+ private static final int MILLIS_IN_MINUTE = 60000;
+ private static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId";
+ private static final int LOADER_BATTERY_USAGE_STATS = 1;
private BatteryStatsDataAdapter mBatteryStatsDataAdapter;
- private Runnable mBatteryStatsRefresh = this::periodicBatteryStatsRefresh;
+ private final Runnable mBatteryStatsRefresh = this::periodicBatteryStatsRefresh;
private SharedPreferences mSharedPref;
private String mBatteryConsumerId;
private TextView mTitleView;
private TextView mDetailsView;
private ImageView mIconView;
private TextView mPackagesView;
+ private View mHeadingsView;
private RecyclerView mBatteryConsumerDataView;
private View mLoadingView;
private View mEmptyView;
- private ActivityResultLauncher<Void> mStartAppPicker = registerForActivityResult(
+ private final ActivityResultLauncher<Void> mStartAppPicker = registerForActivityResult(
BatteryConsumerPickerActivity.CONTRACT, this::onApplicationSelected);
- private BatteryStatsHelper mBatteryStatsHelper;
private List<BatteryUsageStats> mBatteryUsageStats;
@Override
@@ -85,6 +81,7 @@
mDetailsView = findViewById(R.id.details);
mIconView = findViewById(android.R.id.icon);
mPackagesView = findViewById(R.id.packages);
+ mHeadingsView = findViewById(R.id.headings);
mBatteryConsumerDataView = findViewById(R.id.battery_consumer_data_view);
mBatteryConsumerDataView.setLayoutManager(new LinearLayoutManager(this));
@@ -139,55 +136,10 @@
private void loadBatteryStats() {
LoaderManager loaderManager = LoaderManager.getInstance(this);
- loaderManager.restartLoader(LOADER_BATTERY_STATS_HELPER, null,
- new BatteryStatsHelperLoaderCallbacks());
loaderManager.restartLoader(LOADER_BATTERY_USAGE_STATS, null,
new BatteryUsageStatsLoaderCallbacks());
}
- private static class BatteryStatsHelperLoader extends AsyncLoaderCompat<BatteryStatsHelper> {
- private final BatteryStatsHelper mBatteryStatsHelper;
- private final UserManager mUserManager;
-
- BatteryStatsHelperLoader(Context context) {
- super(context);
- mUserManager = context.getSystemService(UserManager.class);
- mBatteryStatsHelper = new BatteryStatsHelper(context,
- false /* collectBatteryBroadcast */);
- mBatteryStatsHelper.create((Bundle) null);
- mBatteryStatsHelper.clearStats();
- }
-
- @Override
- public BatteryStatsHelper loadInBackground() {
- mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
- UserHandle.myUserId());
- return mBatteryStatsHelper;
- }
-
- @Override
- protected void onDiscardResult(BatteryStatsHelper result) {
- }
- }
-
- private class BatteryStatsHelperLoaderCallbacks implements LoaderCallbacks<BatteryStatsHelper> {
- @NonNull
- @Override
- public Loader<BatteryStatsHelper> onCreateLoader(int id, Bundle args) {
- return new BatteryStatsHelperLoader(BatteryStatsViewerActivity.this);
- }
-
- @Override
- public void onLoadFinished(@NonNull Loader<BatteryStatsHelper> loader,
- BatteryStatsHelper batteryStatsHelper) {
- onBatteryStatsHelperLoaded(batteryStatsHelper);
- }
-
- @Override
- public void onLoaderReset(@NonNull Loader<BatteryStatsHelper> loader) {
- }
- }
-
private static class BatteryUsageStatsLoader extends
AsyncLoaderCompat<List<BatteryUsageStats>> {
private final BatteryStatsManager mBatteryStatsManager;
@@ -200,10 +152,13 @@
@Override
public List<BatteryUsageStats> loadInBackground() {
final BatteryUsageStatsQuery queryDefault =
- new BatteryUsageStatsQuery.Builder().build();
+ new BatteryUsageStatsQuery.Builder()
+ .includePowerModels()
+ .build();
final BatteryUsageStatsQuery queryPowerProfileModeledOnly =
new BatteryUsageStatsQuery.Builder()
.powerProfileModeledOnly()
+ .includePowerModels()
.build();
return mBatteryStatsManager.getBatteryUsageStats(
List.of(queryDefault, queryPowerProfileModeledOnly));
@@ -233,22 +188,13 @@
}
}
- public void onBatteryStatsHelperLoaded(BatteryStatsHelper batteryStatsHelper) {
- mBatteryStatsHelper = batteryStatsHelper;
- onBatteryStatsDataLoaded();
- }
-
private void onBatteryUsageStatsLoaded(List<BatteryUsageStats> batteryUsageStats) {
mBatteryUsageStats = batteryUsageStats;
onBatteryStatsDataLoaded();
}
public void onBatteryStatsDataLoaded() {
- if (mBatteryStatsHelper == null || mBatteryUsageStats == null) {
- return;
- }
-
- BatteryConsumerData batteryConsumerData = new BatteryConsumerData(this, mBatteryStatsHelper,
+ BatteryConsumerData batteryConsumerData = new BatteryConsumerData(this,
mBatteryUsageStats, mBatteryConsumerId);
BatteryConsumerInfoHelper.BatteryConsumerInfo
@@ -256,6 +202,7 @@
if (batteryConsumerInfo == null) {
mTitleView.setText("Battery consumer not found");
mPackagesView.setVisibility(View.GONE);
+ mHeadingsView.setVisibility(View.GONE);
} else {
mTitleView.setText(batteryConsumerInfo.label);
if (batteryConsumerInfo.details != null) {
@@ -273,6 +220,12 @@
} else {
mPackagesView.setVisibility(View.GONE);
}
+
+ if (batteryConsumerInfo.isSystemBatteryConsumer) {
+ mHeadingsView.setVisibility(View.VISIBLE);
+ } else {
+ mHeadingsView.setVisibility(View.GONE);
+ }
}
mBatteryStatsDataAdapter.setEntries(batteryConsumerData.getEntries());
@@ -290,6 +243,7 @@
private static class BatteryStatsDataAdapter extends
RecyclerView.Adapter<BatteryStatsDataAdapter.ViewHolder> {
public static class ViewHolder extends RecyclerView.ViewHolder {
+ public ImageView iconImageView;
public TextView titleTextView;
public TextView amountTextView;
public TextView percentTextView;
@@ -297,6 +251,7 @@
ViewHolder(View itemView) {
super(itemView);
+ iconImageView = itemView.findViewById(R.id.icon);
titleTextView = itemView.findViewById(R.id.title);
amountTextView = itemView.findViewById(R.id.amount);
percentTextView = itemView.findViewById(R.id.percent);
@@ -328,21 +283,56 @@
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
BatteryConsumerData.Entry entry = mEntries.get(position);
switch (entry.entryType) {
- case POWER:
+ case POWER_MODELED:
viewHolder.titleTextView.setText(entry.title);
viewHolder.amountTextView.setText(
String.format(Locale.getDefault(), "%.1f mAh", entry.value));
+ viewHolder.iconImageView.setImageResource(R.drawable.gm_calculate_24);
+ viewHolder.itemView.setBackgroundResource(
+ R.color.battery_consumer_bg_power_profile);
+ break;
+ case POWER_MEASURED:
+ viewHolder.titleTextView.setText(entry.title);
+ viewHolder.amountTextView.setText(
+ String.format(Locale.getDefault(), "%.1f mAh", entry.value));
+ viewHolder.iconImageView.setImageResource(R.drawable.gm_amp_24);
+ viewHolder.itemView.setBackgroundResource(
+ R.color.battery_consumer_bg_measured_energy);
+ break;
+ case POWER_CUSTOM:
+ viewHolder.titleTextView.setText(entry.title);
+ viewHolder.amountTextView.setText(
+ String.format(Locale.getDefault(), "%.1f mAh", entry.value));
+ viewHolder.iconImageView.setImageResource(R.drawable.gm_custom_24);
+ viewHolder.itemView.setBackgroundResource(
+ R.color.battery_consumer_bg_measured_energy);
break;
case DURATION:
viewHolder.titleTextView.setText(entry.title);
- viewHolder.amountTextView.setText(
- String.format(Locale.getDefault(), "%,d ms", (long) entry.value));
+ final long durationMs = (long) entry.value;
+ CharSequence text;
+ if (durationMs < MILLIS_IN_MINUTE) {
+ text = String.format(Locale.getDefault(), "%,d ms", durationMs);
+ } else {
+ text = String.format(Locale.getDefault(), "%,d m %d s",
+ durationMs / MILLIS_IN_MINUTE,
+ (durationMs % MILLIS_IN_MINUTE) / 1000);
+ }
+
+ viewHolder.amountTextView.setText(text);
+ viewHolder.iconImageView.setImageResource(R.drawable.gm_timer_24);
+ viewHolder.itemView.setBackground(null);
break;
}
- double proportion = entry.total != 0 ? entry.value * 100 / entry.total : 0;
- viewHolder.percentTextView.setText(String.format(Locale.getDefault(), "%.1f%%",
- proportion));
+ double proportion;
+ if (entry.isSystemBatteryConsumer) {
+ proportion = entry.value != 0 ? entry.total * 100 / entry.value : 0;
+ } else {
+ proportion = entry.total != 0 ? entry.value * 100 / entry.total : 0;
+ }
+ viewHolder.percentTextView.setText(
+ String.format(Locale.getDefault(), "%.1f%%", proportion));
}
}
}
diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
index fb0dd46..b2b9ab3 100644
--- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
+++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
@@ -93,8 +93,8 @@
@Test
public void testComputeForPassword_metrics() {
- final PasswordMetrics metrics =
- PasswordMetrics.computeForPassword("6B~0z1Z3*8A".getBytes());
+ final PasswordMetrics metrics = PasswordMetrics.computeForPasswordOrPin(
+ "6B~0z1Z3*8A".getBytes(), /* isPin */ false);
assertEquals(11, metrics.length);
assertEquals(4, metrics.letters);
assertEquals(3, metrics.upperCase);
@@ -133,61 +133,71 @@
@Test
public void testDetermineComplexity_lowNumeric() {
assertEquals(PASSWORD_COMPLEXITY_LOW,
- PasswordMetrics.computeForPassword("1234".getBytes()).determineComplexity());
+ PasswordMetrics.computeForPasswordOrPin("1234".getBytes(),
+ /* isPin */true).determineComplexity());
}
@Test
public void testDetermineComplexity_lowNumericComplex() {
assertEquals(PASSWORD_COMPLEXITY_LOW,
- PasswordMetrics.computeForPassword("124".getBytes()).determineComplexity());
+ PasswordMetrics.computeForPasswordOrPin("124".getBytes(),
+ /* isPin */ true).determineComplexity());
}
@Test
public void testDetermineComplexity_lowAlphabetic() {
assertEquals(PASSWORD_COMPLEXITY_LOW,
- PasswordMetrics.computeForPassword("a!".getBytes()).determineComplexity());
+ PasswordMetrics.computeForPasswordOrPin("a!".getBytes(),
+ /* isPin */ false).determineComplexity());
}
@Test
public void testDetermineComplexity_lowAlphanumeric() {
assertEquals(PASSWORD_COMPLEXITY_LOW,
- PasswordMetrics.computeForPassword("a!1".getBytes()).determineComplexity());
+ PasswordMetrics.computeForPasswordOrPin("a!1".getBytes(),
+ /* isPin */ false).determineComplexity());
}
@Test
public void testDetermineComplexity_mediumNumericComplex() {
assertEquals(PASSWORD_COMPLEXITY_MEDIUM,
- PasswordMetrics.computeForPassword("1238".getBytes()).determineComplexity());
+ PasswordMetrics.computeForPasswordOrPin("1238".getBytes(),
+ /* isPin */ true).determineComplexity());
}
@Test
public void testDetermineComplexity_mediumAlphabetic() {
assertEquals(PASSWORD_COMPLEXITY_MEDIUM,
- PasswordMetrics.computeForPassword("ab!c".getBytes()).determineComplexity());
+ PasswordMetrics.computeForPasswordOrPin("ab!c".getBytes(),
+ /* isPin */ false).determineComplexity());
}
@Test
public void testDetermineComplexity_mediumAlphanumeric() {
assertEquals(PASSWORD_COMPLEXITY_MEDIUM,
- PasswordMetrics.computeForPassword("ab!1".getBytes()).determineComplexity());
+ PasswordMetrics.computeForPasswordOrPin("ab!1".getBytes(),
+ /* isPin */ false).determineComplexity());
}
@Test
public void testDetermineComplexity_highNumericComplex() {
assertEquals(PASSWORD_COMPLEXITY_HIGH,
- PasswordMetrics.computeForPassword("12389647!".getBytes()).determineComplexity());
+ PasswordMetrics.computeForPasswordOrPin("12389647!".getBytes(),
+ /* isPin */ true).determineComplexity());
}
@Test
public void testDetermineComplexity_highAlphabetic() {
assertEquals(PASSWORD_COMPLEXITY_HIGH,
- PasswordMetrics.computeForPassword("alphabetic!".getBytes()).determineComplexity());
+ PasswordMetrics.computeForPasswordOrPin("alphabetic!".getBytes(),
+ /* isPin */ false).determineComplexity());
}
@Test
public void testDetermineComplexity_highAlphanumeric() {
- assertEquals(PASSWORD_COMPLEXITY_HIGH, PasswordMetrics.computeForPassword(
- "alphanumeric123!".getBytes()).determineComplexity());
+ assertEquals(PASSWORD_COMPLEXITY_HIGH,
+ PasswordMetrics.computeForPasswordOrPin("alphanumeric123!".getBytes(),
+ /* isPin */ false).determineComplexity());
}
@Test
diff --git a/core/tests/coretests/src/android/app/admin/PasswordPolicyTest.java b/core/tests/coretests/src/android/app/admin/PasswordPolicyTest.java
index e951054..f1be173 100644
--- a/core/tests/coretests/src/android/app/admin/PasswordPolicyTest.java
+++ b/core/tests/coretests/src/android/app/admin/PasswordPolicyTest.java
@@ -28,6 +28,7 @@
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static org.junit.Assert.assertEquals;
@@ -80,7 +81,7 @@
public void testGetMinMetrics_numeric() {
PasswordPolicy policy = testPolicy(PASSWORD_QUALITY_NUMERIC);
PasswordMetrics minMetrics = policy.getMinMetrics();
- assertEquals(CREDENTIAL_TYPE_PASSWORD, minMetrics.credType);
+ assertEquals(CREDENTIAL_TYPE_PIN, minMetrics.credType);
assertEquals(TEST_VALUE, minMetrics.length);
assertEquals(0, minMetrics.numeric); // numeric can doesn't really require digits.
assertEquals(0, minMetrics.letters);
@@ -104,7 +105,7 @@
public void testGetMinMetrics_numericComplex() {
PasswordPolicy policy = testPolicy(PASSWORD_QUALITY_NUMERIC_COMPLEX);
PasswordMetrics minMetrics = policy.getMinMetrics();
- assertEquals(CREDENTIAL_TYPE_PASSWORD, minMetrics.credType);
+ assertEquals(CREDENTIAL_TYPE_PIN, minMetrics.credType);
assertEquals(TEST_VALUE, minMetrics.length);
assertEquals(0, minMetrics.numeric);
assertEquals(0, minMetrics.letters);
diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
index 56c685a..3d18337 100644
--- a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java
@@ -20,6 +20,7 @@
import static org.testng.Assert.expectThrows;
+import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
@@ -89,8 +90,12 @@
});
// Verify the NullPointException has been thrown.
- ExecutionException executionException = expectThrows(ExecutionException.class,
- putDocumentsFuture::get);
- assertThat(executionException.getCause()).isInstanceOf(NullPointerException.class);
+ ExecutionException executionException =
+ expectThrows(ExecutionException.class, putDocumentsFuture::get);
+ assertThat(executionException.getCause()).isInstanceOf(AppSearchException.class);
+ AppSearchException appSearchException = (AppSearchException) executionException.getCause();
+ assertThat(appSearchException.getResultCode())
+ .isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR);
+ assertThat(appSearchException.getMessage()).startsWith("NullPointerException");
}
}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java
new file mode 100644
index 0000000..de0670b
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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 org.junit.Test;
+
+public class AppSearchResultTest {
+ @Test
+ public void testMapNullPointerException() {
+ NullPointerException e =
+ expectThrows(
+ NullPointerException.class,
+ () -> {
+ Object o = null;
+ o.toString();
+ });
+ AppSearchResult<?> result = AppSearchResult.throwableToFailedResult(e);
+ assertThat(result.getResultCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR);
+ // Makes sure the exception name is included in the string. Some exceptions have terse or
+ // missing strings so it's confusing to read the output without the exception name.
+ assertThat(result.getErrorMessage()).startsWith("NullPointerException");
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 4dbdc60..281ce2b 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -693,7 +693,7 @@
@Override
public void updateUiTranslationState(IBinder activityToken, int state,
- TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds) {
+ TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds) {
}
}
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java b/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java
index dd93997..0082728 100644
--- a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java
+++ b/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java
@@ -20,6 +20,8 @@
import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
@@ -157,4 +159,60 @@
assertNull(capabilities.tryApplyConfigChanges(oldConfiguration, configChange));
}
+
+ @Test
+ public void copyBuilder_copiesAllFields() {
+ TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
+ .setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
+ .setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
+ .setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
+ .build();
+
+ {
+ TimeZoneCapabilities updatedCapabilities =
+ new TimeZoneCapabilities.Builder(capabilities)
+ .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
+ .build();
+ TimeZoneCapabilities expectedCapabilities =
+ new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
+ .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
+ .setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
+ .setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
+ .build();
+
+ assertThat(updatedCapabilities).isEqualTo(expectedCapabilities);
+ }
+
+ {
+ TimeZoneCapabilities updatedCapabilities =
+ new TimeZoneCapabilities.Builder(capabilities)
+ .setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED)
+ .build();
+
+ TimeZoneCapabilities expectedCapabilities =
+ new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
+ .setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
+ .setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED)
+ .setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
+ .build();
+
+ assertThat(updatedCapabilities).isEqualTo(expectedCapabilities);
+ }
+
+ {
+ TimeZoneCapabilities updatedCapabilities =
+ new TimeZoneCapabilities.Builder(capabilities)
+ .setSuggestManualTimeZoneCapability(CAPABILITY_POSSESSED)
+ .build();
+
+ TimeZoneCapabilities expectedCapabilities =
+ new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
+ .setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
+ .setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
+ .setSuggestManualTimeZoneCapability(CAPABILITY_POSSESSED)
+ .build();
+
+ assertThat(updatedCapabilities).isEqualTo(expectedCapabilities);
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/graphics/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java
index bef1a4e..b2df98d 100644
--- a/core/tests/coretests/src/android/graphics/FontListParserTest.java
+++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java
@@ -59,7 +59,7 @@
+ "</family>";
FontConfig.FontFamily expected = new FontConfig.FontFamily(
Arrays.asList(
- new FontConfig.Font(new File("test.ttf"), null,
+ new FontConfig.Font(new File("test.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "", null)),
"sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
@@ -77,10 +77,10 @@
+ "</family>";
FontConfig.FontFamily expected = new FontConfig.FontFamily(
Arrays.asList(
- new FontConfig.Font(new File("test.ttf"), null,
+ new FontConfig.Font(new File("test.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "", null),
- new FontConfig.Font(new File("test.ttf"), null,
+ new FontConfig.Font(new File("test.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "", "serif")),
null, LocaleList.forLanguageTags("en"), VARIANT_DEFAULT);
@@ -97,7 +97,7 @@
+ "</family>";
FontConfig.FontFamily expected = new FontConfig.FontFamily(
Arrays.asList(
- new FontConfig.Font(new File("test.ttf"), null,
+ new FontConfig.Font(new File("test.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "", null)),
null, LocaleList.forLanguageTags("en"), VARIANT_COMPACT);
@@ -114,7 +114,7 @@
+ "</family>";
FontConfig.FontFamily expected = new FontConfig.FontFamily(
Arrays.asList(
- new FontConfig.Font(new File("test.ttf"), null,
+ new FontConfig.Font(new File("test.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "", null)),
null, LocaleList.forLanguageTags("en"), VARIANT_ELEGANT);
@@ -133,13 +133,13 @@
+ "</family>";
FontConfig.FontFamily expected = new FontConfig.FontFamily(
Arrays.asList(
- new FontConfig.Font(new File("normal.ttf"), null,
+ new FontConfig.Font(new File("normal.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "", null),
- new FontConfig.Font(new File("weight.ttf"), null,
+ new FontConfig.Font(new File("weight.ttf"), null, "test",
new FontStyle(100, FONT_SLANT_UPRIGHT),
0, "", null),
- new FontConfig.Font(new File("italic.ttf"), null,
+ new FontConfig.Font(new File("italic.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_ITALIC),
0, "", null)),
"sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
@@ -162,10 +162,10 @@
+ "</family>";
FontConfig.FontFamily expected = new FontConfig.FontFamily(
Arrays.asList(
- new FontConfig.Font(new File("test-VF.ttf"), null,
+ new FontConfig.Font(new File("test-VF.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "'wdth' 100.0,'wght' 200.0", null),
- new FontConfig.Font(new File("test-VF.ttf"), null,
+ new FontConfig.Font(new File("test-VF.ttf"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "'wdth' 400.0,'wght' 700.0", null)),
"sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
@@ -182,10 +182,30 @@
+ "</family>";
FontConfig.FontFamily expected = new FontConfig.FontFamily(
Arrays.asList(
- new FontConfig.Font(new File("test.ttc"), null,
+ new FontConfig.Font(new File("test.ttc"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
0, "", null),
- new FontConfig.Font(new File("test.ttc"), null,
+ new FontConfig.Font(new File("test.ttc"), null, "test",
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 1, "", null)),
+ "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+ }
+
+ @Test
+ public void psName() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family name='sans-serif'>"
+ + " <font index='0' postScriptName='foo'>test.ttc</font>"
+ + " <font index='1'>test.ttc</font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("test.ttc"), null, "foo",
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", null),
+ new FontConfig.Font(new File("test.ttc"), null, "test",
new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
1, "", null)),
"sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java
index d12f495..6defe91 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java
@@ -27,6 +27,7 @@
import android.graphics.fonts.SystemFonts;
import android.os.SharedMemory;
import android.text.FontConfig;
+import android.util.ArrayMap;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
@@ -200,8 +201,9 @@
Map<String, Typeface> systemFontMap = SystemFonts.buildSystemTypefaces(fontConfig,
fallbackMap);
SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap);
- Map<String, Typeface> copiedFontMap =
- Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN));
+ Map<String, Typeface> copiedFontMap = new ArrayMap<>();
+ Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN),
+ copiedFontMap);
assertEquals(systemFontMap.size(), copiedFontMap.size());
for (String key : systemFontMap.keySet()) {
assertTrue(copiedFontMap.containsKey(key));
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
index 22c71b52..e7b88c8 100644
--- a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
+++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
@@ -32,6 +32,7 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.ICancellationSignal;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -189,4 +190,15 @@
verifyNoMoreInteractions(mRemote);
}
+ @Test
+ public void testClose_whileActive() throws RemoteException {
+ mConnection.startCapture(mSurface, mRemote);
+
+ mCallback.completeStartRequest();
+ assertTrue(mConnection.isActive());
+
+ mConnection.close();
+ mCallback.completeEndRequest();
+ assertFalse(mConnection.isActive());
+ }
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index c393d68..8225afc 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -244,7 +244,8 @@
PendingIntent.getActivity(
context, 0, new Intent("action1"), FLAG_IMMUTABLE)))
.addAction(new RemoteAction(icon1, "title2", "desc2",
- PendingIntent.getActivity(context, 0, new Intent("action2"), 0)))
+ PendingIntent.getActivity(context, 0, new Intent("action2"),
+ FLAG_IMMUTABLE)))
.setEntityType(TextClassifier.TYPE_EMAIL, 0.5f)
.setEntityType(TextClassifier.TYPE_PHONE, 0.4f)
.build();
diff --git a/core/tests/coretests/src/android/window/WindowContextTest.java b/core/tests/coretests/src/android/window/WindowContextTest.java
index 614e7c1..83280f1 100644
--- a/core/tests/coretests/src/android/window/WindowContextTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextTest.java
@@ -17,6 +17,7 @@
package android.window;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -209,6 +210,38 @@
mWms.removeWindowToken(existingToken, DEFAULT_DISPLAY);
}
+ @Test
+ public void testWindowContextAddViewWithSubWindowType_NotCrash() throws Throwable {
+ final WindowContext windowContext = createWindowContext(TYPE_INPUT_METHOD);
+ final WindowManager wm = windowContext.getSystemService(WindowManager.class);
+
+ // Create a WindowToken with system window type.
+ final IBinder existingToken = new Binder();
+ mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId(),
+ null /* options */);
+
+ final WindowManager.LayoutParams params =
+ new WindowManager.LayoutParams(TYPE_INPUT_METHOD);
+ params.token = existingToken;
+ final View parentWindow = new View(windowContext);
+
+ final AttachStateListener listener = new AttachStateListener();
+ parentWindow.addOnAttachStateChangeListener(listener);
+
+ // Add the parent window
+ mInstrumentation.runOnMainSync(() -> wm.addView(parentWindow, params));
+
+ assertTrue(listener.mLatch.await(4, TimeUnit.SECONDS));
+
+ final WindowManager.LayoutParams subWindowAttrs =
+ new WindowManager.LayoutParams(TYPE_APPLICATION_ATTACHED_DIALOG);
+ subWindowAttrs.token = parentWindow.getWindowToken();
+ final View subWindow = new View(windowContext);
+
+ // Add a window with sub-window type.
+ mInstrumentation.runOnMainSync(() -> wm.addView(subWindow, subWindowAttrs));
+ }
+
private WindowContext createWindowContext() {
return createWindowContext(TYPE_APPLICATION_OVERLAY);
}
@@ -219,4 +252,16 @@
.getDisplay(DEFAULT_DISPLAY);
return (WindowContext) instContext.createWindowContext(display, type, null /* options */);
}
+
+ private static class AttachStateListener implements View.OnAttachStateChangeListener {
+ final CountDownLatch mLatch = new CountDownLatch(1);
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ mLatch.countDown();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {}
+ }
}
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 3a6f7b8..e0739be 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -167,7 +167,7 @@
}
BatteryUsageStats apply(BatteryUsageStatsQuery query, PowerCalculator... calculators) {
- final String[] customPowerComponentNames = mBatteryStats.getCustomPowerComponentNames();
+ final String[] customPowerComponentNames = mBatteryStats.getCustomEnergyConsumerNames();
final boolean includePowerModels = (query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
diff --git a/core/tests/coretests/src/com/android/internal/view/RecyclerViewCaptureHelperTest.java b/core/tests/coretests/src/com/android/internal/view/RecyclerViewCaptureHelperTest.java
index 88bbcc2..a6b26be 100644
--- a/core/tests/coretests/src/com/android/internal/view/RecyclerViewCaptureHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/view/RecyclerViewCaptureHelperTest.java
@@ -145,7 +145,9 @@
ScrollResult scrollResult = rvc.onScrollRequested(mTarget, scrollBounds, request);
assertThat(request).isEqualTo(scrollResult.requestedArea);
assertThat(request).isEqualTo(scrollResult.availableArea);
- assertThat(scrollResult.scrollDelta).isEqualTo(CAPTURE_HEIGHT);
+ // Capture height centered in the window
+ assertThat(scrollResult.scrollDelta).isEqualTo(
+ CAPTURE_HEIGHT + (WINDOW_HEIGHT - CAPTURE_HEIGHT) / 2);
assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
}
@@ -163,7 +165,8 @@
ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
assertThat(request).isEqualTo(scrollResult.requestedArea);
assertThat(request).isEqualTo(scrollResult.availableArea);
- assertThat(scrollResult.scrollDelta).isEqualTo(-CAPTURE_HEIGHT);
+ assertThat(scrollResult.scrollDelta).isEqualTo(
+ -CAPTURE_HEIGHT - (WINDOW_HEIGHT - CAPTURE_HEIGHT) / 2);
assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
}
@@ -182,7 +185,8 @@
ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
assertThat(request).isEqualTo(scrollResult.requestedArea);
assertThat(request).isEqualTo(scrollResult.availableArea);
- assertThat(scrollResult.scrollDelta).isEqualTo(CAPTURE_HEIGHT);
+ assertThat(scrollResult.scrollDelta).isEqualTo(
+ CAPTURE_HEIGHT + (WINDOW_HEIGHT - CAPTURE_HEIGHT) / 2);
assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
}
@@ -200,7 +204,8 @@
ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
assertThat(request).isEqualTo(scrollResult.requestedArea);
assertThat(request).isEqualTo(scrollResult.availableArea);
- assertThat(scrollResult.scrollDelta).isEqualTo(-CAPTURE_HEIGHT);
+ assertThat(scrollResult.scrollDelta).isEqualTo(
+ -CAPTURE_HEIGHT - (WINDOW_HEIGHT - CAPTURE_HEIGHT) / 2);
assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
}
diff --git a/core/tests/nfctests/Android.bp b/core/tests/nfctests/Android.bp
new file mode 100644
index 0000000..335cea1
--- /dev/null
+++ b/core/tests/nfctests/Android.bp
@@ -0,0 +1,38 @@
+// 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "NfcManagerTests",
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ ],
+ libs: [
+ "android.test.runner",
+ ],
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/nfctests/AndroidManifest.xml b/core/tests/nfctests/AndroidManifest.xml
new file mode 100644
index 0000000..99e2c34c
--- /dev/null
+++ b/core/tests/nfctests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.nfc">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!-- This is a self-instrumenting test package. -->
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.nfc"
+ android:label="NFC Manager Tests">
+ </instrumentation>
+
+</manifest>
+
diff --git a/core/tests/nfctests/AndroidTest.xml b/core/tests/nfctests/AndroidTest.xml
new file mode 100644
index 0000000..490d6f5
--- /dev/null
+++ b/core/tests/nfctests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for NFC Manager test cases">
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-suite-tag" value="apct-instrumentation"/>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="NfcManagerTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-tag" value="NfcManagerTests"/>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.nfc" />
+ <option name="hidden-api-checks" value="false"/>
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+ </test>
+</configuration>
diff --git a/core/tests/nfctests/OWNERS b/core/tests/nfctests/OWNERS
new file mode 100644
index 0000000..34b095c
--- /dev/null
+++ b/core/tests/nfctests/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/nfc/OWNERS
diff --git a/core/tests/nfctests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java b/core/tests/nfctests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java
new file mode 100644
index 0000000..43f9b6f
--- /dev/null
+++ b/core/tests/nfctests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.nfc;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.nfc.NfcAdapter.ControllerAlwaysOnListener;
+import android.os.RemoteException;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Test of {@link NfcControllerAlwaysOnListener}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NfcControllerAlwaysOnListenerTest {
+
+ private INfcAdapter mNfcAdapter = mock(INfcAdapter.class);
+
+ private Throwable mThrowRemoteException = new RemoteException("RemoteException");
+
+ private static Executor getExecutor() {
+ return new Executor() {
+ @Override
+ public void execute(Runnable command) {
+ command.run();
+ }
+ };
+ }
+
+ private static void verifyListenerInvoked(ControllerAlwaysOnListener listener) {
+ verify(listener, times(1)).onControllerAlwaysOnChanged(anyBoolean());
+ }
+
+ @Test
+ public void testRegister_RegisterUnregister() throws RemoteException {
+ NfcControllerAlwaysOnListener mListener =
+ new NfcControllerAlwaysOnListener(mNfcAdapter);
+ ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class);
+ ControllerAlwaysOnListener mockListener2 = mock(ControllerAlwaysOnListener.class);
+
+ // Verify that the state listener registered with the NFC Adapter
+ mListener.register(getExecutor(), mockListener1);
+ verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any());
+
+ // Register a second client and no new call to NFC Adapter
+ mListener.register(getExecutor(), mockListener2);
+ verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any());
+
+ // Unregister first listener
+ mListener.unregister(mockListener1);
+ verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any());
+ verify(mNfcAdapter, times(0)).unregisterControllerAlwaysOnListener(any());
+
+ // Unregister second listener and the state listener registered with the NFC Adapter
+ mListener.unregister(mockListener2);
+ verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any());
+ verify(mNfcAdapter, times(1)).unregisterControllerAlwaysOnListener(any());
+ }
+
+ @Test
+ public void testRegister_FirstRegisterFails() throws RemoteException {
+ NfcControllerAlwaysOnListener mListener =
+ new NfcControllerAlwaysOnListener(mNfcAdapter);
+ ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class);
+ ControllerAlwaysOnListener mockListener2 = mock(ControllerAlwaysOnListener.class);
+
+ // Throw a remote exception whenever first registering
+ doThrow(mThrowRemoteException).when(mNfcAdapter).registerControllerAlwaysOnListener(
+ any());
+
+ mListener.register(getExecutor(), mockListener1);
+ verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any());
+
+ // No longer throw an exception, instead succeed
+ doNothing().when(mNfcAdapter).registerControllerAlwaysOnListener(any());
+
+ // Register a different listener
+ mListener.register(getExecutor(), mockListener2);
+ verify(mNfcAdapter, times(2)).registerControllerAlwaysOnListener(any());
+
+ // Ensure first and second listener were invoked
+ mListener.onControllerAlwaysOnChanged(true);
+ verifyListenerInvoked(mockListener1);
+ verifyListenerInvoked(mockListener2);
+ }
+
+ @Test
+ public void testRegister_RegisterSameListenerTwice() throws RemoteException {
+ NfcControllerAlwaysOnListener mListener =
+ new NfcControllerAlwaysOnListener(mNfcAdapter);
+ ControllerAlwaysOnListener mockListener = mock(ControllerAlwaysOnListener.class);
+
+ // Register the same listener Twice
+ mListener.register(getExecutor(), mockListener);
+ mListener.register(getExecutor(), mockListener);
+ verify(mNfcAdapter, times(1)).registerControllerAlwaysOnListener(any());
+
+ // Invoke a state change and ensure the listener is only called once
+ mListener.onControllerAlwaysOnChanged(true);
+ verifyListenerInvoked(mockListener);
+ }
+
+ @Test
+ public void testNotify_AllListenersNotified() throws RemoteException {
+
+ NfcControllerAlwaysOnListener listener = new NfcControllerAlwaysOnListener(mNfcAdapter);
+ List<ControllerAlwaysOnListener> mockListeners = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ ControllerAlwaysOnListener mockListener = mock(ControllerAlwaysOnListener.class);
+ listener.register(getExecutor(), mockListener);
+ mockListeners.add(mockListener);
+ }
+
+ // Invoke a state change and ensure all listeners are invoked
+ listener.onControllerAlwaysOnChanged(true);
+ for (ControllerAlwaysOnListener mListener : mockListeners) {
+ verifyListenerInvoked(mListener);
+ }
+ }
+
+ @Test
+ public void testStateChange_CorrectValue() {
+ runStateChangeValue(true, true);
+ runStateChangeValue(false, false);
+
+ }
+
+ private void runStateChangeValue(boolean isEnabledIn, boolean isEnabledOut) {
+ NfcControllerAlwaysOnListener listener = new NfcControllerAlwaysOnListener(mNfcAdapter);
+ ControllerAlwaysOnListener mockListener = mock(ControllerAlwaysOnListener.class);
+ listener.register(getExecutor(), mockListener);
+ listener.onControllerAlwaysOnChanged(isEnabledIn);
+ verify(mockListener, times(1)).onControllerAlwaysOnChanged(isEnabledOut);
+ verify(mockListener, times(0)).onControllerAlwaysOnChanged(!isEnabledOut);
+ }
+}
diff --git a/data/etc/car/com.android.car.shell.xml b/data/etc/car/com.android.car.shell.xml
index ec903ad..578c46e 100644
--- a/data/etc/car/com.android.car.shell.xml
+++ b/data/etc/car/com.android.car.shell.xml
@@ -28,5 +28,9 @@
<permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
<permission name="android.car.permission.CAR_TIRES"/>
<permission name="android.car.permission.READ_CAR_STEERING"/>
+ <permission name="android.car.permission.REQUEST_CAR_EVS_ACTIVITY" />
+ <permission name="android.car.permission.CONTROL_CAR_EVS_ACTIVITY" />
+ <permission name="android.car.permission.USE_CAR_EVS_CAMERA" />
+ <permission name="android.car.permission.MONITOR_CAR_EVS_STATUS" />
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index e6196c2..40dda65 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -87,5 +87,9 @@
<permission name="android.car.permission.STORAGE_MONITORING"/>
<permission name="android.car.permission.VMS_PUBLISHER"/>
<permission name="android.car.permission.VMS_SUBSCRIBER"/>
+ <permission name="android.car.permission.REQUEST_CAR_EVS_ACTIVITY" />
+ <permission name="android.car.permission.CONTROL_CAR_EVS_ACTIVITY" />
+ <permission name="android.car.permission.USE_CAR_EVS_CAMERA" />
+ <permission name="android.car.permission.MONITOR_CAR_EVS_STATUS" />
</privapp-permissions>
</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index e85cc8d..5544eb4 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -406,6 +406,7 @@
<permission name="android.permission.TOGGLE_AUTOMOTIVE_PROJECTION" />
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.USE_RESERVED_DISK"/>
+ <permission name="android.permission.UWB_PRIVILEGED"/>
<permission name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"/>
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
<permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
@@ -416,6 +417,8 @@
<permission name="android.permission.SET_WALLPAPER" />
<permission name="android.permission.SET_WALLPAPER_COMPONENT" />
<permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
+ <!-- Permissions required for Incremental CTS tests -->
+ <permission name="com.android.permission.USE_INSTALLER_V2"/>
<permission name="android.permission.LOADER_USAGE_STATS"/>
<!-- Permission required to test system only camera devices. -->
<permission name="android.permission.SYSTEM_CAMERA" />
@@ -517,6 +520,8 @@
<permission name="android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER" />
<permission name="android.permission.SET_MEDIA_KEY_LISTENER" />
<permission name="android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER" />
+ <!-- Permission required for CTS test - CtsAlarmManagerTestCases -->
+ <permission name="android.permission.UPDATE_DEVICE_STATS" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 2db4c5d..1163536 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -223,7 +223,7 @@
<alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" />
<family name="serif">
- <font weight="400" style="normal">NotoSerif-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSerif">NotoSerif-Regular.ttf</font>
<font weight="700" style="normal">NotoSerif-Bold.ttf</font>
<font weight="400" style="italic">NotoSerif-Italic.ttf</font>
<font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font>
@@ -245,7 +245,7 @@
<alias name="monaco" to="monospace" />
<family name="serif-monospace">
- <font weight="400" style="normal">CutiveMono.ttf</font>
+ <font weight="400" style="normal" postScriptName="CutiveMono-Regular">CutiveMono.ttf</font>
</family>
<alias name="courier" to="serif-monospace" />
<alias name="courier new" to="serif-monospace" />
@@ -255,7 +255,8 @@
</family>
<family name="cursive">
- <font weight="400" style="normal">DancingScript-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="DancingScript">DancingScript-Regular.ttf
+ </font>
<font weight="700" style="normal">DancingScript-Bold.ttf</font>
</family>
@@ -271,148 +272,195 @@
<font weight="700" style="normal">SourceSansPro-Bold.ttf</font>
<font weight="700" style="italic">SourceSansPro-BoldItalic.ttf</font>
</family>
- <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600" />
+ <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600"/>
<!-- fallback fonts -->
<family lang="und-Arab" variant="elegant">
- <font weight="400" style="normal">NotoNaskhArabic-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoNaskhArabic">
+ NotoNaskhArabic-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
</family>
<family lang="und-Arab" variant="compact">
- <font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoNaskhArabicUI">
+ NotoNaskhArabicUI-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
</family>
<family lang="und-Ethi">
- <font weight="400" style="normal">NotoSansEthiopic-VF.ttf
- <axis tag="wght" stylevalue="400"/>
- </font>
- <font weight="500" style="normal">NotoSansEthiopic-VF.ttf
- <axis tag="wght" stylevalue="500"/>
- </font>
- <font weight="600" style="normal">NotoSansEthiopic-VF.ttf
- <axis tag="wght" stylevalue="600"/>
- </font>
- <font weight="700" style="normal">NotoSansEthiopic-VF.ttf
- <axis tag="wght" stylevalue="700"/>
- </font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
- <axis tag="wght" stylevalue="400"/>
- </font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
- <axis tag="wght" stylevalue="500"/>
- </font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
- <axis tag="wght" stylevalue="600"/>
- </font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
- <axis tag="wght" stylevalue="700"/>
- </font>
+ <font weight="400" style="normal" postScriptName="NotoSansEthiopic-Regular">
+ NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" postScriptName="NotoSansEthiopic-Regular">
+ NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" postScriptName="NotoSansEthiopic-Regular">
+ NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" postScriptName="NotoSansEthiopic-Regular">
+ NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="400"/>
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="500"/>
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="600"/>
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="700"/>
+ </font>
</family>
<family lang="und-Hebr">
- <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansHebrew">
+ NotoSansHebrew-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
<font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font>
</family>
<family lang="und-Thai" variant="elegant">
- <font weight="400" style="normal">NotoSansThai-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansThai">NotoSansThai-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifThai-Regular.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif" postScriptName="NotoSerifThai">
+ NotoSerifThai-Regular.ttf
+ </font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font>
</family>
<family lang="und-Thai" variant="compact">
- <font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansThaiUI">
+ NotoSansThaiUI-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
</family>
<family lang="und-Armn">
- <font weight="400" style="normal">NotoSansArmenian-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansArmenian-Regular">
+ NotoSansArmenian-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansArmenian-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansArmenian-Regular">
+ NotoSansArmenian-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansArmenian-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansArmenian-Regular">
+ NotoSansArmenian-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansArmenian-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansArmenian-Regular">
+ NotoSansArmenian-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Geor,und-Geok">
- <font weight="400" style="normal">NotoSansGeorgian-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansGeorgian-Regular">
+ NotoSansGeorgian-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansGeorgian-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansGeorgian-Regular">
+ NotoSansGeorgian-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansGeorgian-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansGeorgian-Regular">
+ NotoSansGeorgian-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansGeorgian-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansGeorgian-Regular">
+ NotoSansGeorgian-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Deva" variant="elegant">
- <font weight="400" style="normal">NotoSansDevanagari-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansDevanagari-Regular">
+ NotoSansDevanagari-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansDevanagari-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansDevanagari-Regular">
+ NotoSansDevanagari-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansDevanagari-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansDevanagari-Regular">
+ NotoSansDevanagari-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansDevanagari-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansDevanagari-Regular">
+ NotoSansDevanagari-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Deva" variant="compact">
- <font weight="400" style="normal">NotoSansDevanagariUI-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansDevanagariUI-Regular">
+ NotoSansDevanagariUI-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansDevanagariUI-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansDevanagariUI-Regular">
+ NotoSansDevanagariUI-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansDevanagariUI-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansDevanagariUI-Regular">
+ NotoSansDevanagariUI-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansDevanagariUI-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansDevanagariUI-Regular">
+ NotoSansDevanagariUI-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
@@ -421,347 +469,451 @@
danda characters.
-->
<family lang="und-Gujr" variant="elegant">
- <font weight="400" style="normal">NotoSansGujarati-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansGujarati">
+ NotoSansGujarati-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Gujr" variant="compact">
- <font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansGujaratiUI">
+ NotoSansGujaratiUI-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font>
</family>
<family lang="und-Guru" variant="elegant">
- <font weight="400" style="normal">NotoSansGurmukhi-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansGurmukhi-Regular">
+ NotoSansGurmukhi-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansGurmukhi-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansGurmukhi-Regular">
+ NotoSansGurmukhi-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansGurmukhi-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansGurmukhi-Regular">
+ NotoSansGurmukhi-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansGurmukhi-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansGurmukhi-Regular">
+ NotoSansGurmukhi-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Guru" variant="compact">
- <font weight="400" style="normal">NotoSansGurmukhiUI-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansGurmukhiUI-Regular">
+ NotoSansGurmukhiUI-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansGurmukhiUI-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansGurmukhiUI-Regular">
+ NotoSansGurmukhiUI-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansGurmukhiUI-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansGurmukhiUI-Regular">
+ NotoSansGurmukhiUI-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansGurmukhiUI-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansGurmukhiUI-Regular">
+ NotoSansGurmukhiUI-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Taml" variant="elegant">
- <font weight="400" style="normal">NotoSansTamil-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansTamil-Regular">
+ NotoSansTamil-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansTamil-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansTamil-Regular">
+ NotoSansTamil-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansTamil-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansTamil-Regular">
+ NotoSansTamil-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansTamil-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansTamil-Regular">
+ NotoSansTamil-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Taml" variant="compact">
- <font weight="400" style="normal">NotoSansTamilUI-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansTamilUI-Regular">
+ NotoSansTamilUI-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansTamilUI-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansTamilUI-Regular">
+ NotoSansTamilUI-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansTamilUI-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansTamilUI-Regular">
+ NotoSansTamilUI-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansTamilUI-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansTamilUI-Regular">
+ NotoSansTamilUI-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Mlym" variant="elegant">
- <font weight="400" style="normal">NotoSansMalayalam-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansMalayalam-Regular">
+ NotoSansMalayalam-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansMalayalam-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansMalayalam-Regular">
+ NotoSansMalayalam-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansMalayalam-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansMalayalam-Regular">
+ NotoSansMalayalam-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansMalayalam-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansMalayalam-Regular">
+ NotoSansMalayalam-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Mlym" variant="compact">
- <font weight="400" style="normal">NotoSansMalayalamUI-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansMalayalamUI-Regular">
+ NotoSansMalayalamUI-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansMalayalamUI-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansMalayalamUI-Regular">
+ NotoSansMalayalamUI-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansMalayalamUI-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansMalayalamUI-Regular">
+ NotoSansMalayalamUI-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansMalayalamUI-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansMalayalamUI-Regular">
+ NotoSansMalayalamUI-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Beng" variant="elegant">
- <font weight="400" style="normal">NotoSansBengali-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansBengali-Regular">
+ NotoSansBengali-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansBengali-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansBengali-Regular">
+ NotoSansBengali-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansBengali-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansBengali-Regular">
+ NotoSansBengali-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansBengali-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansBengali-Regular">
+ NotoSansBengali-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Beng" variant="compact">
- <font weight="400" style="normal">NotoSansBengaliUI-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansBengaliUI-Regular">
+ NotoSansBengaliUI-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansBengaliUI-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansBengaliUI-Regular">
+ NotoSansBengaliUI-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansBengaliUI-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansBengaliUI-Regular">
+ NotoSansBengaliUI-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansBengaliUI-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansBengaliUI-Regular">
+ NotoSansBengaliUI-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Telu" variant="elegant">
- <font weight="400" style="normal">NotoSansTelugu-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansTelugu-Regular">
+ NotoSansTelugu-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansTelugu-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansTelugu-Regular">
+ NotoSansTelugu-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansTelugu-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansTelugu-Regular">
+ NotoSansTelugu-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansTelugu-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansTelugu-Regular">
+ NotoSansTelugu-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Telu" variant="compact">
- <font weight="400" style="normal">NotoSansTeluguUI-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansTeluguUI-Regular">
+ NotoSansTeluguUI-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansTeluguUI-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansTeluguUI-Regular">
+ NotoSansTeluguUI-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansTeluguUI-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansTeluguUI-Regular">
+ NotoSansTeluguUI-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansTeluguUI-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansTeluguUI-Regular">
+ NotoSansTeluguUI-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Knda" variant="elegant">
- <font weight="400" style="normal">NotoSansKannada-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansKannada-Regular">
+ NotoSansKannada-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansKannada-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansKannada-Regular">
+ NotoSansKannada-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansKannada-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansKannada-Regular">
+ NotoSansKannada-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansKannada-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansKannada-Regular">
+ NotoSansKannada-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Knda" variant="compact">
- <font weight="400" style="normal">NotoSansKannadaUI-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansKannadaUI-Regular">
+ NotoSansKannadaUI-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansKannadaUI-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansKannadaUI-Regular">
+ NotoSansKannadaUI-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansKannadaUI-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansKannadaUI-Regular">
+ NotoSansKannadaUI-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansKannadaUI-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansKannadaUI-Regular">
+ NotoSansKannadaUI-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Orya" variant="elegant">
- <font weight="400" style="normal">NotoSansOriya-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansOriya">NotoSansOriya-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoSansOriya-Bold.ttf</font>
</family>
<family lang="und-Orya" variant="compact">
- <font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansOriyaUI">
+ NotoSansOriyaUI-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
</family>
<family lang="und-Sinh" variant="elegant">
- <font weight="400" style="normal">NotoSansSinhala-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansSinhala-Regular">
+ NotoSansSinhala-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansSinhala-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansSinhala-Regular">
+ NotoSansSinhala-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansSinhala-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansSinhala-Regular">
+ NotoSansSinhala-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansSinhala-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansSinhala-Regular">
+ NotoSansSinhala-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf
+ <font weight="400" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf
+ <font weight="500" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf
+ <font weight="600" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf
+ <font weight="700" style="normal" fallbackFor="serif"
+ postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Sinh" variant="compact">
- <font weight="400" style="normal">NotoSansSinhalaUI-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansSinhalaUI-Regular">
+ NotoSansSinhalaUI-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansSinhalaUI-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansSinhalaUI-Regular">
+ NotoSansSinhalaUI-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansSinhalaUI-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansSinhalaUI-Regular">
+ NotoSansSinhalaUI-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansSinhalaUI-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansSinhalaUI-Regular">
+ NotoSansSinhalaUI-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Khmr" variant="elegant">
- <font weight="100" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="100" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="26.0"/>
</font>
- <font weight="200" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="200" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="39.0"/>
</font>
- <font weight="300" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="300" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="58.0"/>
</font>
- <font weight="400" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="90.0"/>
</font>
- <font weight="500" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="108.0"/>
</font>
- <font weight="600" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="128.0"/>
</font>
- <font weight="700" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="151.0"/>
</font>
- <font weight="800" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="800" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="169.0"/>
</font>
- <font weight="900" style="normal">NotoSansKhmer-VF.ttf
+ <font weight="900" style="normal" postScriptName="NotoSansKhmer-Regular">
+ NotoSansKhmer-VF.ttf
<axis tag="wdth" stylevalue="100.0"/>
<axis tag="wght" stylevalue="190.0"/>
</font>
@@ -769,17 +921,23 @@
<font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font>
</family>
<family lang="und-Khmr" variant="compact">
- <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansKhmerUI">
+ NotoSansKhmerUI-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
</family>
<family lang="und-Laoo" variant="elegant">
- <font weight="400" style="normal">NotoSansLao-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansLao">NotoSansLao-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifLao-Regular.ttf</font>
+ <font weight="400" style="normal" fallbackFor="serif" postScriptName="NotoSerifLao">
+ NotoSerifLao-Regular.ttf
+ </font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
</family>
<family lang="und-Laoo" variant="compact">
- <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
</family>
<family lang="und-Mymr" variant="elegant">
@@ -795,56 +953,78 @@
<font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font>
</family>
<family lang="und-Thaa">
- <font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansThaana">
+ NotoSansThaana-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoSansThaana-Bold.ttf</font>
</family>
<family lang="und-Cham">
- <font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansCham">NotoSansCham-Regular.ttf
+ </font>
<font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
</family>
<family lang="und-Ahom">
<font weight="400" style="normal">NotoSansAhom-Regular.otf</font>
</family>
<family lang="und-Adlm">
- <font weight="400" style="normal">NotoSansAdlam-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansAdlam-Regular">
+ NotoSansAdlam-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansAdlam-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansAdlam-Regular">
+ NotoSansAdlam-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansAdlam-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansAdlam-Regular">
+ NotoSansAdlam-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansAdlam-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansAdlam-Regular">
+ NotoSansAdlam-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Avst">
- <font weight="400" style="normal">NotoSansAvestan-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansAvestan">
+ NotoSansAvestan-Regular.ttf
+ </font>
</family>
<family lang="und-Bali">
- <font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansBalinese">
+ NotoSansBalinese-Regular.ttf
+ </font>
</family>
<family lang="und-Bamu">
- <font weight="400" style="normal">NotoSansBamum-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansBamum">NotoSansBamum-Regular.ttf
+ </font>
</family>
<family lang="und-Batk">
- <font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansBatak">NotoSansBatak-Regular.ttf
+ </font>
</family>
<family lang="und-Brah">
- <font weight="400" style="normal">NotoSansBrahmi-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansBrahmi">
+ NotoSansBrahmi-Regular.ttf
+ </font>
</family>
<family lang="und-Bugi">
- <font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansBuginese">
+ NotoSansBuginese-Regular.ttf
+ </font>
</family>
<family lang="und-Buhd">
- <font weight="400" style="normal">NotoSansBuhid-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansBuhid">NotoSansBuhid-Regular.ttf
+ </font>
</family>
<family lang="und-Cans">
- <font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansCanadianAboriginal">
+ NotoSansCanadianAboriginal-Regular.ttf
+ </font>
</family>
<family lang="und-Cari">
- <font weight="400" style="normal">NotoSansCarian-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansCarian">
+ NotoSansCarian-Regular.ttf
+ </font>
</family>
<family lang="und-Cakm">
<font weight="400" style="normal">NotoSansChakma-Regular.otf</font>
@@ -853,164 +1033,255 @@
<font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
</family>
<family lang="und-Copt">
- <font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansCoptic">
+ NotoSansCoptic-Regular.ttf
+ </font>
</family>
<family lang="und-Xsux">
- <font weight="400" style="normal">NotoSansCuneiform-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansCuneiform">
+ NotoSansCuneiform-Regular.ttf
+ </font>
</family>
<family lang="und-Cprt">
- <font weight="400" style="normal">NotoSansCypriot-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansCypriot">
+ NotoSansCypriot-Regular.ttf
+ </font>
</family>
<family lang="und-Dsrt">
- <font weight="400" style="normal">NotoSansDeseret-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansDeseret">
+ NotoSansDeseret-Regular.ttf
+ </font>
</family>
<family lang="und-Egyp">
- <font weight="400" style="normal">NotoSansEgyptianHieroglyphs-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansEgyptianHieroglyphs">
+ NotoSansEgyptianHieroglyphs-Regular.ttf
+ </font>
</family>
<family lang="und-Elba">
<font weight="400" style="normal">NotoSansElbasan-Regular.otf</font>
</family>
<family lang="und-Glag">
- <font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansGlagolitic">
+ NotoSansGlagolitic-Regular.ttf
+ </font>
</family>
<family lang="und-Goth">
- <font weight="400" style="normal">NotoSansGothic-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansGothic">
+ NotoSansGothic-Regular.ttf
+ </font>
</family>
<family lang="und-Hano">
- <font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansHanunoo">
+ NotoSansHanunoo-Regular.ttf
+ </font>
</family>
<family lang="und-Armi">
- <font weight="400" style="normal">NotoSansImperialAramaic-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansImperialAramaic">
+ NotoSansImperialAramaic-Regular.ttf
+ </font>
</family>
<family lang="und-Phli">
- <font weight="400" style="normal">NotoSansInscriptionalPahlavi-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansInscriptionalPahlavi">
+ NotoSansInscriptionalPahlavi-Regular.ttf
+ </font>
</family>
<family lang="und-Prti">
- <font weight="400" style="normal">NotoSansInscriptionalParthian-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansInscriptionalParthian">
+ NotoSansInscriptionalParthian-Regular.ttf
+ </font>
</family>
<family lang="und-Java">
<font weight="400" style="normal">NotoSansJavanese-Regular.otf</font>
</family>
<family lang="und-Kthi">
- <font weight="400" style="normal">NotoSansKaithi-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansKaithi">
+ NotoSansKaithi-Regular.ttf
+ </font>
</family>
<family lang="und-Kali">
- <font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansKayahLi">
+ NotoSansKayahLi-Regular.ttf
+ </font>
</family>
<family lang="und-Khar">
- <font weight="400" style="normal">NotoSansKharoshthi-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansKharoshthi">
+ NotoSansKharoshthi-Regular.ttf
+ </font>
</family>
<family lang="und-Lepc">
- <font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansLepcha">
+ NotoSansLepcha-Regular.ttf
+ </font>
</family>
<family lang="und-Limb">
- <font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansLimbu">NotoSansLimbu-Regular.ttf
+ </font>
</family>
<family lang="und-Linb">
- <font weight="400" style="normal">NotoSansLinearB-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansLinearB">
+ NotoSansLinearB-Regular.ttf
+ </font>
</family>
<family lang="und-Lisu">
- <font weight="400" style="normal">NotoSansLisu-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansLisu">NotoSansLisu-Regular.ttf
+ </font>
</family>
<family lang="und-Lyci">
- <font weight="400" style="normal">NotoSansLycian-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansLycian">
+ NotoSansLycian-Regular.ttf
+ </font>
</family>
<family lang="und-Lydi">
- <font weight="400" style="normal">NotoSansLydian-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansLydian">
+ NotoSansLydian-Regular.ttf
+ </font>
</family>
<family lang="und-Mand">
- <font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansMandaic">
+ NotoSansMandaic-Regular.ttf
+ </font>
</family>
<family lang="und-Mtei">
- <font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansMeeteiMayek">
+ NotoSansMeeteiMayek-Regular.ttf
+ </font>
</family>
<family lang="und-Talu">
- <font weight="400" style="normal">NotoSansNewTaiLue-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansNewTaiLue">
+ NotoSansNewTaiLue-Regular.ttf
+ </font>
</family>
<family lang="und-Nkoo">
- <font weight="400" style="normal">NotoSansNKo-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansNKo">NotoSansNKo-Regular.ttf
+ </font>
</family>
<family lang="und-Ogam">
- <font weight="400" style="normal">NotoSansOgham-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansOgham">NotoSansOgham-Regular.ttf
+ </font>
</family>
<family lang="und-Olck">
- <font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansOlChiki">
+ NotoSansOlChiki-Regular.ttf
+ </font>
</family>
<family lang="und-Ital">
- <font weight="400" style="normal">NotoSansOldItalic-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansOldItalic">
+ NotoSansOldItalic-Regular.ttf
+ </font>
</family>
<family lang="und-Xpeo">
- <font weight="400" style="normal">NotoSansOldPersian-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansOldPersian">
+ NotoSansOldPersian-Regular.ttf
+ </font>
</family>
<family lang="und-Sarb">
- <font weight="400" style="normal">NotoSansOldSouthArabian-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansOldSouthArabian">
+ NotoSansOldSouthArabian-Regular.ttf
+ </font>
</family>
<family lang="und-Orkh">
- <font weight="400" style="normal">NotoSansOldTurkic-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansOldTurkic">
+ NotoSansOldTurkic-Regular.ttf
+ </font>
</family>
<family lang="und-Osge">
<font weight="400" style="normal">NotoSansOsage-Regular.ttf</font>
</family>
<family lang="und-Osma">
- <font weight="400" style="normal">NotoSansOsmanya-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansOsmanya">
+ NotoSansOsmanya-Regular.ttf
+ </font>
</family>
<family lang="und-Phnx">
- <font weight="400" style="normal">NotoSansPhoenician-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansPhoenician">
+ NotoSansPhoenician-Regular.ttf
+ </font>
</family>
<family lang="und-Rjng">
- <font weight="400" style="normal">NotoSansRejang-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansRejang">
+ NotoSansRejang-Regular.ttf
+ </font>
</family>
<family lang="und-Runr">
- <font weight="400" style="normal">NotoSansRunic-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansRunic">NotoSansRunic-Regular.ttf
+ </font>
</family>
<family lang="und-Samr">
- <font weight="400" style="normal">NotoSansSamaritan-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansSamaritan">
+ NotoSansSamaritan-Regular.ttf
+ </font>
</family>
<family lang="und-Saur">
- <font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansSaurashtra">
+ NotoSansSaurashtra-Regular.ttf
+ </font>
</family>
<family lang="und-Shaw">
- <font weight="400" style="normal">NotoSansShavian-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansShavian">
+ NotoSansShavian-Regular.ttf
+ </font>
</family>
<family lang="und-Sund">
- <font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansSundanese">
+ NotoSansSundanese-Regular.ttf
+ </font>
</family>
<family lang="und-Sylo">
- <font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansSylotiNagri">
+ NotoSansSylotiNagri-Regular.ttf
+ </font>
</family>
<!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. -->
<family lang="und-Syre">
- <font weight="400" style="normal">NotoSansSyriacEstrangela-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansSyriacEstrangela">
+ NotoSansSyriacEstrangela-Regular.ttf
+ </font>
</family>
<family lang="und-Syrn">
- <font weight="400" style="normal">NotoSansSyriacEastern-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansSyriacEastern">
+ NotoSansSyriacEastern-Regular.ttf
+ </font>
</family>
<family lang="und-Syrj">
- <font weight="400" style="normal">NotoSansSyriacWestern-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansSyriacWestern">
+ NotoSansSyriacWestern-Regular.ttf
+ </font>
</family>
<family lang="und-Tglg">
- <font weight="400" style="normal">NotoSansTagalog-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansTagalog">
+ NotoSansTagalog-Regular.ttf
+ </font>
</family>
<family lang="und-Tagb">
- <font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansTagbanwa">
+ NotoSansTagbanwa-Regular.ttf
+ </font>
</family>
<family lang="und-Lana">
- <font weight="400" style="normal">NotoSansTaiTham-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansTaiTham">
+ NotoSansTaiTham-Regular.ttf
+ </font>
</family>
<family lang="und-Tavt">
- <font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansTaiViet">
+ NotoSansTaiViet-Regular.ttf
+ </font>
</family>
<family lang="und-Tibt">
- <font weight="400" style="normal">NotoSerifTibetan-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSerifTibetan-Regular">
+ NotoSerifTibetan-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSerifTibetan-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSerifTibetan-Regular">
+ NotoSerifTibetan-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSerifTibetan-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSerifTibetan-Regular">
+ NotoSerifTibetan-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSerifTibetan-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSerifTibetan-Regular">
+ NotoSerifTibetan-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
@@ -1018,29 +1289,48 @@
<font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font>
</family>
<family lang="und-Ugar">
- <font weight="400" style="normal">NotoSansUgaritic-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansUgaritic">
+ NotoSansUgaritic-Regular.ttf
+ </font>
</family>
<family lang="und-Vaii">
- <font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansVai">NotoSansVai-Regular.ttf
+ </font>
</family>
<family>
<font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
</family>
<family lang="zh-Hans">
- <font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
- <font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Regular">
+ NotoSansCJK-Regular.ttc
+ </font>
+ <font weight="400" style="normal" index="2" fallbackFor="serif"
+ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
+ </font>
</family>
<family lang="zh-Hant,zh-Bopo">
- <font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
- <font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Regular">
+ NotoSansCJK-Regular.ttc
+ </font>
+ <font weight="400" style="normal" index="3" fallbackFor="serif"
+ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
+ </font>
</family>
<family lang="ja">
- <font weight="400" style="normal" index="0">NotoSansCJK-Regular.ttc</font>
- <font weight="400" style="normal" index="0" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Regular">
+ NotoSansCJK-Regular.ttc
+ </font>
+ <font weight="400" style="normal" index="0" fallbackFor="serif"
+ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
+ </font>
</family>
<family lang="ko">
- <font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font>
- <font weight="400" style="normal" index="1" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
+ <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Regular">
+ NotoSansCJK-Regular.ttc
+ </font>
+ <font weight="400" style="normal" index="1" fallbackFor="serif"
+ postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc
+ </font>
</family>
<family lang="und-Zsye">
<font weight="400" style="normal">NotoColorEmoji.ttf</font>
@@ -1053,16 +1343,21 @@
override the East Asian punctuation for Chinese.
-->
<family lang="und-Tale">
- <font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansTaiLe">NotoSansTaiLe-Regular.ttf
+ </font>
</family>
<family lang="und-Yiii">
- <font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansYi">NotoSansYi-Regular.ttf</font>
</family>
<family lang="und-Mong">
- <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansMongolian">
+ NotoSansMongolian-Regular.ttf
+ </font>
</family>
<family lang="und-Phag">
- <font weight="400" style="normal">NotoSansPhagsPa-Regular.ttf</font>
+ <font weight="400" style="normal" postScriptName="NotoSansPhagsPa">
+ NotoSansPhagsPa-Regular.ttf
+ </font>
</family>
<family lang="und-Hluw">
<font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font>
@@ -1152,72 +1447,92 @@
<font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font>
</family>
<family lang="und-Medf">
- <font weight="400" style="normal">NotoSansMedefaidrin-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansMedefaidrin-Regular">
+ NotoSansMedefaidrin-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansMedefaidrin-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansMedefaidrin-Regular">
+ NotoSansMedefaidrin-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansMedefaidrin-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansMedefaidrin-Regular">
+ NotoSansMedefaidrin-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansMedefaidrin-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansMedefaidrin-Regular">
+ NotoSansMedefaidrin-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Soyo">
- <font weight="400" style="normal">NotoSansSoyombo-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansSoyombo-Regular">
+ NotoSansSoyombo-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansSoyombo-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansSoyombo-Regular">
+ NotoSansSoyombo-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansSoyombo-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansSoyombo-Regular">
+ NotoSansSoyombo-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansSoyombo-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansSoyombo-Regular">
+ NotoSansSoyombo-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Takr">
- <font weight="400" style="normal">NotoSansTakri-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansTakri-Regular">
+ NotoSansTakri-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSansTakri-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSansTakri-Regular">
+ NotoSansTakri-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSansTakri-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSansTakri-Regular">
+ NotoSansTakri-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSansTakri-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSansTakri-Regular">
+ NotoSansTakri-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Hmnp">
- <font weight="400" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular">
+ NotoSerifNyiakengPuachueHmong-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular">
+ NotoSerifNyiakengPuachueHmong-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular">
+ NotoSerifNyiakengPuachueHmong-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular">
+ NotoSerifNyiakengPuachueHmong-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
<family lang="und-Yezi">
- <font weight="400" style="normal">NotoSerifYezidi-VF.ttf
+ <font weight="400" style="normal" postScriptName="NotoSerifYezidi-Regular">
+ NotoSerifYezidi-VF.ttf
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal">NotoSerifYezidi-VF.ttf
+ <font weight="500" style="normal" postScriptName="NotoSerifYezidi-Regular">
+ NotoSerifYezidi-VF.ttf
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal">NotoSerifYezidi-VF.ttf
+ <font weight="600" style="normal" postScriptName="NotoSerifYezidi-Regular">
+ NotoSerifYezidi-VF.ttf
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal">NotoSerifYezidi-VF.ttf
+ <font weight="700" style="normal" postScriptName="NotoSerifYezidi-Regular">
+ NotoSerifYezidi-VF.ttf
<axis tag="wght" stylevalue="700"/>
</font>
</family>
diff --git a/errorprone/java/android/annotation/RequiresNoPermission.java b/errorprone/java/android/annotation/RequiresNoPermission.java
new file mode 100644
index 0000000..6ff4d6e3
--- /dev/null
+++ b/errorprone/java/android/annotation/RequiresNoPermission.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.annotation;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the annotated element requires no permissions.
+ *
+ * @hide
+ */
+@Retention(CLASS)
+@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD,PARAMETER})
+public @interface RequiresNoPermission {
+}
diff --git a/errorprone/java/android/annotation/RequiresPermission.java b/errorprone/java/android/annotation/RequiresPermission.java
new file mode 100644
index 0000000..303ab41
--- /dev/null
+++ b/errorprone/java/android/annotation/RequiresPermission.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 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.annotation;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the annotated element requires (or may require) one or more permissions.
+ * <p/>
+ * Example of requiring a single permission:
+ * <pre>{@code
+ * {@literal @}RequiresPermission(Manifest.permission.SET_WALLPAPER)
+ * public abstract void setWallpaper(Bitmap bitmap) throws IOException;
+ *
+ * {@literal @}RequiresPermission(ACCESS_COARSE_LOCATION)
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring at least one permission from a set:
+ * <pre>{@code
+ * {@literal @}RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring multiple permissions:
+ * <pre>{@code
+ * {@literal @}RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring separate read and write permissions for a content provider:
+ * <pre>{@code
+ * {@literal @}RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
+ * {@literal @}RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
+ * public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
+ * }</pre>
+ * <p>
+ * When specified on a parameter, the annotation indicates that the method requires
+ * a permission which depends on the value of the parameter. For example, consider
+ * {@link android.app.Activity#startActivity(android.content.Intent)
+ * Activity#startActivity(Intent)}:
+ * <pre>{@code
+ * public void startActivity(@RequiresPermission Intent intent) { ... }
+ * }</pre>
+ * Notice how there are no actual permission names listed in the annotation. The actual
+ * permissions required will depend on the particular intent passed in. For example,
+ * the code may look like this:
+ * <pre>{@code
+ * Intent intent = new Intent(Intent.ACTION_CALL);
+ * startActivity(intent);
+ * }</pre>
+ * and the actual permission requirement for this particular intent is described on
+ * the Intent name itself:
+ * <pre>{@code
+ * {@literal @}RequiresPermission(Manifest.permission.CALL_PHONE)
+ * public static final String ACTION_CALL = "android.intent.action.CALL";
+ * }</pre>
+ *
+ * @hide
+ */
+@Retention(CLASS)
+@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD,PARAMETER})
+public @interface RequiresPermission {
+ /**
+ * The name of the permission that is required, if precisely one permission
+ * is required. If more than one permission is required, specify either
+ * {@link #allOf()} or {@link #anyOf()} instead.
+ * <p>
+ * If specified, {@link #anyOf()} and {@link #allOf()} must both be null.
+ */
+ String value() default "";
+
+ /**
+ * Specifies a list of permission names that are all required.
+ * <p>
+ * If specified, {@link #anyOf()} and {@link #value()} must both be null.
+ */
+ String[] allOf() default {};
+
+ /**
+ * Specifies a list of permission names where at least one is required
+ * <p>
+ * If specified, {@link #allOf()} and {@link #value()} must both be null.
+ */
+ String[] anyOf() default {};
+
+ /**
+ * If true, the permission may not be required in all cases (e.g. it may only be
+ * enforced on certain platforms, or for certain call parameters, etc.
+ */
+ boolean conditional() default false;
+
+ /**
+ * Specifies that the given permission is required for read operations.
+ * <p>
+ * When specified on a parameter, the annotation indicates that the method requires
+ * a permission which depends on the value of the parameter (and typically
+ * the corresponding field passed in will be one of a set of constants which have
+ * been annotated with a <code>@RequiresPermission</code> annotation.)
+ */
+ @Target({FIELD, METHOD, PARAMETER})
+ @interface Read {
+ RequiresPermission value() default @RequiresPermission;
+ }
+
+ /**
+ * Specifies that the given permission is required for write operations.
+ * <p>
+ * When specified on a parameter, the annotation indicates that the method requires
+ * a permission which depends on the value of the parameter (and typically
+ * the corresponding field passed in will be one of a set of constants which have
+ * been annotated with a <code>@RequiresPermission</code> annotation.)
+ */
+ @Target({FIELD, METHOD, PARAMETER})
+ @interface Write {
+ RequiresPermission value() default @RequiresPermission;
+ }
+}
diff --git a/errorprone/java/android/annotation/SuppressLint.java b/errorprone/java/android/annotation/SuppressLint.java
new file mode 100644
index 0000000..2d3456b
--- /dev/null
+++ b/errorprone/java/android/annotation/SuppressLint.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 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.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Indicates that Lint should ignore the specified warnings for the annotated element. */
+@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
+@Retention(RetentionPolicy.CLASS)
+public @interface SuppressLint {
+ /**
+ * The set of warnings (identified by the lint issue id) that should be
+ * ignored by lint. It is not an error to specify an unrecognized name.
+ */
+ String[] value();
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/BluetoothPermissionChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/BluetoothPermissionChecker.java
new file mode 100644
index 0000000..9d1cf87
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/BluetoothPermissionChecker.java
@@ -0,0 +1,213 @@
+/*
+ * 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.bugpatterns.android.RequiresPermissionChecker.simpleNameMatches;
+import static com.google.errorprone.matchers.Matchers.allOf;
+import static com.google.errorprone.matchers.Matchers.anyOf;
+import static com.google.errorprone.matchers.Matchers.enclosingClass;
+import static com.google.errorprone.matchers.Matchers.isStatic;
+import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
+import static com.google.errorprone.matchers.Matchers.methodHasVisibility;
+import static com.google.errorprone.matchers.Matchers.methodIsConstructor;
+import static com.google.errorprone.matchers.Matchers.methodIsNamed;
+import static com.google.errorprone.matchers.Matchers.not;
+import static com.google.errorprone.matchers.Matchers.packageStartsWith;
+
+import android.annotation.RequiresNoPermission;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.matchers.MethodVisibility.Visibility;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.regex.Pattern;
+
+/**
+ * Verifies that all Bluetooth APIs have consistent permissions.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkBluetoothPermission",
+ summary = "Verifies that all Bluetooth APIs have consistent permissions",
+ severity = WARNING)
+public final class BluetoothPermissionChecker extends BugChecker implements MethodTreeMatcher {
+ private static final Matcher<MethodTree> BLUETOOTH_API = allOf(
+ packageStartsWith("android.bluetooth"),
+ methodHasVisibility(Visibility.PUBLIC),
+ not(isStatic()),
+ not(methodIsConstructor()),
+ not(enclosingClass(isInsideParcelable())),
+ not(enclosingClass(simpleNameMatches(Pattern.compile(".+Callback$")))),
+ not(enclosingClass(isSubtypeOf("android.bluetooth.BluetoothProfileConnector"))),
+ not(enclosingClass(isSubtypeOf("android.app.PropertyInvalidatedCache"))));
+
+ private static final Matcher<ClassTree> PARCELABLE_CLASS =
+ isSubtypeOf("android.os.Parcelable");
+ private static final Matcher<MethodTree> BINDER_METHOD = enclosingClass(
+ isSubtypeOf("android.os.IInterface"));
+
+ private static final Matcher<MethodTree> BINDER_INTERNALS = allOf(
+ enclosingClass(isSubtypeOf("android.os.IInterface")),
+ anyOf(
+ methodIsNamed("onTransact"),
+ methodIsNamed("dump"),
+ enclosingClass(simpleNameMatches(Pattern.compile("^(Stub|Default|Proxy)$")))));
+
+ private static final Matcher<MethodTree> GENERIC_INTERNALS = anyOf(
+ methodIsNamed("close"),
+ methodIsNamed("finalize"),
+ methodIsNamed("equals"),
+ methodIsNamed("hashCode"),
+ methodIsNamed("toString"));
+
+ private static final String PERMISSION_ADVERTISE = "android.permission.BLUETOOTH_ADVERTISE";
+ private static final String PERMISSION_CONNECT = "android.permission.BLUETOOTH_CONNECT";
+ private static final String PERMISSION_SCAN = "android.permission.BLUETOOTH_SCAN";
+
+ private static final String ANNOTATION_ADVERTISE =
+ "android.bluetooth.annotations.RequiresBluetoothAdvertisePermission";
+ private static final String ANNOTATION_CONNECT =
+ "android.bluetooth.annotations.RequiresBluetoothConnectPermission";
+ private static final String ANNOTATION_SCAN =
+ "android.bluetooth.annotations.RequiresBluetoothScanPermission";
+
+ @Override
+ public Description matchMethod(MethodTree tree, VisitorState state) {
+ // Ignore methods outside Bluetooth area
+ if (!BLUETOOTH_API.matches(tree, state)) return Description.NO_MATCH;
+
+ // Ignore certain types of generated or internal code
+ if (BINDER_INTERNALS.matches(tree, state)) return Description.NO_MATCH;
+ if (GENERIC_INTERNALS.matches(tree, state)) return Description.NO_MATCH;
+
+ // Skip abstract methods, except for binder interfaces
+ if (tree.getBody() == null && !BINDER_METHOD.matches(tree, state)) {
+ return Description.NO_MATCH;
+ }
+
+ // Ignore callbacks which don't need permission enforcement
+ final MethodSymbol symbol = ASTHelpers.getSymbol(tree);
+ if (isCallbackOrWrapper(symbol)) return Description.NO_MATCH;
+
+ // Ignore when suppressed
+ if (isSuppressed(symbol)) return Description.NO_MATCH;
+
+ final RequiresPermission requiresPerm = ASTHelpers.getAnnotation(tree,
+ RequiresPermission.class);
+ final RequiresNoPermission requiresNoPerm = ASTHelpers.getAnnotation(tree,
+ RequiresNoPermission.class);
+
+ final boolean requiresValid = requiresPerm != null
+ && (requiresPerm.value() != null || requiresPerm.allOf() != null);
+ final boolean requiresNoValid = requiresNoPerm != null;
+ if (!requiresValid && !requiresNoValid) {
+ return buildDescription(tree)
+ .setMessage("Method " + symbol.name.toString()
+ + "() must be protected by at least one permission")
+ .build();
+ }
+
+ // No additional checks needed for Binder generated code
+ if (BINDER_METHOD.matches(tree, state)) return Description.NO_MATCH;
+
+ if (ASTHelpers.hasAnnotation(tree, ANNOTATION_ADVERTISE,
+ state) != isPermissionReferenced(requiresPerm, PERMISSION_ADVERTISE)) {
+ return buildDescription(tree)
+ .setMessage("Method " + symbol.name.toString()
+ + "() has inconsistent annotations for " + PERMISSION_ADVERTISE)
+ .build();
+ }
+ if (ASTHelpers.hasAnnotation(tree, ANNOTATION_CONNECT,
+ state) != isPermissionReferenced(requiresPerm, PERMISSION_CONNECT)) {
+ return buildDescription(tree)
+ .setMessage("Method " + symbol.name.toString()
+ + "() has inconsistent annotations for " + PERMISSION_CONNECT)
+ .build();
+ }
+ if (ASTHelpers.hasAnnotation(tree, ANNOTATION_SCAN,
+ state) != isPermissionReferenced(requiresPerm, PERMISSION_SCAN)) {
+ return buildDescription(tree)
+ .setMessage("Method " + symbol.name.toString()
+ + "() has inconsistent annotations for " + PERMISSION_SCAN)
+ .build();
+ }
+
+ return Description.NO_MATCH;
+ }
+
+ private static boolean isPermissionReferenced(RequiresPermission anno, String perm) {
+ if (anno == null) return false;
+ if (perm.equals(anno.value())) return true;
+ return anno.allOf() != null && Arrays.asList(anno.allOf()).contains(perm);
+ }
+
+ private static boolean isCallbackOrWrapper(Symbol symbol) {
+ if (symbol == null) return false;
+ final String name = symbol.name.toString();
+ return isCallbackOrWrapper(ASTHelpers.enclosingClass(symbol))
+ || name.endsWith("Callback")
+ || name.endsWith("Wrapper");
+ }
+
+ public boolean isSuppressed(Symbol symbol) {
+ if (symbol == null) return false;
+ return isSuppressed(ASTHelpers.enclosingClass(symbol))
+ || isSuppressed(ASTHelpers.getAnnotation(symbol, SuppressWarnings.class))
+ || isSuppressed(ASTHelpers.getAnnotation(symbol, SuppressLint.class));
+ }
+
+ private boolean isSuppressed(SuppressWarnings anno) {
+ return (anno != null) && !Collections.disjoint(Arrays.asList(anno.value()), allNames());
+ }
+
+ private boolean isSuppressed(SuppressLint anno) {
+ return (anno != null) && !Collections.disjoint(Arrays.asList(anno.value()), allNames());
+ }
+
+ private static Matcher<ClassTree> isInsideParcelable() {
+ return new Matcher<ClassTree>() {
+ @Override
+ public boolean matches(ClassTree tree, VisitorState state) {
+ final TreePath path = state.getPath();
+ for (Tree node : path) {
+ if (node instanceof ClassTree
+ && PARCELABLE_CLASS.matches((ClassTree) node, state)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
new file mode 100644
index 0000000..f54782d
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
@@ -0,0 +1,333 @@
+/*
+ * 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.allOf;
+import static com.google.errorprone.matchers.Matchers.anyOf;
+import static com.google.errorprone.matchers.Matchers.enclosingClass;
+import static com.google.errorprone.matchers.Matchers.instanceMethod;
+import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+import static com.google.errorprone.matchers.Matchers.methodIsNamed;
+import static com.google.errorprone.matchers.Matchers.staticMethod;
+
+import android.annotation.SuppressLint;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.util.TreeScanner;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Type.ClassType;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.ExecutableElement;
+
+/**
+ * Inspects both the client and server side of AIDL interfaces to ensure that
+ * any {@code RequiresPermission} annotations are consistently declared and
+ * enforced.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkRequiresPermission",
+ summary = "Verifies that @RequiresPermission annotations are consistent across AIDL",
+ severity = WARNING)
+public final class RequiresPermissionChecker extends BugChecker implements MethodTreeMatcher {
+ private static final String ANNOTATION_REQUIRES_PERMISSION = "RequiresPermission";
+
+ private static final Matcher<ExpressionTree> ENFORCE_VIA_CONTEXT = methodInvocation(
+ instanceMethod()
+ .onDescendantOf("android.content.Context")
+ .withNameMatching(
+ Pattern.compile("^(enforce|check)(Calling)?(OrSelf)?Permission$")));
+ private static final Matcher<ExpressionTree> ENFORCE_VIA_CHECKER = methodInvocation(
+ staticMethod()
+ .onClass("android.content.PermissionChecker")
+ .withNameMatching(Pattern.compile("^check.*")));
+
+ private static final Matcher<MethodTree> BINDER_INTERNALS = allOf(
+ enclosingClass(isSubtypeOf("android.os.IInterface")),
+ anyOf(
+ methodIsNamed("onTransact"),
+ methodIsNamed("dump"),
+ enclosingClass(simpleNameMatches(Pattern.compile("^(Stub|Default|Proxy)$")))));
+ private static final Matcher<MethodTree> LOCAL_INTERNALS = anyOf(
+ methodIsNamed("finalize"),
+ allOf(
+ enclosingClass(isSubtypeOf("android.content.BroadcastReceiver")),
+ methodIsNamed("onReceive")),
+ allOf(
+ enclosingClass(isSubtypeOf("android.database.ContentObserver")),
+ methodIsNamed("onChange")),
+ allOf(
+ enclosingClass(isSubtypeOf("android.os.Handler")),
+ methodIsNamed("handleMessage")),
+ allOf(
+ enclosingClass(isSubtypeOf("android.os.IBinder.DeathRecipient")),
+ methodIsNamed("binderDied")));
+
+ private static final Matcher<ExpressionTree> CLEAR_CALL = methodInvocation(staticMethod()
+ .onClass("android.os.Binder").withSignature("clearCallingIdentity()"));
+ private static final Matcher<ExpressionTree> RESTORE_CALL = methodInvocation(staticMethod()
+ .onClass("android.os.Binder").withSignature("restoreCallingIdentity(long)"));
+
+ @Override
+ public Description matchMethod(MethodTree tree, VisitorState state) {
+ // Ignore methods without an implementation
+ if (tree.getBody() == null) return Description.NO_MATCH;
+
+ // Ignore certain types of Binder generated code
+ if (BINDER_INTERNALS.matches(tree, state)) return Description.NO_MATCH;
+
+ // Ignore known-local methods which don't need to propagate
+ if (LOCAL_INTERNALS.matches(tree, state)) return Description.NO_MATCH;
+
+ // Ignore when suppressed via superclass
+ final MethodSymbol method = ASTHelpers.getSymbol(tree);
+ if (isSuppressedRecursively(method, state)) return Description.NO_MATCH;
+
+ // First, look at all outgoing method invocations to ensure that we
+ // carry those annotations forward; yell if we're too narrow
+ final ParsedRequiresPermission expectedPerm = parseRequiresPermissionRecursively(
+ method, state);
+ final ParsedRequiresPermission actualPerm = new ParsedRequiresPermission();
+ final Description desc = tree.accept(new TreeScanner<Description, Void>() {
+ private boolean clearedCallingIdentity = false;
+
+ @Override
+ public Description visitMethodInvocation(MethodInvocationTree node, Void param) {
+ if (CLEAR_CALL.matches(node, state)) {
+ clearedCallingIdentity = true;
+ } else if (RESTORE_CALL.matches(node, state)) {
+ clearedCallingIdentity = false;
+ } else if (!clearedCallingIdentity) {
+ final ParsedRequiresPermission nodePerm = parseRequiresPermissionRecursively(
+ node, state);
+ if (!expectedPerm.containsAll(nodePerm)) {
+ return buildDescription(node)
+ .setMessage("Method " + method.name.toString() + "() annotated "
+ + expectedPerm
+ + " but too narrow; invokes method requiring " + nodePerm)
+ .build();
+ } else {
+ actualPerm.addAll(nodePerm);
+ }
+ }
+ return super.visitMethodInvocation(node, param);
+ }
+
+ @Override
+ public Description reduce(Description r1, Description r2) {
+ return (r1 != null) ? r1 : r2;
+ }
+ }, null);
+ if (desc != null) return desc;
+
+ // Second, determine if we actually used all permissions that we claim
+ // to require; yell if we're too broad
+ if (!actualPerm.containsAll(expectedPerm)) {
+ return buildDescription(tree)
+ .setMessage("Method " + method.name.toString() + "() annotated " + expectedPerm
+ + " but too wide; only invokes methods requiring " + actualPerm)
+ .build();
+ }
+
+ return Description.NO_MATCH;
+ }
+
+ static class ParsedRequiresPermission {
+ final Set<String> allOf = new HashSet<>();
+ final Set<String> anyOf = new HashSet<>();
+
+ public boolean isEmpty() {
+ return allOf.isEmpty() && anyOf.isEmpty();
+ }
+
+ /**
+ * Validate that this annotation effectively "contains" the given
+ * annotation. This is typically used to ensure that a method carries
+ * along all relevant annotations for the methods it invokes.
+ */
+ public boolean containsAll(ParsedRequiresPermission perm) {
+ boolean allMet = allOf.containsAll(perm.allOf);
+ boolean anyMet = false;
+ if (perm.anyOf.isEmpty()) {
+ anyMet = true;
+ } else {
+ for (String anyPerm : perm.anyOf) {
+ if (allOf.contains(anyPerm) || anyOf.contains(anyPerm)) {
+ anyMet = true;
+ }
+ }
+ }
+ return allMet && anyMet;
+ }
+
+ @Override
+ public String toString() {
+ if (isEmpty()) {
+ return "[none]";
+ }
+ String res = "{allOf=" + allOf;
+ if (!anyOf.isEmpty()) {
+ res += " anyOf=" + anyOf;
+ }
+ res += "}";
+ return res;
+ }
+
+ public void addAll(ParsedRequiresPermission perm) {
+ this.allOf.addAll(perm.allOf);
+ this.anyOf.addAll(perm.anyOf);
+ }
+
+ public void addAll(AnnotationMirror a) {
+ for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : a
+ .getElementValues().entrySet()) {
+ if (entry.getKey().getSimpleName().contentEquals("value")) {
+ maybeAdd(allOf, entry.getValue());
+ } else if (entry.getKey().getSimpleName().contentEquals("allOf")) {
+ maybeAdd(allOf, entry.getValue());
+ } else if (entry.getKey().getSimpleName().contentEquals("anyOf")) {
+ maybeAdd(anyOf, entry.getValue());
+ }
+ }
+ }
+
+ private static void maybeAdd(Set<String> set, Object value) {
+ if (value instanceof AnnotationValue) {
+ maybeAdd(set, ((AnnotationValue) value).getValue());
+ } else if (value instanceof String) {
+ set.add((String) value);
+ } else if (value instanceof Collection) {
+ for (Object o : (Collection) value) {
+ maybeAdd(set, o);
+ }
+ } else {
+ throw new RuntimeException(String.valueOf(value.getClass()));
+ }
+ }
+ }
+
+ private static ParsedRequiresPermission parseRequiresPermissionRecursively(
+ MethodInvocationTree tree, VisitorState state) {
+ if (ENFORCE_VIA_CONTEXT.matches(tree, state)) {
+ final ParsedRequiresPermission res = new ParsedRequiresPermission();
+ res.allOf.add(String.valueOf(ASTHelpers.constValue(tree.getArguments().get(0))));
+ return res;
+ } else if (ENFORCE_VIA_CHECKER.matches(tree, state)) {
+ final ParsedRequiresPermission res = new ParsedRequiresPermission();
+ res.allOf.add(String.valueOf(ASTHelpers.constValue(tree.getArguments().get(1))));
+ return res;
+ } else {
+ final MethodSymbol method = ASTHelpers.getSymbol(tree);
+ return parseRequiresPermissionRecursively(method, state);
+ }
+ }
+
+ /**
+ * Parse any {@code RequiresPermission} annotations associated with the
+ * given method, defined either directly on the method or by any superclass.
+ */
+ private static ParsedRequiresPermission parseRequiresPermissionRecursively(
+ MethodSymbol method, VisitorState state) {
+ final List<MethodSymbol> symbols = new ArrayList<>();
+ symbols.add(method);
+ symbols.addAll(ASTHelpers.findSuperMethods(method, state.getTypes()));
+
+ final ParsedRequiresPermission res = new ParsedRequiresPermission();
+ for (MethodSymbol symbol : symbols) {
+ for (AnnotationMirror a : symbol.getAnnotationMirrors()) {
+ if (a.getAnnotationType().asElement().getSimpleName()
+ .contentEquals(ANNOTATION_REQUIRES_PERMISSION)) {
+ res.addAll(a);
+ }
+ }
+ }
+ return res;
+ }
+
+ private boolean isSuppressedRecursively(MethodSymbol method, VisitorState state) {
+ // Is method suppressed anywhere?
+ if (isSuppressed(method)) return true;
+ for (MethodSymbol symbol : ASTHelpers.findSuperMethods(method, state.getTypes())) {
+ if (isSuppressed(symbol)) return true;
+ }
+
+ // Is class suppressed anywhere?
+ final ClassSymbol clazz = ASTHelpers.enclosingClass(method);
+ if (isSuppressed(clazz)) return true;
+ Type type = clazz.getSuperclass();
+ while (type != null) {
+ if (isSuppressed(type.tsym)) return true;
+ if (type instanceof ClassType) {
+ type = ((ClassType) type).supertype_field;
+ } else {
+ type = null;
+ }
+ }
+ return false;
+ }
+
+ public boolean isSuppressed(Symbol symbol) {
+ return isSuppressed(ASTHelpers.getAnnotation(symbol, SuppressWarnings.class))
+ || isSuppressed(ASTHelpers.getAnnotation(symbol, SuppressLint.class));
+ }
+
+ private boolean isSuppressed(SuppressWarnings anno) {
+ return (anno != null) && !Collections.disjoint(Arrays.asList(anno.value()), allNames());
+ }
+
+ private boolean isSuppressed(SuppressLint anno) {
+ return (anno != null) && !Collections.disjoint(Arrays.asList(anno.value()), allNames());
+ }
+
+ static Matcher<ClassTree> simpleNameMatches(Pattern pattern) {
+ return new Matcher<ClassTree>() {
+ @Override
+ public boolean matches(ClassTree tree, VisitorState state) {
+ final CharSequence name = tree.getSimpleName().toString();
+ return pattern.matcher(name).matches();
+ }
+ };
+ }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java
new file mode 100644
index 0000000..771258d
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java
@@ -0,0 +1,325 @@
+/*
+ * 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.google.errorprone.bugpatterns.android;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.errorprone.CompilationTestHelper;
+import com.google.errorprone.bugpatterns.android.RequiresPermissionChecker.ParsedRequiresPermission;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(JUnit4.class)
+public class RequiresPermissionCheckerTest {
+ private CompilationTestHelper compilationHelper;
+
+ private static final String RED = "red";
+ private static final String BLUE = "blue";
+
+ @Before
+ public void setUp() {
+ compilationHelper = CompilationTestHelper.newInstance(
+ RequiresPermissionChecker.class, getClass());
+ }
+
+ private static ParsedRequiresPermission build(Collection<String> allOf,
+ Collection<String> anyOf) {
+ ParsedRequiresPermission res = new ParsedRequiresPermission();
+ res.allOf.addAll(allOf);
+ res.anyOf.addAll(anyOf);
+ return res;
+ }
+
+ @Test
+ public void testParser_AllOf() {
+ final ParsedRequiresPermission a = build(Arrays.asList(RED, BLUE), Arrays.asList());
+ final ParsedRequiresPermission b = build(Arrays.asList(RED), Arrays.asList());
+ assertTrue(a.containsAll(b));
+ assertFalse(b.containsAll(a));
+ }
+
+ @Test
+ public void testParser_AnyOf() {
+ final ParsedRequiresPermission a = build(Arrays.asList(), Arrays.asList(RED, BLUE));
+ final ParsedRequiresPermission b = build(Arrays.asList(), Arrays.asList(RED));
+ assertTrue(a.containsAll(b));
+ assertTrue(b.containsAll(a));
+ }
+
+ @Test
+ public void testParser_AnyOf_AllOf() {
+ final ParsedRequiresPermission a = build(Arrays.asList(RED, BLUE), Arrays.asList());
+ final ParsedRequiresPermission b = build(Arrays.asList(), Arrays.asList(RED));
+ assertTrue(a.containsAll(b));
+ assertFalse(b.containsAll(a));
+ }
+
+ @Test
+ public void testSimple() {
+ compilationHelper
+ .addSourceFile("/android/annotation/RequiresPermission.java")
+ .addSourceFile("/android/content/Context.java")
+ .addSourceLines("ColorManager.java",
+ "import android.annotation.RequiresPermission;",
+ "import android.content.Context;",
+ "public abstract class ColorManager extends Context {",
+ " private static final String RED = \"red\";",
+ " private static final String BLUE = \"blue\";",
+ " @RequiresPermission(RED) abstract int red();",
+ " @RequiresPermission(BLUE) abstract int blue();",
+ " @RequiresPermission(allOf={RED, BLUE}) abstract int all();",
+ " @RequiresPermission(anyOf={RED, BLUE}) abstract int any();",
+ " @RequiresPermission(allOf={RED, BLUE})",
+ " int redPlusBlue() { return red() + blue(); }",
+ " @RequiresPermission(allOf={RED, BLUE})",
+ " int allPlusRed() { return all() + red(); }",
+ " @RequiresPermission(allOf={RED})",
+ " int anyPlusRed() { return any() + red(); }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testManager() {
+ compilationHelper
+ .addSourceFile("/android/annotation/RequiresPermission.java")
+ .addSourceFile("/android/foo/IColorService.java")
+ .addSourceFile("/android/os/IInterface.java")
+ .addSourceLines("ColorManager.java",
+ "import android.annotation.RequiresPermission;",
+ "import android.foo.IColorService;",
+ "public class ColorManager {",
+ " IColorService mService;",
+ " @RequiresPermission(IColorService.RED)",
+ " void redValid() {",
+ " mService.red();",
+ " }",
+ " @RequiresPermission(allOf={IColorService.RED, IColorService.BLUE})",
+ " // BUG: Diagnostic contains:",
+ " void redOverbroad() {",
+ " mService.red();",
+ " }",
+ " @RequiresPermission(IColorService.BLUE)",
+ " void redInvalid() {",
+ " // BUG: Diagnostic contains:",
+ " mService.red();",
+ " }",
+ " void redMissing() {",
+ " // BUG: Diagnostic contains:",
+ " mService.red();",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testService() {
+ compilationHelper
+ .addSourceFile("/android/annotation/RequiresPermission.java")
+ .addSourceFile("/android/content/Context.java")
+ .addSourceFile("/android/foo/IColorService.java")
+ .addSourceFile("/android/os/IInterface.java")
+ .addSourceLines("ColorService.java",
+ "import android.annotation.RequiresPermission;",
+ "import android.content.Context;",
+ "import android.foo.IColorService;",
+ "class ColorService extends Context implements IColorService {",
+ " public void none() {}",
+ " // BUG: Diagnostic contains:",
+ " public void red() {}",
+ " // BUG: Diagnostic contains:",
+ " public void redAndBlue() {}",
+ " // BUG: Diagnostic contains:",
+ " public void redOrBlue() {}",
+ " void onTransact(int code) {",
+ " red();",
+ " }",
+ "}",
+ "class ValidService extends ColorService {",
+ " public void red() {",
+ " ((Context) this).enforceCallingOrSelfPermission(RED, null);",
+ " }",
+ "}",
+ "class InvalidService extends ColorService {",
+ " public void red() {",
+ " // BUG: Diagnostic contains:",
+ " ((Context) this).enforceCallingOrSelfPermission(BLUE, null);",
+ " }",
+ "}",
+ "class NestedService extends ColorService {",
+ " public void red() {",
+ " enforceRed();",
+ " }",
+ " @RequiresPermission(RED)",
+ " public void enforceRed() {",
+ " ((Context) this).enforceCallingOrSelfPermission(RED, null);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testBroadcastReceiver() {
+ compilationHelper
+ .addSourceFile("/android/annotation/RequiresPermission.java")
+ .addSourceFile("/android/content/BroadcastReceiver.java")
+ .addSourceFile("/android/content/Context.java")
+ .addSourceFile("/android/content/Intent.java")
+ .addSourceLines("ColorManager.java",
+ "import android.annotation.RequiresPermission;",
+ "import android.content.BroadcastReceiver;",
+ "import android.content.Context;",
+ "import android.content.Intent;",
+ "public abstract class ColorManager extends BroadcastReceiver {",
+ " private static final String RED = \"red\";",
+ " @RequiresPermission(RED) abstract int red();",
+ " // BUG: Diagnostic contains:",
+ " public void onSend() { red(); }",
+ " public void onReceive(Context context, Intent intent) { red(); }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ @Ignore
+ public void testContentObserver() {
+ compilationHelper
+ .addSourceFile("/android/annotation/RequiresPermission.java")
+ .addSourceFile("/android/database/ContentObserver.java")
+ .addSourceLines("ColorManager.java",
+ "import android.annotation.RequiresPermission;",
+ "import android.database.ContentObserver;",
+ "public abstract class ColorManager {",
+ " private static final String RED = \"red\";",
+ " @RequiresPermission(RED) abstract int red();",
+ " public void example() {",
+ " ContentObserver ob = new ContentObserver() {",
+ " public void onChange(boolean selfChange) {",
+ " red();",
+ " }",
+ " };",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testHandler() {
+ compilationHelper
+ .addSourceFile("/android/annotation/RequiresPermission.java")
+ .addSourceFile("/android/os/Handler.java")
+ .addSourceFile("/android/os/Message.java")
+ .addSourceLines("ColorManager.java",
+ "import android.annotation.RequiresPermission;",
+ "import android.os.Handler;",
+ "import android.os.Message;",
+ "public abstract class ColorManager extends Handler {",
+ " private static final String RED = \"red\";",
+ " @RequiresPermission(RED) abstract int red();",
+ " // BUG: Diagnostic contains:",
+ " public void sendMessage() { red(); }",
+ " public void handleMessage(Message msg) { red(); }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testDeathRecipient() {
+ compilationHelper
+ .addSourceFile("/android/annotation/RequiresPermission.java")
+ .addSourceFile("/android/os/IBinder.java")
+ .addSourceLines("ColorManager.java",
+ "import android.annotation.RequiresPermission;",
+ "import android.os.IBinder;",
+ "public abstract class ColorManager implements IBinder.DeathRecipient {",
+ " private static final String RED = \"red\";",
+ " @RequiresPermission(RED) abstract int red();",
+ " // BUG: Diagnostic contains:",
+ " public void binderAlive() { red(); }",
+ " public void binderDied() { red(); }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testClearCallingIdentity() {
+ compilationHelper
+ .addSourceFile("/android/annotation/RequiresPermission.java")
+ .addSourceFile("/android/os/Binder.java")
+ .addSourceLines("ColorManager.java",
+ "import android.annotation.RequiresPermission;",
+ "import android.os.Binder;",
+ "public abstract class ColorManager {",
+ " private static final String RED = \"red\";",
+ " private static final String BLUE = \"blue\";",
+ " @RequiresPermission(RED) abstract int red();",
+ " @RequiresPermission(BLUE) abstract int blue();",
+ " @RequiresPermission(BLUE)",
+ " public void half() {",
+ " final long token = Binder.clearCallingIdentity();",
+ " try {",
+ " red();",
+ " } finally {",
+ " Binder.restoreCallingIdentity(token);",
+ " }",
+ " blue();",
+ " }",
+ " public void full() {",
+ " final long token = Binder.clearCallingIdentity();",
+ " red();",
+ " blue();",
+ " }",
+ " @RequiresPermission(allOf={RED, BLUE})",
+ " public void none() {",
+ " red();",
+ " blue();",
+ " final long token = Binder.clearCallingIdentity();",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testSuppressLint() {
+ compilationHelper
+ .addSourceFile("/android/annotation/RequiresPermission.java")
+ .addSourceFile("/android/annotation/SuppressLint.java")
+ .addSourceLines("Example.java",
+ "import android.annotation.RequiresPermission;",
+ "import android.annotation.SuppressLint;",
+ "@SuppressLint(\"AndroidFrameworkRequiresPermission\")",
+ "abstract class Parent {",
+ " private static final String RED = \"red\";",
+ " @RequiresPermission(RED) abstract int red();",
+ "}",
+ "abstract class Child extends Parent {",
+ " private static final String BLUE = \"blue\";",
+ " @RequiresPermission(BLUE) abstract int blue();",
+ " public void toParent() { red(); }",
+ " public void toSibling() { blue(); }",
+ "}")
+ .doTest();
+ }
+}
diff --git a/errorprone/tests/res/android/annotation/RequiresPermission.java b/errorprone/tests/res/android/annotation/RequiresPermission.java
new file mode 100644
index 0000000..670eb3b
--- /dev/null
+++ b/errorprone/tests/res/android/annotation/RequiresPermission.java
@@ -0,0 +1,35 @@
+/*
+ * 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.annotation;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(SOURCE)
+@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD,PARAMETER})
+public @interface RequiresPermission {
+ String value() default "";
+ String[] allOf() default {};
+ String[] anyOf() default {};
+}
diff --git a/errorprone/tests/res/android/annotation/SuppressLint.java b/errorprone/tests/res/android/annotation/SuppressLint.java
new file mode 100644
index 0000000..4150c47
--- /dev/null
+++ b/errorprone/tests/res/android/annotation/SuppressLint.java
@@ -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 android.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
+@Retention(RetentionPolicy.CLASS)
+public @interface SuppressLint {
+ String[] value();
+}
diff --git a/telephony/java/android/telephony/data/SlicingConfig.aidl b/errorprone/tests/res/android/content/BroadcastReceiver.java
similarity index 70%
copy from telephony/java/android/telephony/data/SlicingConfig.aidl
copy to errorprone/tests/res/android/content/BroadcastReceiver.java
index ad93d8c..9d066b7 100644
--- a/telephony/java/android/telephony/data/SlicingConfig.aidl
+++ b/errorprone/tests/res/android/content/BroadcastReceiver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,10 @@
* limitations under the License.
*/
-package android.telephony.data;
+package android.content;
-parcelable SlicingConfig;
+public class BroadcastReceiver {
+ public void onReceive(Context context, Intent intent) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/errorprone/tests/res/android/content/Context.java b/errorprone/tests/res/android/content/Context.java
index 7ba3fbb..323b8dd 100644
--- a/errorprone/tests/res/android/content/Context.java
+++ b/errorprone/tests/res/android/content/Context.java
@@ -20,4 +20,7 @@
public int getUserId() {
return 0;
}
+
+ public void enforceCallingOrSelfPermission(String permission, String message) {
+ }
}
diff --git a/telephony/java/android/telephony/data/SlicingConfig.aidl b/errorprone/tests/res/android/database/ContentObserver.java
similarity index 70%
copy from telephony/java/android/telephony/data/SlicingConfig.aidl
copy to errorprone/tests/res/android/database/ContentObserver.java
index ad93d8c..4c73a10 100644
--- a/telephony/java/android/telephony/data/SlicingConfig.aidl
+++ b/errorprone/tests/res/android/database/ContentObserver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,10 @@
* limitations under the License.
*/
-package android.telephony.data;
+package android.database;
-parcelable SlicingConfig;
+public abstract class ContentObserver {
+ public void onChange(boolean selfChange) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/errorprone/tests/res/android/foo/IColorService.java b/errorprone/tests/res/android/foo/IColorService.java
new file mode 100644
index 0000000..20c8e95
--- /dev/null
+++ b/errorprone/tests/res/android/foo/IColorService.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.foo;
+
+import android.annotation.RequiresPermission;
+
+public interface IColorService extends android.os.IInterface {
+ public static final String RED = "red";
+ public static final String BLUE = "blue";
+
+ public void none();
+ @RequiresPermission(RED)
+ public void red();
+ @RequiresPermission(allOf = { RED, BLUE })
+ public void redAndBlue();
+ @RequiresPermission(anyOf = { RED, BLUE })
+ public void redOrBlue();
+}
diff --git a/telephony/java/android/telephony/data/SlicingConfig.aidl b/errorprone/tests/res/android/os/Handler.java
similarity index 73%
copy from telephony/java/android/telephony/data/SlicingConfig.aidl
copy to errorprone/tests/res/android/os/Handler.java
index ad93d8c..f001896 100644
--- a/telephony/java/android/telephony/data/SlicingConfig.aidl
+++ b/errorprone/tests/res/android/os/Handler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,10 @@
* limitations under the License.
*/
-package android.telephony.data;
+package android.os;
-parcelable SlicingConfig;
+public class Handler {
+ public void handleMessage(Message msg) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/telephony/java/android/telephony/data/SlicingConfig.aidl b/errorprone/tests/res/android/os/IBinder.java
similarity index 75%
copy from telephony/java/android/telephony/data/SlicingConfig.aidl
copy to errorprone/tests/res/android/os/IBinder.java
index ad93d8c..214a396 100644
--- a/telephony/java/android/telephony/data/SlicingConfig.aidl
+++ b/errorprone/tests/res/android/os/IBinder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,10 @@
* limitations under the License.
*/
-package android.telephony.data;
+package android.os;
-parcelable SlicingConfig;
+public interface IBinder {
+ public interface DeathRecipient {
+ public void binderDied();
+ }
+}
diff --git a/telephony/java/android/telephony/data/SlicingConfig.aidl b/errorprone/tests/res/android/os/Message.java
similarity index 83%
copy from telephony/java/android/telephony/data/SlicingConfig.aidl
copy to errorprone/tests/res/android/os/Message.java
index ad93d8c..2421263 100644
--- a/telephony/java/android/telephony/data/SlicingConfig.aidl
+++ b/errorprone/tests/res/android/os/Message.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,7 @@
* limitations under the License.
*/
-package android.telephony.data;
+package android.os;
-parcelable SlicingConfig;
+public class Message {
+}
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 6fdf552..13e2692 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -56,6 +56,7 @@
// XML constants for Font.
public static final String ATTR_INDEX = "index";
public static final String ATTR_WEIGHT = "weight";
+ public static final String ATTR_POSTSCRIPT_NAME = "postScriptName";
public static final String ATTR_STYLE = "style";
public static final String ATTR_FALLBACK_FOR = "fallbackFor";
public static final String STYLE_ITALIC = "italic";
@@ -209,6 +210,7 @@
int weight = weightStr == null ? FontStyle.FONT_WEIGHT_NORMAL : Integer.parseInt(weightStr);
boolean isItalic = STYLE_ITALIC.equals(parser.getAttributeValue(null, ATTR_STYLE));
String fallbackFor = parser.getAttributeValue(null, ATTR_FALLBACK_FOR);
+ String postScriptName = parser.getAttributeValue(null, ATTR_POSTSCRIPT_NAME);
StringBuilder filename = new StringBuilder();
while (keepReading(parser)) {
if (parser.getEventType() == XmlPullParser.TEXT) {
@@ -242,8 +244,18 @@
axes.toArray(new FontVariationAxis[0]));
}
- return new FontConfig.Font(new File(filePath),
+ File file = new File(filePath);
+
+ if (postScriptName == null) {
+ // If post script name was not provided, assume the file name is same to PostScript
+ // name.
+ String name = file.getName();
+ postScriptName = name.substring(0, name.length() - 4);
+ }
+
+ return new FontConfig.Font(file,
originalPath == null ? null : new File(originalPath),
+ postScriptName,
new FontStyle(
weight,
isItalic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index f3dba82..8f1223b 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -754,6 +754,11 @@
}
/** @hide */
+ public void setASurfaceTransactionCallback(ASurfaceTransactionCallback callback) {
+ nSetASurfaceTransactionCallback(mNativeProxy, callback);
+ }
+
+ /** @hide */
public void setFrameCallback(FrameDrawingCallback callback) {
nSetFrameCallback(mNativeProxy, callback);
}
@@ -868,6 +873,23 @@
}
/**
+ * Interface used to receive callbacks when a transaction needs to be merged.
+ *
+ * @hide
+ */
+ public interface ASurfaceTransactionCallback {
+ /**
+ * Invoked during a frame drawing.
+ *
+ * @param aSurfaceTranactionNativeObj the ASurfaceTransaction native object handle
+ * @param aSurfaceControlNativeObj ASurfaceControl native object handle
+ * @param frame The id of the frame being drawn.
+ */
+ void onMergeTransaction(long aSurfaceTranactionNativeObj,
+ long aSurfaceControlNativeObj, long frame);
+ }
+
+ /**
* Interface used to receive callbacks when a frame is being drawn.
*
* @hide
@@ -1342,6 +1364,9 @@
private static native void nSetPictureCaptureCallback(long nativeProxy,
PictureCapturedCallback callback);
+ private static native void nSetASurfaceTransactionCallback(long nativeProxy,
+ ASurfaceTransactionCallback callback);
+
private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback);
private static native void nSetFrameCompleteCallback(long nativeProxy,
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 6c03ddc..e9e58c6 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -226,10 +226,12 @@
*/
public void drawRipple(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
CanvasProperty<Float> radius, CanvasProperty<Paint> paint,
- CanvasProperty<Float> progress, RuntimeShader shader) {
+ CanvasProperty<Float> progress, CanvasProperty<Float> turbulencePhase,
+ RuntimeShader shader) {
nDrawRipple(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(),
radius.getNativeContainer(), paint.getNativeContainer(),
- progress.getNativeContainer(), shader.getNativeShaderBuilder());
+ progress.getNativeContainer(), turbulencePhase.getNativeContainer(),
+ shader.getNativeShaderBuilder());
}
/**
@@ -290,7 +292,7 @@
long propCy, long propRadius, long propPaint);
@CriticalNative
private static native void nDrawRipple(long renderer, long propCx, long propCy, long propRadius,
- long propPaint, long propProgress, long runtimeEffect);
+ long propPaint, long propProgress, long turbulencePhase, long runtimeEffect);
@CriticalNative
private static native void nDrawRoundRect(long renderer, long propLeft, long propTop,
long propRight, long propBottom, long propRx, long propRy, long propPaint);
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index c48fd8b..b88751a 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1259,20 +1259,21 @@
* @hide
*/
@TestApi
- public static @NonNull Map<String, Typeface> deserializeFontMap(@NonNull ByteBuffer buffer)
+ public static @NonNull long[] deserializeFontMap(
+ @NonNull ByteBuffer buffer, @NonNull Map<String, Typeface> out)
throws IOException {
- Map<String, Typeface> fontMap = new ArrayMap<>();
int typefacesBytesCount = buffer.getInt();
long[] nativePtrs = nativeReadTypefaces(buffer.slice());
if (nativePtrs == null) {
throw new IOException("Could not read typefaces");
}
+ out.clear();
buffer.position(buffer.position() + typefacesBytesCount);
for (long nativePtr : nativePtrs) {
String name = readString(buffer);
- fontMap.put(name, new Typeface(nativePtr));
+ out.put(name, new Typeface(nativePtr));
}
- return fontMap;
+ return nativePtrs;
}
private static String readString(ByteBuffer buffer) {
@@ -1330,7 +1331,14 @@
return;
}
sSystemFontMapBuffer = sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN);
- Map<String, Typeface> systemFontMap = deserializeFontMap(sSystemFontMapBuffer);
+ Map<String, Typeface> systemFontMap = new ArrayMap<>();
+ long[] nativePtrs = deserializeFontMap(sSystemFontMapBuffer, systemFontMap);
+
+ // Initialize native font APIs. The native font API will read fonts.xml by itself if
+ // Typeface is initialized with loadPreinstalledSystemFontMap.
+ for (long ptr : nativePtrs) {
+ nativeAddFontCollections(ptr);
+ }
setSystemFontMap(systemFontMap);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
@@ -1528,5 +1536,8 @@
private static native void nativeForceSetStaticFinalField(String fieldName, Typeface typeface);
+ @CriticalNative
+ private static native void nativeAddFontCollections(long nativePtr);
+
private static native void nativeWarmUpCache(String fileName);
}
diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
index c317831..1cd4cf1 100644
--- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java
+++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
@@ -40,6 +40,8 @@
private static final String TAG = "RippleAnimationSession";
private static final int ENTER_ANIM_DURATION = 450;
private static final int EXIT_ANIM_DURATION = 300;
+ private static final long NOISE_ANIMATION_DURATION = 7000;
+ private static final long MAX_NOISE_PHASE = NOISE_ANIMATION_DURATION / 120;
private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
private static final Interpolator FAST_OUT_SLOW_IN =
new PathInterpolator(0.4f, 0f, 0.2f, 1f);
@@ -49,7 +51,7 @@
private Runnable mOnUpdate;
private long mStartTime;
private boolean mForceSoftware;
- private boolean mAnimateSparkle;
+ private Animator mLoopAnimation;
RippleAnimationSession(@NonNull AnimationProperties<Float, Paint> properties,
boolean forceSoftware) {
@@ -88,16 +90,6 @@
return this;
}
- public boolean shouldAnimateSparkle() {
- return mAnimateSparkle && ValueAnimator.getDurationScale() > 0;
- }
-
- public float getSparklePhase() {
- final long now = AnimationUtils.currentAnimationTimeMillis();
- final long elapsed = now - mStartTime;
- return (float) elapsed / 800;
- }
-
private boolean isHwAccelerated(Canvas canvas) {
return canvas.isHardwareAccelerated() && !mForceSoftware;
}
@@ -114,7 +106,7 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- mAnimateSparkle = false;
+ if (mLoopAnimation != null) mLoopAnimation.cancel();
Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
}
@@ -148,7 +140,7 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- mAnimateSparkle = false;
+ if (mLoopAnimation != null) mLoopAnimation.cancel();
Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
}
@@ -167,24 +159,42 @@
RenderNodeAnimator expand =
new RenderNodeAnimator(props.getProgress(), .5f);
expand.setTarget(canvas);
- startAnimation(expand);
+ RenderNodeAnimator loop = new RenderNodeAnimator(props.getNoisePhase(), MAX_NOISE_PHASE);
+ loop.setTarget(canvas);
+ startAnimation(expand, loop);
}
- private void startAnimation(Animator expand) {
+ private void startAnimation(Animator expand, Animator loop) {
expand.setDuration(ENTER_ANIM_DURATION);
expand.addListener(new AnimatorListener(this));
expand.setInterpolator(FAST_OUT_SLOW_IN);
expand.start();
- mAnimateSparkle = true;
+ loop.setDuration(NOISE_ANIMATION_DURATION);
+ loop.addListener(new AnimatorListener(this) {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mLoopAnimation = null;
+ }
+ });
+ loop.setInterpolator(LINEAR_INTERPOLATOR);
+ loop.start();
+ if (mLoopAnimation != null) mLoopAnimation.cancel();
+ mLoopAnimation = loop;
}
private void enterSoftware() {
ValueAnimator expand = ValueAnimator.ofFloat(0f, 0.5f);
expand.addUpdateListener(updatedAnimation -> {
notifyUpdate();
- mProperties.getShader().setProgress((Float) expand.getAnimatedValue());
+ mProperties.getShader().setProgress((float) expand.getAnimatedValue());
});
- startAnimation(expand);
+ ValueAnimator loop = ValueAnimator.ofFloat(0f, MAX_NOISE_PHASE);
+ loop.addUpdateListener(updatedAnimation -> {
+ notifyUpdate();
+ mProperties.getShader().setNoisePhase((float) loop.getAnimatedValue());
+ });
+ startAnimation(expand, loop);
}
@NonNull AnimationProperties<Float, Paint> getProperties() {
@@ -198,6 +208,7 @@
CanvasProperty.createFloat(mProperties.getX()),
CanvasProperty.createFloat(mProperties.getY()),
CanvasProperty.createFloat(mProperties.getMaxRadius()),
+ CanvasProperty.createFloat(mProperties.getNoisePhase()),
CanvasProperty.createPaint(mProperties.getPaint()),
CanvasProperty.createFloat(mProperties.getProgress()),
mProperties.getShader());
@@ -236,16 +247,18 @@
static class AnimationProperties<FloatType, PaintType> {
private final FloatType mProgress;
private final FloatType mMaxRadius;
+ private final FloatType mNoisePhase;
private final PaintType mPaint;
private final RippleShader mShader;
private FloatType mX;
private FloatType mY;
- AnimationProperties(FloatType x, FloatType y, FloatType maxRadius,
+ AnimationProperties(FloatType x, FloatType y, FloatType maxRadius, FloatType noisePhase,
PaintType paint, FloatType progress, RippleShader shader) {
mY = y;
mX = x;
mMaxRadius = maxRadius;
+ mNoisePhase = noisePhase;
mPaint = paint;
mShader = shader;
mProgress = progress;
@@ -279,5 +292,9 @@
RippleShader getShader() {
return mShader;
}
+
+ FloatType getNoisePhase() {
+ return mNoisePhase;
+ }
}
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 0865332..29fa09d 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -150,6 +150,7 @@
/** The maximum number of ripples supported. */
private static final int MAX_RIPPLES = 10;
private static final LinearInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+ private static final int DEFAULT_EFFECT_COLOR = 0x80ffffff;
/** Temporary flag for teamfood. **/
private static final boolean FORCE_PATTERNED_STYLE = true;
@@ -465,12 +466,37 @@
*
* @attr ref android.R.styleable#RippleDrawable_color
*/
- public void setColor(ColorStateList color) {
+ public void setColor(@NonNull ColorStateList color) {
+ if (color == null) {
+ throw new IllegalArgumentException("color cannot be null");
+ }
mState.mColor = color;
invalidateSelf(false);
}
/**
+ * Sets the ripple effect color.
+ *
+ * @param color Ripple color as a color state list.
+ *
+ * @attr ref android.R.styleable#RippleDrawable_effectColor
+ */
+ public void setEffectColor(@NonNull ColorStateList color) {
+ if (color == null) {
+ throw new IllegalArgumentException("color cannot be null");
+ }
+ mState.mEffectColor = color;
+ invalidateSelf(false);
+ }
+
+ /**
+ * @return The ripple effect color as a color state list.
+ */
+ public @NonNull ColorStateList getEffectColor() {
+ return mState.mEffectColor;
+ }
+
+ /**
* Sets the radius in pixels of the fully expanded ripple.
*
* @param radius ripple radius in pixels, or {@link #RADIUS_AUTO} to
@@ -561,13 +587,14 @@
mState.mColor = color;
}
+ final ColorStateList effectColor =
+ a.getColorStateList(R.styleable.RippleDrawable_effectColor);
+ if (effectColor != null) {
+ mState.mEffectColor = effectColor;
+ }
+
mState.mMaxRadius = a.getDimensionPixelSize(
R.styleable.RippleDrawable_radius, mState.mMaxRadius);
-
- if (!FORCE_PATTERNED_STYLE) {
- mState.mRippleStyle = a.getInteger(R.styleable.RippleDrawable_rippleStyle,
- mState.mRippleStyle);
- }
}
private void verifyRequiredAttributes(@NonNull TypedArray a) throws XmlPullParserException {
@@ -858,15 +885,6 @@
}
for (int i = 0; i < mRunningAnimations.size(); i++) {
RippleAnimationSession s = mRunningAnimations.get(i);
- if (s.shouldAnimateSparkle()) {
- final float phase = s.getSparklePhase();
- if (useCanvasProps) {
- s.getCanvasProperties().getShader().setNoisePhase(phase);
- } else {
- s.getProperties().getShader().setNoisePhase(phase);
- }
- invalidateSelf();
- }
if (useCanvasProps) {
RippleAnimationSession.AnimationProperties<CanvasProperty<Float>,
CanvasProperty<Paint>>
@@ -883,7 +901,7 @@
yProp = p.getY();
}
can.drawRipple(xProp, yProp, p.getMaxRadius(), p.getPaint(),
- p.getProgress(), p.getShader());
+ p.getProgress(), p.getNoisePhase(), p.getShader());
} else {
RippleAnimationSession.AnimationProperties<Float, Paint> p =
s.getProperties();
@@ -942,10 +960,11 @@
float radius = getComputedRadius();
RippleAnimationSession.AnimationProperties<Float, Paint> properties;
RippleShader shader = new RippleShader();
- int color = mMaskColorFilter == null
+ final int color = mMaskColorFilter == null
? mState.mColor.getColorForState(getState(), Color.BLACK)
: mMaskColorFilter.getColor();
- shader.setColor(color);
+ final int effectColor = mState.mEffectColor.getColorForState(getState(), Color.MAGENTA);
+ shader.setColor(color, effectColor);
shader.setOrigin(cx, cy);
shader.setTouch(x, y);
shader.setResolution(w, h, mState.mDensity);
@@ -953,7 +972,7 @@
shader.setRadius(radius);
shader.setProgress(.0f);
properties = new RippleAnimationSession.AnimationProperties<>(
- cx, cy, radius, p, 0f, shader);
+ cx, cy, radius, 0f, p, 0f, shader);
if (mMaskShader == null) {
shader.setShader(null);
} else {
@@ -1266,33 +1285,6 @@
return this;
}
- /**
- * Sets the visual style of the ripple.
- *
- * @see #STYLE_SOLID
- * @see #STYLE_PATTERNED
- *
- * @param style The style of the ripple
- * @hide
- */
- public void setRippleStyle(@RippleStyle int style) throws IllegalArgumentException {
- if (style == STYLE_SOLID || style == STYLE_PATTERNED) {
- mState.mRippleStyle = style;
- } else {
- throw new IllegalArgumentException("Invalid style value " + style);
- }
- }
-
- /**
- * Get the current ripple style
- * @return Ripple style
- * @hide
- */
- public @RippleStyle int getRippleStyle() {
- return mState.mRippleStyle;
- }
-
-
@Override
RippleState createConstantState(LayerState state, Resources res) {
return new RippleState(state, this, res);
@@ -1302,6 +1294,7 @@
int[] mTouchThemeAttrs;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
ColorStateList mColor = ColorStateList.valueOf(Color.MAGENTA);
+ ColorStateList mEffectColor = ColorStateList.valueOf(DEFAULT_EFFECT_COLOR);
int mMaxRadius = RADIUS_AUTO;
int mRippleStyle = FORCE_PATTERNED_STYLE ? STYLE_PATTERNED : STYLE_SOLID;
@@ -1314,6 +1307,7 @@
mColor = origs.mColor;
mMaxRadius = origs.mMaxRadius;
mRippleStyle = origs.mRippleStyle;
+ mEffectColor = origs.mEffectColor;
if (origs.mDensity != mDensity) {
applyDensityScaling(orig.mDensity, mDensity);
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index 4608d02..a0aa41e 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -39,6 +39,7 @@
+ "uniform vec2 in_tRotation2;\n"
+ "uniform vec2 in_tRotation3;\n"
+ "uniform vec4 in_color;\n"
+ + "uniform vec4 in_sparkleColor;\n"
+ "uniform shader in_shader;\n";
private static final String SHADER_LIB =
"float triangleNoise(vec2 n) {\n"
@@ -48,7 +49,6 @@
+ " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n"
+ "}"
+ "const float PI = 3.1415926535897932384626;\n"
- + "const float SPARKLE_OPACITY = 0.75;\n"
+ "\n"
+ "float sparkles(vec2 uv, float t) {\n"
+ " float n = triangleNoise(uv);\n"
@@ -60,7 +60,7 @@
+ " o *= abs(sin(PI * o * (t + 0.55 * i)));\n"
+ " s += o;\n"
+ " }\n"
- + " return saturate(s) * SPARKLE_OPACITY;\n"
+ + " return saturate(s) * in_sparkleColor.a;\n"
+ "}\n"
+ "float softCircle(vec2 uv, vec2 xy, float radius, float blur) {\n"
+ " float blurHalf = blur * 0.5;\n"
@@ -116,7 +116,7 @@
+ " vec4 circle = in_color * (softCircle(p, center, in_maxRadius "
+ " * scaleIn, 0.2) * fade);\n"
+ " float mask = in_hasMask == 1. ? sample(in_shader).a > 0. ? 1. : 0. : 1.;\n"
- + " return mix(circle, vec4(sparkle), sparkle) * mask;\n"
+ + " return mix(circle, in_sparkleColor, sparkle) * mask;\n"
+ "}";
private static final String SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN;
private static final double PI_ROTATE_RIGHT = Math.PI * 0.0078125;
@@ -167,6 +167,9 @@
final float turbulencePhase = (float) ((mProgress + mNoisePhase * 0.333f) * 5f * Math.PI);
setUniform("in_turbulencePhase", turbulencePhase);
+ //
+ // Keep in sync with: frameworks/base/libs/hwui/pipeline/skia/AnimatedDrawables.h
+ //
final float scale = 1.5f;
setUniform("in_tCircle1", new float[]{
(float) (scale * 0.5 + (turbulencePhase * 0.01 * Math.cos(scale * 0.55))),
@@ -197,10 +200,13 @@
/**
* Color of the circle that's under the sparkles. Sparkles will always be white.
*/
- public void setColor(@ColorInt int colorIn) {
- Color color = Color.valueOf(colorIn);
- this.setUniform("in_color", new float[] {color.red(),
+ public void setColor(@ColorInt int colorInt, @ColorInt int sparkleColorInt) {
+ Color color = Color.valueOf(colorInt);
+ Color sparkleColor = Color.valueOf(sparkleColorInt);
+ setUniform("in_color", new float[] {color.red(),
color.green(), color.blue(), color.alpha()});
+ setUniform("in_sparkleColor", new float[] {sparkleColor.red(),
+ sparkleColor.green(), sparkleColor.blue(), sparkleColor.alpha()});
}
public void setResolution(float w, float h, int density) {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index ecb082e..62fe54f 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -62,10 +62,8 @@
*/
@UnsupportedAppUsage
public static long getKeyStoreOperationHandle(Object cryptoPrimitive) {
- if (cryptoPrimitive == null) {
- throw new NullPointerException();
- }
- return 0;
+ return android.security.keystore2.AndroidKeyStoreProvider
+ .getKeyStoreOperationHandle(cryptoPrimitive);
}
/**
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index f3baad7..2207e62 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -43,10 +43,10 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Superior 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utilizar el modo una mano"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Usar Modo una mano"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para salir, desliza el dedo hacia arriba desde la parte inferior de la pantalla o toca cualquier zona que haya encima de la aplicación"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar modo una mano"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del modo una mano"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar Modo una mano"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del Modo una mano"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Ajustes de las burbujas de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menú adicional"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Volver a añadir a la pila"</string>
@@ -70,5 +70,5 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
<string name="restart_button_description" msgid="5887656107651190519">"Toca para reiniciar esta aplicación e ir a la pantalla completa."</string>
- <string name="got_it" msgid="4428750913636945527">"Listo"</string>
+ <string name="got_it" msgid="4428750913636945527">"Entendido"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index fed3ea9..a17f543 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -62,10 +62,10 @@
<string name="bubbles_user_education_title" msgid="2112319053732691899">"گپ بااستفاده از حبابکها"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"مکالمههای جدید بهصورت نمادهای شناور یا حبابکها نشان داده میشوند. برای باز کردن حبابکها ضربه بزنید. برای جابهجایی، آن را بکشید."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کنترل حبابکها در هرزمانی"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن «حبابکها» از این برنامه، روی «مدیریت» ضربه بزنید"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن حبابکها از این برنامه، روی «مدیریت» ضربه بزنید"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"متوجهام"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"هیچ حبابک جدیدی وجود ندارد"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابکها اخیر و حبابکها ردشده اینجا ظاهر خواهند شد"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابکهای اخیر و حبابکهای ردشده اینجا ظاهر خواهند شد"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index a969386..b2c0055 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -67,7 +67,7 @@
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हाल ही के बबल्स मौजूद नहीं हैं"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हाल ही के बबल्स और हटाए गए बबल्स यहां दिखेंगे"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
- <string name="manage_bubbles_text" msgid="7730624269650594419">"प्रबंधित करें"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"मैनेज करें"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
<string name="restart_button_description" msgid="5887656107651190519">"इस ऐप्लिकेशन को रीस्टार्ट करने और फ़ुल स्क्रीन पर देखने के लिए टैप करें."</string>
<string name="got_it" msgid="4428750913636945527">"ठीक है"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
index 8ca54e0..ef98a9c 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -19,6 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"תמונה בתוך תמונה"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(תוכנית ללא כותרת)"</string>
- <string name="pip_close" msgid="9135220303720555525">"סגור PIP"</string>
+ <string name="pip_close" msgid="9135220303720555525">"סגירת PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 530d40a..0c64c76 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -62,7 +62,7 @@
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн таптап коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Калкып чыкма билдирмелерди каалаган убакта көзөмөлдөңүз"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн, \"Башкарууну\" басыңыз"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн \"Башкарууну\" басыңыз"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түшүндүм"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Азырынча эч нерсе жок"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Акыркы жана жабылган калкып чыкма билдирмелер ушул жерде көрүнөт"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 882ac37..dfa364a 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -30,7 +30,7 @@
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदल्नुहोस्"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"एप विभाजित स्क्रिनमा काम नगर्न सक्छ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अनुप्रयोगले विभाजित-स्क्रिनलाई समर्थन गर्दैन।"</string>
- <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो अनुप्रयोगले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो एपले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"अनुप्रयोगले सहायक प्रदर्शनहरूमा लञ्च सुविधालाई समर्थन गर्दैन।"</string>
<string name="accessibility_divider" msgid="703810061635792791">"विभाजित-स्क्रिन छुट्याउने"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"बायाँ भाग फुल स्क्रिन"</string>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 6698a01..a138fee 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -40,7 +40,7 @@
<integer name="long_press_dock_anim_duration">250</integer>
<!-- Animation duration for translating of one handed when trigger / dismiss. -->
- <integer name="config_one_handed_translate_animation_duration">300</integer>
+ <integer name="config_one_handed_translate_animation_duration">800</integer>
<!-- One handed mode default offset % of display size -->
<fraction name="config_one_handed_offset">40%</fraction>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 11c1464..dca5985 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -739,14 +739,11 @@
return (isSummary && isSuppressedSummary) || isSuppressedBubble;
}
- private void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback,
- Executor callbackExecutor) {
+ private void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback) {
if (mBubbleData.isSummarySuppressed(groupKey)) {
mBubbleData.removeSuppressedSummary(groupKey);
if (callback != null) {
- callbackExecutor.execute(() -> {
- callback.accept(mBubbleData.getSummaryKey(groupKey));
- });
+ callback.accept(mBubbleData.getSummaryKey(groupKey));
}
}
}
@@ -1298,8 +1295,10 @@
public void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback,
Executor callbackExecutor) {
mMainExecutor.execute(() -> {
- BubbleController.this.removeSuppressedSummaryIfNecessary(groupKey, callback,
- callbackExecutor);
+ Consumer<String> cb = callback != null
+ ? (key) -> callbackExecutor.execute(() -> callback.accept(key))
+ : null;
+ BubbleController.this.removeSuppressedSummaryIfNecessary(groupKey, cb);
});
}
@@ -1340,10 +1339,13 @@
@Override
public boolean handleDismissalInterception(BubbleEntry entry,
- @Nullable List<BubbleEntry> children, IntConsumer removeCallback) {
+ @Nullable List<BubbleEntry> children, IntConsumer removeCallback,
+ Executor callbackExecutor) {
+ IntConsumer cb = removeCallback != null
+ ? (index) -> callbackExecutor.execute(() -> removeCallback.accept(index))
+ : null;
return mMainExecutor.executeBlockingForResult(() -> {
- return BubbleController.this.handleDismissalInterception(entry, children,
- removeCallback);
+ return BubbleController.this.handleDismissalInterception(entry, children, cb);
}, Boolean.class);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 9fc8aef..1bfb619 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -140,7 +140,7 @@
* @return true if we want to intercept the dismissal of the entry, else false.
*/
boolean handleDismissalInterception(BubbleEntry entry, @Nullable List<BubbleEntry> children,
- IntConsumer removeCallback);
+ IntConsumer removeCallback, Executor callbackExecutor);
/** Set the proxy to commnuicate with SysUi side components. */
void setSysuiProxy(SysuiProxy proxy);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
index 125e322..25dd3ca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
@@ -22,8 +22,7 @@
import android.content.Context;
import android.graphics.Rect;
import android.view.SurfaceControl;
-import android.view.animation.Interpolator;
-import android.view.animation.OvershootInterpolator;
+import android.view.animation.BaseInterpolator;
import android.window.WindowContainerToken;
import androidx.annotation.VisibleForTesting;
@@ -54,7 +53,7 @@
public @interface TransitionDirection {
}
- private final Interpolator mOvershootInterpolator;
+ private final OneHandedInterpolator mInterpolator;
private final OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper;
private final HashMap<WindowContainerToken, OneHandedTransitionAnimator> mAnimatorMap =
new HashMap<>();
@@ -64,7 +63,7 @@
*/
public OneHandedAnimationController(Context context) {
mSurfaceTransactionHelper = new OneHandedSurfaceTransactionHelper(context);
- mOvershootInterpolator = new OvershootInterpolator();
+ mInterpolator = new OneHandedInterpolator();
}
@SuppressWarnings("unchecked")
@@ -102,7 +101,7 @@
OneHandedTransitionAnimator setupOneHandedTransitionAnimator(
OneHandedTransitionAnimator animator) {
animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
- animator.setInterpolator(mOvershootInterpolator);
+ animator.setInterpolator(mInterpolator);
animator.setFloatValues(FRACTION_START, FRACTION_END);
return animator;
}
@@ -112,6 +111,8 @@
*
* @param <T> Type of property to animate, either offset (float)
*/
+ // TODO: Refactoring to use SpringAnimation and DynamicAnimation instead of using ValueAnimator
+ // to implement One-Handed transition animation. (b/185129031)
public abstract static class OneHandedTransitionAnimator<T> extends ValueAnimator implements
ValueAnimator.AnimatorUpdateListener,
ValueAnimator.AnimatorListener {
@@ -297,4 +298,15 @@
};
}
}
+
+ /**
+ * An Interpolator for One-Handed transition animation.
+ */
+ public class OneHandedInterpolator extends BaseInterpolator {
+ @Override
+ public float getInterpolation(float input) {
+ return (float) (Math.pow(2, -10 * input) * Math.sin(((input - 4.0f) / 4.0f)
+ * (2.0f * Math.PI) / 4.0f) + 1);
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 19098fd..625f4b8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -29,6 +29,7 @@
import android.content.om.OverlayInfo;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -61,7 +62,8 @@
/**
* Manages and manipulates the one handed states, transitions, and gesture for phones.
*/
-public class OneHandedController implements RemoteCallable<OneHandedController> {
+public class OneHandedController implements RemoteCallable<OneHandedController>,
+ OneHandedTransitionCallback {
private static final String TAG = "OneHandedController";
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
@@ -73,6 +75,7 @@
private volatile boolean mIsOneHandedEnabled;
private volatile boolean mIsSwipeToNotificationEnabled;
+ private volatile boolean mIsTransitioning;
private boolean mTaskChangeToExit;
private boolean mLockedDisabled;
private int mUserId;
@@ -327,7 +330,7 @@
@VisibleForTesting
void startOneHanded() {
- if (isLockedDisabled()) {
+ if (isLockedDisabled() || mIsTransitioning) {
Slog.d(TAG, "Temporary lock disabled");
return;
}
@@ -337,13 +340,13 @@
return;
}
if (!mDisplayAreaOrganizer.isInOneHanded()) {
+ mIsTransitioning = true;
final int yOffSet = Math.round(
mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
mOneHandedAccessibilityUtil.announcementForScreenReader(
mOneHandedAccessibilityUtil.getOneHandedStartDescription());
mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
mTimeoutHandler.resetTimer();
-
mOneHandedUiEventLogger.writeEvent(
OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN);
}
@@ -351,16 +354,12 @@
@VisibleForTesting
void stopOneHanded() {
- if (mDisplayAreaOrganizer.isInOneHanded()) {
- mOneHandedAccessibilityUtil.announcementForScreenReader(
- mOneHandedAccessibilityUtil.getOneHandedStopDescription());
- mDisplayAreaOrganizer.scheduleOffset(0, 0);
- mTimeoutHandler.removeTimer();
- }
+ stopOneHanded(OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
}
private void stopOneHanded(int uiEvent) {
- if (mDisplayAreaOrganizer.isInOneHanded()) {
+ if (mDisplayAreaOrganizer.isInOneHanded() && !mIsTransitioning) {
+ mIsTransitioning = true;
mOneHandedAccessibilityUtil.announcementForScreenReader(
mOneHandedAccessibilityUtil.getOneHandedStopDescription());
mDisplayAreaOrganizer.scheduleOffset(0, 0);
@@ -389,6 +388,7 @@
mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler);
mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler);
mDisplayAreaOrganizer.registerTransitionCallback(mBackgroundPanelOrganizer);
+ mDisplayAreaOrganizer.registerTransitionCallback(this);
if (mTaskChangeToExit) {
mTaskStackListener.addListener(mTaskStackListenerCallback);
}
@@ -615,6 +615,8 @@
pw.println(mLockedDisabled);
pw.print(innerPrefix + "mUserId=");
pw.println(mUserId);
+ pw.print(innerPrefix + "mIsTransitioning=");
+ pw.println(mIsTransitioning);
if (mBackgroundPanelOrganizer != null) {
mBackgroundPanelOrganizer.dump(pw);
@@ -661,6 +663,26 @@
}
/**
+ * TODO(b/185558765) To implement a state machine for One-Handed transition state machine.
+ * ONE_HANDDE_STATE_TRANSITION {
+ * STATE_DEFAULT,
+ * STATE_TRANSITIONING,
+ * STATE_ENTER_ONE_HANED,
+ * STATE_EXIT_ONE_HANDED
+ * }
+ * and we need to align the state to launcher3 quick steps through SysuiProxy.
+ */
+ @Override
+ public void onStartFinished(Rect bounds) {
+ mIsTransitioning = false;
+ }
+
+ @Override
+ public void onStopFinished(Rect bounds) {
+ mIsTransitioning = false;
+ }
+
+ /**
* The interface for calls from outside the Shell, within the host process.
*/
@ExternalThread
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
index d4f229c..3af0ff0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
@@ -216,7 +216,7 @@
}
ArrayList<RemoteAction> mediaActions = new ArrayList<>();
- boolean isPlaying = mMediaController.getPlaybackState().isActive();
+ boolean isPlaying = mMediaController.getPlaybackState().isActiveState();
long actions = mMediaController.getPlaybackState().getActions();
// Prev action
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
index a474831..dd7e294 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
@@ -50,6 +50,7 @@
// Referenced in com.android.systemui.util.NotificationChannels.
public static final String NOTIFICATION_CHANNEL = "TVPIP";
private static final String NOTIFICATION_TAG = "TvPip";
+ private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF";
private static final String ACTION_SHOW_PIP_MENU =
"com.android.wm.shell.pip.tv.notification.action.SHOW_PIP_MENU";
@@ -207,7 +208,8 @@
}
private static PendingIntent createPendingIntent(Context context, String action) {
- return PendingIntent.getBroadcast(context, 0, new Intent(action),
+ return PendingIntent.getBroadcast(context, 0,
+ new Intent(action).setPackage(context.getPackageName()),
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
}
@@ -223,7 +225,7 @@
void register() {
if (mRegistered) return;
- mContext.registerReceiverForAllUsers(this, mIntentFilter, null /* permission */,
+ mContext.registerReceiverForAllUsers(this, mIntentFilter, SYSTEMUI_PERMISSION,
mMainHandler);
mRegistered = true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 516dfd0..147f2e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.startingsurface;
+import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.os.UserHandle.getUserHandleForUid;
@@ -35,6 +36,8 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Trace;
import android.util.Slog;
import android.view.SurfaceControl;
@@ -48,10 +51,11 @@
import com.android.wm.shell.common.TransactionPool;
import java.util.List;
+import java.util.function.Consumer;
/**
* Util class to create the view for a splash screen content.
- *
+ * Everything execute in this class should be post to mSplashscreenWorkerHandler.
* @hide
*/
public class SplashscreenContentDrawer {
@@ -78,6 +82,7 @@
private int mIconEarlyExitDistance;
private final TransactionPool mTransactionPool;
private final SplashScreenWindowAttrs mTmpAttrs = new SplashScreenWindowAttrs();
+ private final Handler mSplashscreenWorkerHandler;
SplashscreenContentDrawer(Context context, int maxAnimatableIconDuration,
int iconExitAnimDuration, int appRevealAnimDuration, TransactionPool pool) {
@@ -87,6 +92,45 @@
mAppRevealDuration = appRevealAnimDuration;
mIconExitDuration = iconExitAnimDuration;
mTransactionPool = pool;
+
+ // Initialize Splashscreen worker thread
+ // TODO(b/185288910) move it into WMShellConcurrencyModule and provide an executor to make
+ // it easier to test stuff that happens on that thread later.
+ final HandlerThread shellSplashscreenWorkerThread =
+ new HandlerThread("wmshell.splashworker", THREAD_PRIORITY_TOP_APP_BOOST);
+ shellSplashscreenWorkerThread.start();
+ mSplashscreenWorkerHandler = shellSplashscreenWorkerThread.getThreadHandler();
+ }
+
+ /**
+ * Create a SplashScreenView object.
+ *
+ * In order to speed up the splash screen view to show on first frame, preparing the
+ * view on background thread so the view and the drawable can be create and pre-draw in
+ * parallel.
+ *
+ * @param consumer Receiving the SplashScreenView object, which will also be executed
+ * on splash screen thread. Note that the view can be null if failed.
+ */
+ void createContentView(Context context, int splashScreenResId, ActivityInfo info,
+ int taskId, Consumer<SplashScreenView> consumer) {
+ mSplashscreenWorkerHandler.post(() -> {
+ SplashScreenView contentView;
+ try {
+ contentView = SplashscreenContentDrawer.makeSplashscreenContent(
+ context, splashScreenResId);
+ if (contentView == null) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "makeSplashScreenContentView");
+ contentView = makeSplashScreenContentView(context, info);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "failed creating starting window content at taskId: "
+ + taskId, e);
+ contentView = null;
+ }
+ consumer.accept(contentView);
+ });
}
private void updateDensity() {
@@ -146,7 +190,7 @@
}
}
- SplashScreenView makeSplashScreenContentView(Context context, ActivityInfo ai) {
+ private SplashScreenView makeSplashScreenContentView(Context context, ActivityInfo ai) {
updateDensity();
getWindowAttrs(context, mTmpAttrs);
@@ -199,7 +243,7 @@
}
}
- static class SplashScreenWindowAttrs {
+ private static class SplashScreenWindowAttrs {
private int mWindowBgResId = 0;
private int mWindowBgColor = Color.TRANSPARENT;
private Drawable mReplaceIcon = null;
@@ -271,9 +315,7 @@
if (DEBUG) {
Slog.d(TAG, "The icon is not an AdaptiveIconDrawable");
}
- mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable(
- mIconBackground != Color.TRANSPARENT
- ? mIconBackground : mThemeColor, mIconDrawable, mIconSize);
+ createIconDrawable(mIconDrawable, mIconSize);
}
final int iconSize = mFinalIconDrawable != null ? (int) (mIconSize * mScale) : 0;
mCachedResult = fillViewWithIcon(mContext, iconSize, mFinalIconDrawable);
@@ -283,8 +325,8 @@
private void createIconDrawable(Drawable iconDrawable, int iconSize) {
mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable(
- mIconBackground != Color.TRANSPARENT
- ? mIconBackground : mThemeColor, iconDrawable, iconSize);
+ mIconBackground != Color.TRANSPARENT ? mIconBackground : mThemeColor,
+ iconDrawable, iconSize, mSplashscreenWorkerHandler);
}
private boolean processAdaptiveIcon() {
@@ -399,7 +441,7 @@
return root < 0.1;
}
- static SplashScreenView makeSplashscreenContent(Context ctx,
+ private static SplashScreenView makeSplashscreenContent(Context ctx,
int splashscreenContentResId) {
// doesn't support windowSplashscreenContent after S
// TODO add an allowlist to skip some packages if needed
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index 4196d68..85845d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -37,6 +37,7 @@
import android.graphics.drawable.Animatable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
import android.os.Trace;
import android.util.PathParser;
import android.window.SplashScreenView;
@@ -50,42 +51,61 @@
public class SplashscreenIconDrawableFactory {
static Drawable makeIconDrawable(@ColorInt int backgroundColor,
- @NonNull Drawable foregroundDrawable, int iconSize) {
+ @NonNull Drawable foregroundDrawable, int iconSize,
+ Handler splashscreenWorkerHandler) {
if (foregroundDrawable instanceof Animatable) {
return new AnimatableIconDrawable(backgroundColor, foregroundDrawable);
} else if (foregroundDrawable instanceof AdaptiveIconDrawable) {
- return new ImmobileIconDrawable((AdaptiveIconDrawable) foregroundDrawable, iconSize);
+ return new ImmobileIconDrawable((AdaptiveIconDrawable) foregroundDrawable, iconSize,
+ splashscreenWorkerHandler);
} else {
return new ImmobileIconDrawable(new AdaptiveIconDrawable(
- new ColorDrawable(backgroundColor), foregroundDrawable), iconSize);
+ new ColorDrawable(backgroundColor), foregroundDrawable), iconSize,
+ splashscreenWorkerHandler);
}
}
private static class ImmobileIconDrawable extends Drawable {
- private Shader mLayersShader;
+ private boolean mCacheComplete;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG
| Paint.FILTER_BITMAP_FLAG);
- ImmobileIconDrawable(AdaptiveIconDrawable drawable, int iconSize) {
- cachePaint(drawable, iconSize, iconSize);
+ ImmobileIconDrawable(AdaptiveIconDrawable drawable, int iconSize,
+ Handler splashscreenWorkerHandler) {
+ splashscreenWorkerHandler.post(() -> cachePaint(drawable, iconSize, iconSize));
}
private void cachePaint(AdaptiveIconDrawable drawable, int width, int height) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "cachePaint");
- final Bitmap layersBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(layersBitmap);
- drawable.setBounds(0, 0, width, height);
- drawable.draw(canvas);
- mLayersShader = new BitmapShader(layersBitmap, Shader.TileMode.CLAMP,
- Shader.TileMode.CLAMP);
- mPaint.setShader(mLayersShader);
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ synchronized (mPaint) {
+ if (mCacheComplete) {
+ return;
+ }
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "cachePaint");
+ final Bitmap layersBitmap = Bitmap.createBitmap(width, height,
+ Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(layersBitmap);
+ drawable.setBounds(0, 0, width, height);
+ drawable.draw(canvas);
+ final Shader layersShader = new BitmapShader(layersBitmap, Shader.TileMode.CLAMP,
+ Shader.TileMode.CLAMP);
+ mPaint.setShader(layersShader);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ mCacheComplete = true;
+ }
}
@Override
public void draw(Canvas canvas) {
- final Rect bounds = getBounds();
- canvas.drawRect(bounds, mPaint);
+ synchronized (mPaint) {
+ if (mCacheComplete) {
+ final Rect bounds = getBounds();
+ canvas.drawRect(bounds, mPaint);
+ } else {
+ // this shouldn't happen, but if it really happen, invalidate self to wait
+ // for cachePaint finish.
+ invalidateSelf();
+ }
+ }
}
@Override
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 e95135a..7037d18 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
@@ -18,8 +18,10 @@
import static android.content.Context.CONTEXT_RESTRICTED;
import static android.content.res.Configuration.EMPTY;
+import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION;
import static android.view.Display.DEFAULT_DISPLAY;
+import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
@@ -29,16 +31,15 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.Choreographer;
import android.view.Display;
import android.view.SurfaceControl;
import android.view.View;
@@ -54,9 +55,44 @@
import com.android.wm.shell.common.TransactionPool;
import java.util.function.Consumer;
+import java.util.function.Supplier;
/**
* A class which able to draw splash screen or snapshot as the starting window for a task.
+ *
+ * In order to speed up, there will use two threads to creating a splash screen in parallel.
+ * Right now we are still using PhoneWindow to create splash screen window, so the view is added to
+ * the ViewRootImpl, and those view won't be draw immediately because the ViewRootImpl will call
+ * scheduleTraversal to register a callback from Choreographer, so the drawing result of the view
+ * can synchronize on each frame.
+ *
+ * The bad thing is that we cannot decide when would Choreographer#doFrame happen, and drawing
+ * the AdaptiveIconDrawable object can be time consuming, so we use the splash-screen background
+ * thread to draw the AdaptiveIconDrawable object to a Bitmap and cache it to a BitmapShader after
+ * the SplashScreenView just created, once we get the BitmapShader then the #draw call can be very
+ * quickly.
+ *
+ * So basically we are using the spare time to prepare the SplashScreenView while splash screen
+ * thread is waiting for
+ * 1. WindowManager#addView(binder call to WM),
+ * 2. Choreographer#doFrame happen(uncertain time for next frame, depends on device),
+ * 3. Session#relayout(another binder call to WM which under Choreographer#doFrame, but will
+ * always happen before #draw).
+ * Because above steps are running on splash-screen thread, so pre-draw the BitmapShader on
+ * splash-screen background tread can make they execute in parallel, which ensure it is faster then
+ * to draw the AdaptiveIconDrawable when receive callback from Choreographer#doFrame.
+ *
+ * Here is the sequence to compare the difference between using single and two thread.
+ *
+ * Single thread:
+ * => makeSplashScreenContentView -> WM#addView .. waiting for Choreographer#doFrame -> relayout
+ * -> draw -> AdaptiveIconDrawable#draw
+ *
+ * Two threads:
+ * => makeSplashScreenContentView -> cachePaint(=AdaptiveIconDrawable#draw)
+ * => WM#addView -> .. waiting for Choreographer#doFrame -> relayout -> draw -> (draw the Paint
+ * directly).
+ *
* @hide
*/
public class StartingSurfaceDrawer {
@@ -68,7 +104,11 @@
private final DisplayManager mDisplayManager;
private final ShellExecutor mSplashScreenExecutor;
private final SplashscreenContentDrawer mSplashscreenContentDrawer;
+ private Choreographer mChoreographer;
+ /**
+ * @param splashScreenExecutor The thread used to control add and remove starting window.
+ */
public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor,
TransactionPool pool) {
mContext = context;
@@ -82,6 +122,7 @@
com.android.wm.shell.R.integer.starting_window_app_reveal_anim_duration);
mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext,
maxAnimatableIconDuration, iconExitAnimDuration, appRevealAnimDuration, pool);
+ mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
}
private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
@@ -267,36 +308,91 @@
params.setTitle("Splash Screen " + activityInfo.packageName);
// TODO(b/173975965) tracking performance
- SplashScreenView sView = null;
+ // Prepare the splash screen content view on splash screen worker thread in parallel, so the
+ // content view won't be blocked by binder call like addWindow and relayout.
+ // 1. Trigger splash screen worker thread to create SplashScreenView before/while
+ // Session#addWindow.
+ // 2. Synchronize the SplashscreenView to splash screen thread before Choreographer start
+ // traversal, which will call Session#relayout on splash screen thread.
+ // 3. Pre-draw the BitmapShader if the icon is immobile on splash screen worker thread, at
+ // the same time the splash screen thread should be executing Session#relayout. Blocking the
+ // traversal -> draw on splash screen thread until the BitmapShader of the icon is ready.
+ final Runnable setViewSynchronized;
+ if (!emptyView) {
+ // Record whether create splash screen view success, notify to current thread after
+ // create splash screen view finished.
+ final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier();
+ setViewSynchronized = () -> {
+ // waiting for setContentView before relayoutWindow
+ SplashScreenView contentView = viewSupplier.get();
+ final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
+ // if record == null, either the starting window added fail or removed already.
+ if (record != null) {
+ // if view == null then creation of content view was failed.
+ if (contentView != null) {
+ try {
+ win.setContentView(contentView);
+ contentView.cacheRootWindow(win);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "failed set content view to starting window "
+ + "at taskId: " + taskId, e);
+ contentView = null;
+ }
+ }
+ record.setSplashScreenView(contentView);
+ }
+ };
+ mSplashscreenContentDrawer.createContentView(context,
+ splashscreenContentResId[0], activityInfo, taskId, viewSupplier::setView);
+ } else {
+ setViewSynchronized = null;
+ }
+
try {
final View view = win.getDecorView();
final WindowManager wm = mContext.getSystemService(WindowManager.class);
- if (!emptyView) {
- // splash screen content will be deprecated after S.
- sView = SplashscreenContentDrawer.makeSplashscreenContent(
- context, splashscreenContentResId[0]);
- final boolean splashscreenContentCompatible = sView != null;
- if (splashscreenContentCompatible) {
- win.setContentView(sView);
- } else {
- sView = mSplashscreenContentDrawer
- .makeSplashScreenContentView(context, activityInfo);
- win.setContentView(sView);
- win.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
- sView.cacheRootWindow(win);
- }
- }
postAddWindow(taskId, appToken, view, wm, params);
+
+ // all done
+ if (emptyView) {
+ return;
+ }
+ // We use the splash screen worker thread to create SplashScreenView while adding the
+ // window, as otherwise Choreographer#doFrame might be delayed on this thread.
+ // And since Choreographer#doFrame won't happen immediately after adding the window, if
+ // the view is not added to the PhoneWindow on the first #doFrame, the view will not be
+ // rendered on the first frame. So here we need to synchronize the view on the window
+ // before first round relayoutWindow, which will happen after insets animation.
+ mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null);
} catch (RuntimeException e) {
// don't crash if something else bad happens, for example a
// failure loading resources because we are loading from an app
// on external storage that has been unmounted.
- Slog.w(TAG, " failed creating starting window at taskId: " + taskId, e);
- sView = null;
- } finally {
- final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
- if (record != null) {
- record.setSplashScreenView(sView);
+ Slog.w(TAG, "failed creating starting window at taskId: " + taskId, e);
+ }
+ }
+
+ private static class SplashScreenViewSupplier implements Supplier<SplashScreenView> {
+ private SplashScreenView mView;
+ private boolean mIsViewSet;
+ void setView(SplashScreenView view) {
+ synchronized (this) {
+ mView = view;
+ mIsViewSet = true;
+ notify();
+ }
+ }
+
+ @Override
+ public @Nullable SplashScreenView get() {
+ synchronized (this) {
+ while (!mIsViewSet) {
+ try {
+ wait();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ return mView;
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index 8a629bc..e336287 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.startingsurface;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
@@ -30,11 +31,11 @@
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityTaskManager;
import android.content.Context;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.Slog;
import android.view.SurfaceControl;
import android.window.StartingWindowInfo;
@@ -109,17 +110,9 @@
}
private static class StartingTypeChecker {
- TaskSnapshot mSnapshot;
-
- StartingTypeChecker() { }
-
- private void reset() {
- mSnapshot = null;
- }
private @StartingWindowInfo.StartingWindowType int
estimateStartingWindowType(StartingWindowInfo windowInfo) {
- reset();
final int parameter = windowInfo.startingWindowTypeParameter;
final boolean newTask = (parameter & TYPE_PARAMETER_NEW_TASK) != 0;
final boolean taskSwitch = (parameter & TYPE_PARAMETER_TASK_SWITCH) != 0;
@@ -159,7 +152,7 @@
}
}
if (taskSwitch && allowTaskSnapshot) {
- final TaskSnapshot snapshot = getTaskSnapshot(windowInfo.taskInfo.taskId);
+ final TaskSnapshot snapshot = windowInfo.mTaskSnapshot;
if (isSnapshotCompatible(windowInfo, snapshot)) {
return STARTING_WINDOW_TYPE_SNAPSHOT;
}
@@ -198,20 +191,6 @@
}
return taskRotation == snapshotRotation;
}
-
- private TaskSnapshot getTaskSnapshot(int taskId) {
- if (mSnapshot != null) {
- return mSnapshot;
- }
- try {
- mSnapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId,
- false/* isLowResolution */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to get snapshot for task: " + taskId + ", from: " + e);
- return null;
- }
- return mSnapshot;
- }
}
/*
@@ -232,7 +211,9 @@
*/
public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
mSplashScreenExecutor.execute(() -> {
- final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(windowInfo);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addStartingWindow");
+ final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(
+ windowInfo);
final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;
if (mTaskLaunchingCallback != null && shouldSendToListener(suggestionType)) {
mTaskLaunchingCallback.accept(runningTaskInfo.taskId, suggestionType);
@@ -244,10 +225,12 @@
mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken,
true /* emptyView */);
} else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {
- final TaskSnapshot snapshot = mStartingTypeChecker.mSnapshot;
- mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot);
+ final TaskSnapshot snapshot = windowInfo.mTaskSnapshot;
+ mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken,
+ snapshot);
}
// If prefer don't show, then don't show!
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 6e43741..e9ce2ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.graphics.Color.WHITE;
import static android.graphics.Color.alpha;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -60,6 +61,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.view.IWindowSession;
@@ -222,8 +224,10 @@
final InputChannel tmpInputChannel = new InputChannel();
mainExecutor.execute(() -> {
try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#addToDisplay");
final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
mTmpInsetsState, tmpInputChannel, mTmpInsetsState, mTempControls);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
return;
@@ -233,9 +237,11 @@
}
window.setOuter(snapshotSurface);
try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout");
session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
mTempControls, TMP_SURFACE_SIZE);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} catch (RemoteException e) {
snapshotSurface.clearWindowSynced();
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index e50bde2..6494f89 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -23,7 +23,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.LAUNCHER_TITLE
+import com.android.server.wm.flicker.HOME_WINDOW_TITLE
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
@@ -62,7 +62,7 @@
override val ignoredWindows: List<String>
get() = listOf(LAUNCHER_PACKAGE_NAME, LIVE_WALLPAPER_PACKAGE_NAME,
splitScreenApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME, *LAUNCHER_TITLE)
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME, *HOME_WINDOW_TITLE)
@FlakyTest(bugId = 169271943)
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index d1d1313..2623535 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -77,9 +77,9 @@
int mAddWindowForTask = 0;
int mViewThemeResId;
- TestStartingSurfaceDrawer(Context context, ShellExecutor animExecutor,
+ TestStartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor,
TransactionPool pool) {
- super(context, animExecutor, pool);
+ super(context, splashScreenExecutor, pool);
}
@Override
@@ -120,8 +120,9 @@
doReturn(metrics).when(mMockWindowManager).getMaximumWindowMetrics();
doNothing().when(mMockWindowManager).addView(any(), any());
- mStartingSurfaceDrawer = spy(new TestStartingSurfaceDrawer(context,
- new HandlerExecutor(new Handler(Looper.getMainLooper())),
+ final HandlerExecutor testExecutor =
+ new HandlerExecutor(new Handler(Looper.getMainLooper()));
+ mStartingSurfaceDrawer = spy(new TestStartingSurfaceDrawer(context, testExecutor,
mTransactionPool));
}
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 4c4a152..3056e97 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -820,10 +820,11 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
+ uirenderer::CanvasPropertyPrimitive* turbulencePhase,
const SkRuntimeShaderBuilder& effectBuilder) {
sk_sp<uirenderer::skiapipeline::AnimatedRipple> drawable(
new uirenderer::skiapipeline::AnimatedRipple(x, y, radius, paint, progress,
- effectBuilder));
+ turbulencePhase, effectBuilder));
mCanvas->drawDrawable(drawable.get());
}
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index e0a0be5..995f00c 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -153,6 +153,7 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
+ uirenderer::CanvasPropertyPrimitive* turbulencePhase,
const SkRuntimeShaderBuilder& effectBuilder) override;
virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 671c66f..979678d 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -18,6 +18,7 @@
#include <private/hwui/WebViewFunctor.h>
#include "Properties.h"
+#include "renderthread/CanvasContext.h"
#include "renderthread/RenderThread.h"
#include <log/log.h>
@@ -115,11 +116,20 @@
ScopedCurrentFunctor currentFunctor(this);
WebViewOverlayData overlayParams = {
- // TODO:
.overlaysMode = OverlaysMode::Disabled,
.getSurfaceControl = currentFunctor.getSurfaceControl,
.mergeTransaction = currentFunctor.mergeTransaction,
};
+
+ if (!drawInfo.isLayer) {
+ renderthread::CanvasContext* activeContext =
+ renderthread::CanvasContext::getActiveContext();
+ if (activeContext != nullptr) {
+ ASurfaceControl* rootSurfaceControl = activeContext->getSurfaceControl();
+ if (rootSurfaceControl) overlayParams.overlaysMode = OverlaysMode::Enabled;
+ }
+ }
+
mCallbacks.gles.draw(mFunctor, mData, drawInfo, overlayParams);
}
@@ -138,11 +148,12 @@
ScopedCurrentFunctor currentFunctor(this);
WebViewOverlayData overlayParams = {
- // TODO
.overlaysMode = OverlaysMode::Disabled,
.getSurfaceControl = currentFunctor.getSurfaceControl,
.mergeTransaction = currentFunctor.mergeTransaction,
};
+
+ // TODO, enable surface control once offscreen mode figured out
mCallbacks.vk.draw(mFunctor, mData, params, overlayParams);
}
@@ -166,15 +177,43 @@
void WebViewFunctor::removeOverlays() {
ScopedCurrentFunctor currentFunctor(this);
mCallbacks.removeOverlays(mFunctor, mData, currentFunctor.mergeTransaction);
+ if (mSurfaceControl) {
+ auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions();
+ funcs.releaseFunc(mSurfaceControl);
+ mSurfaceControl = nullptr;
+ }
}
ASurfaceControl* WebViewFunctor::getSurfaceControl() {
- // TODO
- return nullptr;
+ ATRACE_NAME("WebViewFunctor::getSurfaceControl");
+ if (mSurfaceControl != nullptr) return mSurfaceControl;
+
+ renderthread::CanvasContext* activeContext = renderthread::CanvasContext::getActiveContext();
+ LOG_ALWAYS_FATAL_IF(activeContext == nullptr, "Null active canvas context!");
+
+ ASurfaceControl* rootSurfaceControl = activeContext->getSurfaceControl();
+ LOG_ALWAYS_FATAL_IF(rootSurfaceControl == nullptr, "Null root surface control!");
+
+ auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions();
+ mSurfaceControl = funcs.createFunc(rootSurfaceControl, "Webview Overlay SurfaceControl");
+ ASurfaceTransaction* transaction = funcs.transactionCreateFunc();
+ funcs.transactionSetVisibilityFunc(transaction, mSurfaceControl,
+ ASURFACE_TRANSACTION_VISIBILITY_SHOW);
+ funcs.transactionApplyFunc(transaction);
+ funcs.transactionDeleteFunc(transaction);
+ return mSurfaceControl;
}
void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) {
- // TODO
+ ATRACE_NAME("WebViewFunctor::mergeTransaction");
+ if (transaction == nullptr) return;
+ renderthread::CanvasContext* activeContext = renderthread::CanvasContext::getActiveContext();
+ LOG_ALWAYS_FATAL_IF(activeContext == nullptr, "Null active canvas context!");
+ bool done = activeContext->mergeTransaction(transaction, mSurfaceControl);
+ if (!done) {
+ auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions();
+ funcs.transactionApplyFunc(transaction);
+ }
}
WebViewFunctorManager& WebViewFunctorManager::instance() {
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index 17b936a..a84cda5 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -91,6 +91,7 @@
RenderMode mMode;
bool mHasContext = false;
bool mCreatedHandle = false;
+ ASurfaceControl* mSurfaceControl = nullptr;
};
class WebViewFunctorManager {
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index 855cd0d..173f394 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -152,32 +152,66 @@
sp<uirenderer::CanvasPropertyPrimitive> radius;
sp<uirenderer::CanvasPropertyPaint> paint;
sp<uirenderer::CanvasPropertyPrimitive> progress;
+ sp<uirenderer::CanvasPropertyPrimitive> turbulencePhase;
sk_sp<SkRuntimeEffect> effect;
+ const float PI = 3.1415926535897932384626;
+ const float PI_ROTATE_RIGHT = PI * 0.0078125;
+ const float PI_ROTATE_LEFT = PI * -0.0078125;
+ const float SCALE = 1.5;
+ const float CIRCLE_X_1 = 0.01 * cos(SCALE * 0.55);
+ const float CIRCLE_Y_1 = 0.01 * sin(SCALE * 0.55);
+ const float CIRCLE_X_2 = -0.0066 * cos(SCALE * 0.45);
+ const float CIRCLE_Y_2 = -0.0066 * sin(SCALE * 0.45);
+ const float CIRCLE_X_3 = -0.0066 * cos(SCALE * 0.35);
+ const float CIRCLE_Y_3 = -0.0066 * sin(SCALE * 0.35);
+
void draw(SkCanvas* canvas) const {
SkRuntimeShaderBuilder runtimeEffectBuilder(effect);
- SkRuntimeShaderBuilder::BuilderUniform center = runtimeEffectBuilder.uniform("in_origin");
- if (center.fVar != nullptr) {
- center = SkV2{x->value, y->value};
- }
+ setUniform2f(runtimeEffectBuilder, "in_origin", x->value, y->value);
+ setUniform(runtimeEffectBuilder, "in_radius", radius);
+ setUniform(runtimeEffectBuilder, "in_progress", progress);
+ setUniform(runtimeEffectBuilder, "in_turbulencePhase", turbulencePhase);
- SkRuntimeShaderBuilder::BuilderUniform radiusU =
- runtimeEffectBuilder.uniform("in_radius");
- if (radiusU.fVar != nullptr) {
- radiusU = radius->value;
- }
-
- SkRuntimeShaderBuilder::BuilderUniform progressU =
- runtimeEffectBuilder.uniform("in_progress");
- if (progressU.fVar != nullptr) {
- progressU = progress->value;
- }
+ //
+ // Keep in sync with:
+ // frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java
+ //
+ const float turbulence = turbulencePhase->value;
+ setUniform2f(runtimeEffectBuilder, "in_tCircle1", SCALE * 0.5 + (turbulence * CIRCLE_X_1),
+ SCALE * 0.5 + (turbulence * CIRCLE_Y_1));
+ setUniform2f(runtimeEffectBuilder, "in_tCircle2", SCALE * 0.2 + (turbulence * CIRCLE_X_2),
+ SCALE * 0.2 + (turbulence * CIRCLE_Y_2));
+ setUniform2f(runtimeEffectBuilder, "in_tCircle3", SCALE + (turbulence * CIRCLE_X_3),
+ SCALE + (turbulence * CIRCLE_Y_3));
+ const float rotation1 = turbulence * PI_ROTATE_RIGHT + 1.7 * PI;
+ setUniform2f(runtimeEffectBuilder, "in_tRotation1", cos(rotation1), sin(rotation1));
+ const float rotation2 = turbulence * PI_ROTATE_LEFT + 2 * PI;
+ setUniform2f(runtimeEffectBuilder, "in_tRotation2", cos(rotation2), sin(rotation2));
+ const float rotation3 = turbulence * PI_ROTATE_RIGHT + 2.75 * PI;
+ setUniform2f(runtimeEffectBuilder, "in_tRotation3", cos(rotation3), sin(rotation3));
SkPaint paintMod = paint->value;
paintMod.setShader(runtimeEffectBuilder.makeShader(nullptr, false));
canvas->drawCircle(x->value, y->value, radius->value, paintMod);
}
+
+ void setUniform(SkRuntimeShaderBuilder& effect, std::string name,
+ sp<uirenderer::CanvasPropertyPrimitive> property) const {
+ SkRuntimeShaderBuilder::BuilderUniform uniform = effect.uniform(name.c_str());
+ if (uniform.fVar != nullptr) {
+ uniform = property->value;
+ }
+ }
+
+ void setUniform2f(SkRuntimeShaderBuilder effect, std::string name, float a, float b) const {
+ SkRuntimeShaderBuilder::BuilderUniform uniform = effect.uniform(name.c_str());
+ if (uniform.fVar != nullptr) {
+ uniform = SkV2{a, b};
+ }
+ }
+
ASSERT_DRAWABLE()
};
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index c1feb76..837b055 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -146,6 +146,7 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
+ uirenderer::CanvasPropertyPrimitive* turbulencePhase,
const SkRuntimeShaderBuilder& effectBuilder) = 0;
virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) = 0;
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 251323d..f928baa 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -373,6 +373,12 @@
makeSkDataCached(filePath.c_str(), false /* fs verity */);
}
+// Critical Native
+static void Typeface_addFontCollection(CRITICAL_JNI_PARAMS_COMMA jlong faceHandle) {
+ std::shared_ptr<minikin::FontCollection> collection = toTypeface(faceHandle)->fFontCollection;
+ minikin::SystemFonts::addFontMap(std::move(collection));
+}
+
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gTypefaceMethods[] = {
@@ -397,6 +403,7 @@
{"nativeGetFamilySize", "(J)I", (void*)Typeface_getFamilySize},
{"nativeGetFamily", "(JI)J", (void*)Typeface_getFamily},
{"nativeWarmUpCache", "(Ljava/lang/String;)V", (void*)Typeface_warmUpCache},
+ {"nativeAddFontCollections", "(J)V", (void*)Typeface_addFontCollection},
};
int register_android_graphics_Typeface(JNIEnv* env)
diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
index 855d56e..eb5a88a 100644
--- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
+++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
@@ -141,20 +141,22 @@
canvas->drawCircle(xProp, yProp, radiusProp, paintProp);
}
-static void android_view_DisplayListCanvas_drawRippleProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
- jlong xPropPtr, jlong yPropPtr,
- jlong radiusPropPtr, jlong paintPropPtr,
- jlong progressPropPtr,
- jlong builderPtr) {
+static void android_view_DisplayListCanvas_drawRippleProps(
+ CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jlong xPropPtr, jlong yPropPtr,
+ jlong radiusPropPtr, jlong paintPropPtr, jlong progressPropPtr, jlong turbulencePhasePtr,
+ jlong builderPtr) {
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr);
CanvasPropertyPrimitive* radiusProp = reinterpret_cast<CanvasPropertyPrimitive*>(radiusPropPtr);
+ CanvasPropertyPrimitive* turbulencePhaseProp =
+ reinterpret_cast<CanvasPropertyPrimitive*>(turbulencePhasePtr);
CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr);
CanvasPropertyPrimitive* progressProp =
reinterpret_cast<CanvasPropertyPrimitive*>(progressPropPtr);
SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(builderPtr);
- canvas->drawRipple(xProp, yProp, radiusProp, paintProp, progressProp, *builder);
+ canvas->drawRipple(xProp, yProp, radiusProp, paintProp, progressProp, turbulencePhaseProp,
+ *builder);
}
static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jint functor) {
@@ -169,19 +171,22 @@
const char* const kClassPathName = "android/graphics/RecordingCanvas";
static JNINativeMethod gMethods[] = {
- // ------------ @CriticalNative --------------
- { "nCreateDisplayListCanvas", "(JII)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas },
- { "nResetDisplayListCanvas", "(JJII)V", (void*) android_view_DisplayListCanvas_resetDisplayListCanvas },
- { "nGetMaximumTextureWidth", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureSize },
- { "nGetMaximumTextureHeight", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureSize },
- { "nEnableZ", "(JZ)V", (void*) android_view_DisplayListCanvas_enableZ },
- { "nFinishRecording", "(JJ)V", (void*) android_view_DisplayListCanvas_finishRecording },
- { "nDrawRenderNode", "(JJ)V", (void*) android_view_DisplayListCanvas_drawRenderNode },
- { "nDrawTextureLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawTextureLayer },
- { "nDrawCircle", "(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps },
- { "nDrawRoundRect", "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
- { "nDrawWebViewFunctor", "(JI)V", (void*) android_view_DisplayListCanvas_drawWebViewFunctor },
- { "nDrawRipple", "(JJJJJJJ)V", (void*) android_view_DisplayListCanvas_drawRippleProps },
+ // ------------ @CriticalNative --------------
+ {"nCreateDisplayListCanvas", "(JII)J",
+ (void*)android_view_DisplayListCanvas_createDisplayListCanvas},
+ {"nResetDisplayListCanvas", "(JJII)V",
+ (void*)android_view_DisplayListCanvas_resetDisplayListCanvas},
+ {"nGetMaximumTextureWidth", "()I", (void*)android_view_DisplayListCanvas_getMaxTextureSize},
+ {"nGetMaximumTextureHeight", "()I",
+ (void*)android_view_DisplayListCanvas_getMaxTextureSize},
+ {"nEnableZ", "(JZ)V", (void*)android_view_DisplayListCanvas_enableZ},
+ {"nFinishRecording", "(JJ)V", (void*)android_view_DisplayListCanvas_finishRecording},
+ {"nDrawRenderNode", "(JJ)V", (void*)android_view_DisplayListCanvas_drawRenderNode},
+ {"nDrawTextureLayer", "(JJ)V", (void*)android_view_DisplayListCanvas_drawTextureLayer},
+ {"nDrawCircle", "(JJJJJ)V", (void*)android_view_DisplayListCanvas_drawCircleProps},
+ {"nDrawRoundRect", "(JJJJJJJJ)V", (void*)android_view_DisplayListCanvas_drawRoundRectProps},
+ {"nDrawWebViewFunctor", "(JI)V", (void*)android_view_DisplayListCanvas_drawWebViewFunctor},
+ {"nDrawRipple", "(JJJJJJJJ)V", (void*)android_view_DisplayListCanvas_drawRippleProps},
};
int register_android_view_DisplayListCanvas(JNIEnv* env) {
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index d859541..dd78d58 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -64,6 +64,10 @@
} gHardwareRenderer;
struct {
+ jmethodID onMergeTransaction;
+} gASurfaceTransactionCallback;
+
+struct {
jmethodID onFrameDraw;
} gFrameDrawingCallback;
@@ -509,6 +513,27 @@
}
}
+static void android_view_ThreadedRenderer_setASurfaceTransactionCallback(
+ JNIEnv* env, jobject clazz, jlong proxyPtr, jobject aSurfaceTransactionCallback) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ if (!aSurfaceTransactionCallback) {
+ proxy->setASurfaceTransactionCallback(nullptr);
+ } else {
+ JavaVM* vm = nullptr;
+ LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+ auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(
+ vm, env->NewGlobalRef(aSurfaceTransactionCallback));
+ proxy->setASurfaceTransactionCallback(
+ [globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) {
+ JNIEnv* env = getenv(globalCallbackRef->vm());
+ env->CallVoidMethod(globalCallbackRef->object(),
+ gASurfaceTransactionCallback.onMergeTransaction,
+ static_cast<jlong>(transObj), static_cast<jlong>(scObj),
+ static_cast<jlong>(frameNr));
+ });
+ }
+}
+
static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env,
jobject clazz, jlong proxyPtr, jobject frameCallback) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -762,8 +787,7 @@
{"nSetName", "(JLjava/lang/String;)V", (void*)android_view_ThreadedRenderer_setName},
{"nSetSurface", "(JLandroid/view/Surface;Z)V",
(void*)android_view_ThreadedRenderer_setSurface},
- {"nSetSurfaceControl", "(JJ)V",
- (void*)android_view_ThreadedRenderer_setSurfaceControl},
+ {"nSetSurfaceControl", "(JJ)V", (void*)android_view_ThreadedRenderer_setSurfaceControl},
{"nPause", "(J)Z", (void*)android_view_ThreadedRenderer_pause},
{"nSetStopped", "(JZ)V", (void*)android_view_ThreadedRenderer_setStopped},
{"nSetLightAlpha", "(JFF)V", (void*)android_view_ThreadedRenderer_setLightAlpha},
@@ -804,6 +828,9 @@
{"nSetPictureCaptureCallback",
"(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V",
(void*)android_view_ThreadedRenderer_setPictureCapturedCallbackJNI},
+ {"nSetASurfaceTransactionCallback",
+ "(JLandroid/graphics/HardwareRenderer$ASurfaceTransactionCallback;)V",
+ (void*)android_view_ThreadedRenderer_setASurfaceTransactionCallback},
{"nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V",
(void*)android_view_ThreadedRenderer_setFrameCallback},
{"nSetFrameCompleteCallback",
@@ -866,6 +893,11 @@
GetStaticMethodIDOrDie(env, hardwareRenderer, "closeHintSession",
"(Landroid/os/PerformanceHintManager$Session;)V");
+ jclass aSurfaceTransactionCallbackClass =
+ FindClassOrDie(env, "android/graphics/HardwareRenderer$ASurfaceTransactionCallback");
+ gASurfaceTransactionCallback.onMergeTransaction =
+ GetMethodIDOrDie(env, aSurfaceTransactionCallbackClass, "onMergeTransaction", "(JJJ)V");
+
jclass frameCallbackClass = FindClassOrDie(env,
"android/graphics/HardwareRenderer$FrameDrawingCallback");
gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass,
diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h
index 7859145..7d65be1 100644
--- a/libs/hwui/pipeline/skia/AnimatedDrawables.h
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -19,6 +19,7 @@
#include <SkCanvas.h>
#include <SkDrawable.h>
#include <SkRuntimeEffect.h>
+#include <math.h>
#include <utils/RefBase.h>
#include "CanvasProperty.h"
@@ -61,12 +62,14 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
+ uirenderer::CanvasPropertyPrimitive* turbulencePhase,
const SkRuntimeShaderBuilder& effectBuilder)
: mX(x)
, mY(y)
, mRadius(radius)
, mPaint(paint)
, mProgress(progress)
+ , mTurbulencePhase(turbulencePhase)
, mRuntimeEffectBuilder(effectBuilder) {}
protected:
@@ -77,22 +80,28 @@
return SkRect::MakeLTRB(x - radius, y - radius, x + radius, y + radius);
}
virtual void onDraw(SkCanvas* canvas) override {
- SkRuntimeShaderBuilder::BuilderUniform center = mRuntimeEffectBuilder.uniform("in_origin");
- if (center.fVar != nullptr) {
- center = SkV2{mX->value, mY->value};
- }
+ setUniform2f("in_origin", mX->value, mY->value);
+ setUniform("in_radius", mRadius);
+ setUniform("in_progress", mProgress);
+ setUniform("in_turbulencePhase", mTurbulencePhase);
- SkRuntimeShaderBuilder::BuilderUniform radiusU =
- mRuntimeEffectBuilder.uniform("in_radius");
- if (radiusU.fVar != nullptr) {
- radiusU = mRadius->value;
- }
-
- SkRuntimeShaderBuilder::BuilderUniform progressU =
- mRuntimeEffectBuilder.uniform("in_progress");
- if (progressU.fVar != nullptr) {
- progressU = mProgress->value;
- }
+ //
+ // Keep in sync with:
+ // frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java
+ //
+ const float turbulencePhase = mTurbulencePhase->value;
+ setUniform2f("in_tCircle1", SCALE * 0.5 + (turbulencePhase * CIRCLE_X_1),
+ SCALE * 0.5 + (turbulencePhase * CIRCLE_Y_1));
+ setUniform2f("in_tCircle2", SCALE * 0.2 + (turbulencePhase * CIRCLE_X_2),
+ SCALE * 0.2 + (turbulencePhase * CIRCLE_Y_2));
+ setUniform2f("in_tCircle3", SCALE + (turbulencePhase * CIRCLE_X_3),
+ SCALE + (turbulencePhase * CIRCLE_Y_3));
+ const float rotation1 = turbulencePhase * PI_ROTATE_RIGHT + 1.7 * PI;
+ setUniform2f("in_tRotation1", cos(rotation1), sin(rotation1));
+ const float rotation2 = turbulencePhase * PI_ROTATE_LEFT + 2 * PI;
+ setUniform2f("in_tRotation2", cos(rotation2), sin(rotation2));
+ const float rotation3 = turbulencePhase * PI_ROTATE_RIGHT + 2.75 * PI;
+ setUniform2f("in_tRotation3", cos(rotation3), sin(rotation3));
SkPaint paint = mPaint->value;
paint.setShader(mRuntimeEffectBuilder.makeShader(nullptr, false));
@@ -105,7 +114,35 @@
sp<uirenderer::CanvasPropertyPrimitive> mRadius;
sp<uirenderer::CanvasPropertyPaint> mPaint;
sp<uirenderer::CanvasPropertyPrimitive> mProgress;
+ sp<uirenderer::CanvasPropertyPrimitive> mTurbulencePhase;
SkRuntimeShaderBuilder mRuntimeEffectBuilder;
+
+ const float PI = 3.1415926535897932384626;
+ const float PI_ROTATE_RIGHT = PI * 0.0078125;
+ const float PI_ROTATE_LEFT = PI * -0.0078125;
+ const float SCALE = 1.5;
+ const float CIRCLE_X_1 = 0.01 * cos(SCALE * 0.55);
+ const float CIRCLE_Y_1 = 0.01 * sin(SCALE * 0.55);
+ const float CIRCLE_X_2 = -0.0066 * cos(SCALE * 0.45);
+ const float CIRCLE_Y_2 = -0.0066 * sin(SCALE * 0.45);
+ const float CIRCLE_X_3 = -0.0066 * cos(SCALE * 0.35);
+ const float CIRCLE_Y_3 = -0.0066 * sin(SCALE * 0.35);
+
+ virtual void setUniform(std::string name, sp<uirenderer::CanvasPropertyPrimitive> property) {
+ SkRuntimeShaderBuilder::BuilderUniform uniform =
+ mRuntimeEffectBuilder.uniform(name.c_str());
+ if (uniform.fVar != nullptr) {
+ uniform = property->value;
+ }
+ }
+
+ virtual void setUniform2f(std::string name, float a, float b) {
+ SkRuntimeShaderBuilder::BuilderUniform uniform =
+ mRuntimeEffectBuilder.uniform(name.c_str());
+ if (uniform.fVar != nullptr) {
+ uniform = SkV2{a, b};
+ }
+ }
};
class AnimatedCircle : public SkDrawable {
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 71f533c..ab00dd5 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -24,6 +24,7 @@
#include "SkClipStack.h"
#include "SkRect.h"
#include "SkM44.h"
+#include "utils/GLUtils.h"
namespace android {
namespace uirenderer {
@@ -170,6 +171,8 @@
setScissor(info.height, clipRegion.getBounds());
}
+ // WebView may swallow GL errors, so catch them here
+ GL_CHECKPOINT(LOW);
mWebViewHandle->drawGl(info);
if (clearStencilAfterFunctor) {
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 82814de..9e73f04 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -60,12 +60,11 @@
// Add the marker annotation to allow HWUI to determine where the current
// clip/transformation should be applied
SkVector vector = rect.getSimpleRadii();
- const int dataSize = 2;
- float data[dataSize];
+ float data[2];
data[0] = vector.x();
data[1] = vector.y();
mRecorder.drawAnnotation(rect.rect(), HOLE_PUNCH_ANNOTATION.c_str(),
- SkData::MakeWithCopy(data, dataSize));
+ SkData::MakeWithCopy(data, 2 * sizeof(float)));
// Clear the current rect within the layer itself
SkPaint paint = SkPaint();
@@ -115,9 +114,10 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
+ uirenderer::CanvasPropertyPrimitive* turbulencePhase,
const SkRuntimeShaderBuilder& effectBuilder) {
drawDrawable(mDisplayList->allocateDrawable<AnimatedRipple>(x, y, radius, paint, progress,
- effectBuilder));
+ turbulencePhase, effectBuilder));
}
void SkiaRecordingCanvas::enableZ(bool enableZ) {
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 06f2a27..4deb3b9 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -75,6 +75,7 @@
uirenderer::CanvasPropertyPrimitive* radius,
uirenderer::CanvasPropertyPaint* paint,
uirenderer::CanvasPropertyPrimitive* progress,
+ uirenderer::CanvasPropertyPrimitive* turbulencePhase,
const SkRuntimeShaderBuilder& effectBuilder) override;
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
index 6bfbb0d..a6e4c4c 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.cpp
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -22,7 +22,7 @@
void TransformCanvas::onDrawAnnotation(const SkRect& rect, const char* key, SkData* value) {
if (HOLE_PUNCH_ANNOTATION == key) {
- auto* rectParams = static_cast<const float*>(value->data());
+ auto* rectParams = reinterpret_cast<const float*>(value->data());
float radiusX = rectParams[0];
float radiusY = rectParams[1];
SkRRect roundRect = SkRRect::MakeRectXY(rect, radiusX, radiusY);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 2482188..bba2207 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -56,6 +56,22 @@
namespace uirenderer {
namespace renderthread {
+namespace {
+class ScopedActiveContext {
+public:
+ ScopedActiveContext(CanvasContext* context) { sActiveContext = context; }
+
+ ~ScopedActiveContext() { sActiveContext = nullptr; }
+
+ static CanvasContext* getActiveContext() { return sActiveContext; }
+
+private:
+ static CanvasContext* sActiveContext;
+};
+
+CanvasContext* ScopedActiveContext::sActiveContext = nullptr;
+} /* namespace */
+
CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
RenderNode* rootRenderNode, IContextFactory* contextFactory) {
auto renderType = Properties::getRenderPipelineType();
@@ -473,6 +489,7 @@
return;
}
+ ScopedActiveContext activeContext(this);
mCurrentFrameInfo->set(FrameInfoIndex::FrameInterval) =
mRenderThread.timeLord().frameIntervalNanos();
@@ -880,6 +897,17 @@
return windowDirty;
}
+CanvasContext* CanvasContext::getActiveContext() {
+ return ScopedActiveContext::getActiveContext();
+}
+
+bool CanvasContext::mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control) {
+ if (!mASurfaceTransactionCallback) return false;
+ std::invoke(mASurfaceTransactionCallback, reinterpret_cast<int64_t>(transaction),
+ reinterpret_cast<int64_t>(control), getFrameNumber());
+ return true;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 74f426e..af1ebb2 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -109,6 +109,8 @@
*/
GrDirectContext* getGrContext() const { return mRenderThread.getGrContext(); }
+ ASurfaceControl* getSurfaceControl() const { return mSurfaceControl; }
+
// Won't take effect until next EGLSurface creation
void setSwapBehavior(SwapBehavior swapBehavior);
@@ -201,6 +203,15 @@
static void onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
ASurfaceControlStats* stats);
+ void setASurfaceTransactionCallback(
+ const std::function<void(int64_t, int64_t, int64_t)>& callback) {
+ mASurfaceTransactionCallback = callback;
+ }
+
+ bool mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control);
+
+ static CanvasContext* getActiveContext();
+
private:
CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
@@ -296,6 +307,8 @@
// If set to true, we expect that callbacks into onSurfaceStatsAvailable
bool mExpectSurfaceStats = false;
+
+ std::function<void(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 9361abd..1b4b4b9 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -295,6 +295,12 @@
[this, cb = callback]() { mContext->setPictureCapturedCallback(cb); });
}
+void RenderProxy::setASurfaceTransactionCallback(
+ const std::function<void(int64_t, int64_t, int64_t)>& callback) {
+ mRenderThread.queue().post(
+ [this, cb = callback]() { mContext->setASurfaceTransactionCallback(cb); });
+}
+
void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) {
mDrawFrameTask.setFrameCallback(std::move(callback));
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 8d55d3c..288f555 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -120,6 +120,8 @@
void drawRenderNode(RenderNode* node);
void setContentDrawBounds(int left, int top, int right, int bottom);
void setPictureCapturedCallback(const std::function<void(sk_sp<SkPicture>&&)>& callback);
+ void setASurfaceTransactionCallback(
+ const std::function<void(int64_t, int64_t, int64_t)>& callback);
void setFrameCallback(std::function<void(int64_t)>&& callback);
void setFrameCompleteCallback(std::function<void(int64_t)>&& callback);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 682baa6..04aa1cb 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -54,6 +54,10 @@
ASurfaceControlFunctions::ASurfaceControlFunctions() {
void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+ createFunc = (ASC_create)dlsym(handle_, "ASurfaceControl_create");
+ LOG_ALWAYS_FATAL_IF(createFunc == nullptr,
+ "Failed to find required symbol ASurfaceControl_create!");
+
acquireFunc = (ASC_acquire) dlsym(handle_, "ASurfaceControl_acquire");
LOG_ALWAYS_FATAL_IF(acquireFunc == nullptr,
"Failed to find required symbol ASurfaceControl_acquire!");
@@ -81,6 +85,23 @@
"ASurfaceControlStats_getFrameNumber");
LOG_ALWAYS_FATAL_IF(getFrameNumberFunc == nullptr,
"Failed to find required symbol ASurfaceControlStats_getFrameNumber!");
+
+ transactionCreateFunc = (AST_create)dlsym(handle_, "ASurfaceTransaction_create");
+ LOG_ALWAYS_FATAL_IF(transactionCreateFunc == nullptr,
+ "Failed to find required symbol ASurfaceTransaction_create!");
+
+ transactionDeleteFunc = (AST_delete)dlsym(handle_, "ASurfaceTransaction_delete");
+ LOG_ALWAYS_FATAL_IF(transactionDeleteFunc == nullptr,
+ "Failed to find required symbol ASurfaceTransaction_delete!");
+
+ transactionApplyFunc = (AST_apply)dlsym(handle_, "ASurfaceTransaction_apply");
+ LOG_ALWAYS_FATAL_IF(transactionApplyFunc == nullptr,
+ "Failed to find required symbol ASurfaceTransaction_apply!");
+
+ transactionSetVisibilityFunc =
+ (AST_setVisibility)dlsym(handle_, "ASurfaceTransaction_setVisibility");
+ LOG_ALWAYS_FATAL_IF(transactionSetVisibilityFunc == nullptr,
+ "Failed to find required symbol ASurfaceTransaction_setVisibility!");
}
void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 9e5bce7..cd9b923 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -79,25 +79,39 @@
virtual ~VsyncSource() {}
};
+typedef ASurfaceControl* (*ASC_create)(ASurfaceControl* parent, const char* debug_name);
typedef void (*ASC_acquire)(ASurfaceControl* control);
typedef void (*ASC_release)(ASurfaceControl* control);
typedef void (*ASC_registerSurfaceStatsListener)(ASurfaceControl* control, void* context,
ASurfaceControl_SurfaceStatsListener func);
typedef void (*ASC_unregisterSurfaceStatsListener)(void* context,
- ASurfaceControl_SurfaceStatsListener func);
+ ASurfaceControl_SurfaceStatsListener func);
typedef int64_t (*ASCStats_getAcquireTime)(ASurfaceControlStats* stats);
typedef uint64_t (*ASCStats_getFrameNumber)(ASurfaceControlStats* stats);
+typedef ASurfaceTransaction* (*AST_create)();
+typedef void (*AST_delete)(ASurfaceTransaction* transaction);
+typedef void (*AST_apply)(ASurfaceTransaction* transaction);
+typedef void (*AST_setVisibility)(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control, int8_t visibility);
+
struct ASurfaceControlFunctions {
ASurfaceControlFunctions();
+
+ ASC_create createFunc;
ASC_acquire acquireFunc;
ASC_release releaseFunc;
ASC_registerSurfaceStatsListener registerListenerFunc;
ASC_unregisterSurfaceStatsListener unregisterListenerFunc;
ASCStats_getAcquireTime getAcquireTimeFunc;
ASCStats_getFrameNumber getFrameNumberFunc;
+
+ AST_create transactionCreateFunc;
+ AST_delete transactionDeleteFunc;
+ AST_apply transactionApplyFunc;
+ AST_setVisibility transactionSetVisibilityFunc;
};
class ChoreographerSource;
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 24f553f..3a86786 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -273,8 +273,8 @@
/** Audio data format: compressed audio wrapped in PCM for HDMI
* or S/PDIF passthrough.
- * IEC61937 uses a stereo stream of 16-bit samples as the wrapper.
- * So the channel mask for the track must be {@link #CHANNEL_OUT_STEREO}.
+ * For devices whose SDK version is less than {@link android.os.Build.VERSION_CODES#S}, the
+ * channel mask of IEC61937 track must be {@link #CHANNEL_OUT_STEREO}.
* Data should be written to the stream in a short[] array.
* If the data is written in a byte[] array then there may be endian problems
* on some platforms when converting to short internally.
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 85d1bc5..4dc1cca 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6887,7 +6887,6 @@
*
* @return true if successful, otherwise false
*/
- @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS)
public @EncodedSurroundOutputMode int getEncodedSurroundMode() {
try {
return getService().getEncodedSurroundMode(
@@ -6944,7 +6943,6 @@
*
* @return whether the required surround format is enabled
*/
- @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS)
public boolean isSurroundFormatEnabled(@AudioFormat.SurroundSoundEncoding int audioFormat) {
try {
return getService().isSurroundFormatEnabled(audioFormat);
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 7a2b022..2ea0984 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1676,13 +1676,11 @@
}
mSampleRate = sampleRateInHz;
- // IEC61937 is based on stereo. We could coerce it to stereo.
- // But the application needs to know the stream is stereo so that
- // it is encoded and played correctly. So better to just reject it.
if (audioFormat == AudioFormat.ENCODING_IEC61937
- && channelConfig != AudioFormat.CHANNEL_OUT_STEREO) {
- throw new IllegalArgumentException(
- "ENCODING_IEC61937 must be configured as CHANNEL_OUT_STEREO");
+ && channelConfig != AudioFormat.CHANNEL_OUT_STEREO
+ && AudioFormat.channelCountFromOutChannelMask(channelConfig) != 8) {
+ Log.w(TAG, "ENCODING_IEC61937 is configured with channel mask as " + channelConfig
+ + ", which is not 2 or 8 channels");
}
//--------------
diff --git a/media/java/android/media/metrics/Event.java b/media/java/android/media/metrics/Event.java
index 17218f0..4a69ac5 100644
--- a/media/java/android/media/metrics/Event.java
+++ b/media/java/android/media/metrics/Event.java
@@ -32,19 +32,17 @@
mTimeSinceCreatedMillis = MediaMetricsManager.INVALID_TIMESTAMP;
}
- // TODO: remove
- protected Event(long timeSinceCreatedMillis) {
- mTimeSinceCreatedMillis = timeSinceCreatedMillis;
- }
-
/* package */ Event(long timeSinceCreatedMillis, Bundle extras) {
mTimeSinceCreatedMillis = timeSinceCreatedMillis;
mMetricsBundle = extras;
}
/**
- * Gets time since the corresponding instance is created in millisecond.
+ * Gets time since the corresponding log session is created in millisecond.
* @return the timestamp since the instance is created, or -1 if unknown.
+ * @see LogSessionId
+ * @see PlaybackSession
+ * @see RecordingSession
*/
@IntRange(from = -1)
public long getTimeSinceCreatedMillis() {
diff --git a/media/java/android/media/metrics/MediaMetricsManager.java b/media/java/android/media/metrics/MediaMetricsManager.java
index b4a74a3..23b697f 100644
--- a/media/java/android/media/metrics/MediaMetricsManager.java
+++ b/media/java/android/media/metrics/MediaMetricsManager.java
@@ -25,7 +25,7 @@
* This class gives information about, and interacts with media metrics.
*/
@SystemService(Context.MEDIA_METRICS_SERVICE)
-public class MediaMetricsManager {
+public final class MediaMetricsManager {
public static final long INVALID_TIMESTAMP = -1;
private static final String TAG = "MediaMetricsManager";
diff --git a/media/java/android/media/metrics/NetworkEvent.java b/media/java/android/media/metrics/NetworkEvent.java
index 0e80543..7471d1d 100644
--- a/media/java/android/media/metrics/NetworkEvent.java
+++ b/media/java/android/media/metrics/NetworkEvent.java
@@ -105,10 +105,8 @@
/**
* Creates a new NetworkEvent.
- *
- * @hide
*/
- public NetworkEvent(@NetworkType int type, long timeSinceCreatedMillis,
+ private NetworkEvent(@NetworkType int type, long timeSinceCreatedMillis,
@NonNull Bundle extras) {
this.mNetworkType = type;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
@@ -124,8 +122,11 @@
}
/**
- * Gets timestamp since the creation in milliseconds.
+ * Gets timestamp since the creation of the log session in milliseconds.
* @return the timestamp since the creation in milliseconds, or -1 if unknown.
+ * @see LogSessionId
+ * @see PlaybackSession
+ * @see RecordingSession
*/
@Override
@IntRange(from = -1)
@@ -177,8 +178,7 @@
return 0;
}
- /** @hide */
- /* package-private */ NetworkEvent(@NonNull android.os.Parcel in) {
+ private NetworkEvent(@NonNull android.os.Parcel in) {
int type = in.readInt();
long timeSinceCreatedMillis = in.readLong();
Bundle extras = in.readBundle();
@@ -230,6 +230,7 @@
* Sets timestamp since the creation in milliseconds.
* @param value the timestamp since the creation in milliseconds.
* -1 indicates the value is unknown.
+ * @see #getTimeSinceCreatedMillis()
*/
public @NonNull Builder setTimeSinceCreatedMillis(@IntRange(from = -1) long value) {
mTimeSinceCreatedMillis = value;
diff --git a/media/java/android/media/metrics/PlaybackErrorEvent.java b/media/java/android/media/metrics/PlaybackErrorEvent.java
index f36c04e..d155576 100644
--- a/media/java/android/media/metrics/PlaybackErrorEvent.java
+++ b/media/java/android/media/metrics/PlaybackErrorEvent.java
@@ -150,10 +150,8 @@
/**
* Creates a new PlaybackErrorEvent.
- *
- * @hide
*/
- public PlaybackErrorEvent(
+ private PlaybackErrorEvent(
@Nullable String exceptionStack,
int errorCode,
int subErrorCode,
@@ -191,8 +189,10 @@
}
/**
- * Gets the timestamp since creation in milliseconds.
+ * Gets the timestamp since creation of the playback session in milliseconds.
* @return the timestamp since the playback is created, or -1 if unknown.
+ * @see LogSessionId
+ * @see PlaybackSession
*/
@Override
@IntRange(from = -1)
@@ -254,8 +254,7 @@
return 0;
}
- /** @hide */
- /* package-private */ PlaybackErrorEvent(@NonNull Parcel in) {
+ private PlaybackErrorEvent(@NonNull Parcel in) {
byte flg = in.readByte();
String exceptionStack = (flg & 0x1) == 0 ? null : in.readString();
int errorCode = in.readInt();
@@ -330,6 +329,7 @@
* Set the timestamp since creation in milliseconds.
* @param value the timestamp since the creation in milliseconds.
* -1 indicates the value is unknown.
+ * @see #getTimeSinceCreatedMillis()
*/
public @NonNull Builder setTimeSinceCreatedMillis(@IntRange(from = -1) long value) {
mTimeSinceCreatedMillis = value;
diff --git a/media/java/android/media/metrics/PlaybackMetrics.java b/media/java/android/media/metrics/PlaybackMetrics.java
index 5f606a0..bbcc484 100644
--- a/media/java/android/media/metrics/PlaybackMetrics.java
+++ b/media/java/android/media/metrics/PlaybackMetrics.java
@@ -213,6 +213,7 @@
/**
* Gets the media duration in milliseconds.
+ * <p>Media duration is the length of the media.
* @return the media duration in milliseconds, or -1 if unknown.
*/
@IntRange(from = -1)
@@ -328,6 +329,8 @@
/**
* Gets network transfer duration in milliseconds.
+ * <p>Total transfer time spent reading from the network in ms. For parallel requests, the
+ * overlapping time intervals are counted only once.
*/
@IntRange(from = -1)
public long getNetworkTransferDurationMillis() {
@@ -523,6 +526,7 @@
/**
* Sets the media duration in milliseconds.
* @param value the media duration in milliseconds. -1 indicates the value is unknown.
+ * @see #getMediaDurationMillis()
*/
public @NonNull Builder setMediaDurationMillis(@IntRange(from = -1) long value) {
mMediaDurationMillis = value;
@@ -645,6 +649,7 @@
* Sets the network transfer duration in milliseconds.
* @param value the network transfer duration in milliseconds.
* -1 indicates the value is unknown.
+ * @see #getNetworkTransferDurationMillis()
*/
public @NonNull Builder setNetworkTransferDurationMillis(@IntRange(from = -1) long value) {
mNetworkTransferDurationMillis = value;
diff --git a/media/java/android/media/metrics/PlaybackStateEvent.java b/media/java/android/media/metrics/PlaybackStateEvent.java
index 449abe9..8e74825 100644
--- a/media/java/android/media/metrics/PlaybackStateEvent.java
+++ b/media/java/android/media/metrics/PlaybackStateEvent.java
@@ -132,10 +132,8 @@
/**
* Creates a new PlaybackStateEvent.
- *
- * @hide
*/
- public PlaybackStateEvent(
+ private PlaybackStateEvent(
int state,
long timeSinceCreatedMillis,
@NonNull Bundle extras) {
@@ -147,13 +145,16 @@
/**
* Gets playback state.
*/
+ @State
public int getState() {
return mState;
}
/**
- * Gets time since the corresponding playback is created in millisecond.
+ * Gets time since the corresponding playback session is created in millisecond.
* @return the timestamp since the playback is created, or -1 if unknown.
+ * @see LogSessionId
+ * @see PlaybackSession
*/
@Override
@IntRange(from = -1)
@@ -197,8 +198,7 @@
return 0;
}
- /** @hide */
- /* package-private */ PlaybackStateEvent(@NonNull Parcel in) {
+ private PlaybackStateEvent(@NonNull Parcel in) {
int state = in.readInt();
long timeSinceCreatedMillis = in.readLong();
Bundle extras = in.readBundle();
@@ -247,6 +247,7 @@
* Sets timestamp since the creation in milliseconds.
* @param value the timestamp since the creation in milliseconds.
* -1 indicates the value is unknown.
+ * @see #getTimeSinceCreatedMillis()
*/
public @NonNull Builder setTimeSinceCreatedMillis(@IntRange(from = -1) long value) {
mTimeSinceCreatedMillis = value;
diff --git a/media/java/android/media/metrics/TrackChangeEvent.java b/media/java/android/media/metrics/TrackChangeEvent.java
index c3670269..65d011c 100644
--- a/media/java/android/media/metrics/TrackChangeEvent.java
+++ b/media/java/android/media/metrics/TrackChangeEvent.java
@@ -184,8 +184,11 @@
}
/**
- * Gets timestamp since the creation in milliseconds.
+ * Gets timestamp since the creation of the log session in milliseconds.
* @return the timestamp since the creation in milliseconds, or -1 if unknown.
+ * @see LogSessionId
+ * @see PlaybackSession
+ * @see RecordingSession
*/
@Override
@IntRange(from = -1)
@@ -193,6 +196,11 @@
return mTimeSinceCreatedMillis;
}
+ /**
+ * Gets the track type.
+ * <p>The track type must be one of {@link #TRACK_TYPE_AUDIO}, {@link #TRACK_TYPE_VIDEO},
+ * {@link #TRACK_TYPE_TEXT}.
+ */
@TrackType
public int getTrackType() {
return mType;
@@ -302,8 +310,7 @@
return 0;
}
- /** @hide */
- /* package-private */ TrackChangeEvent(@NonNull Parcel in) {
+ private TrackChangeEvent(@NonNull Parcel in) {
int flg = in.readInt();
int state = in.readInt();
int reason = in.readInt();
@@ -429,8 +436,14 @@
/**
* Creates a new Builder.
+ * @param type the track type. It must be one of {@link #TRACK_TYPE_AUDIO},
+ * {@link #TRACK_TYPE_VIDEO}, {@link #TRACK_TYPE_TEXT}.
*/
- public Builder(int type) {
+ public Builder(@TrackType int type) {
+ if (type != TRACK_TYPE_AUDIO && type != TRACK_TYPE_VIDEO && type != TRACK_TYPE_TEXT) {
+ throw new IllegalArgumentException("track type must be one of TRACK_TYPE_AUDIO, "
+ + "TRACK_TYPE_VIDEO, TRACK_TYPE_TEXT.");
+ }
mType = type;
}
@@ -499,6 +512,7 @@
* Sets timestamp since the creation in milliseconds.
* @param value the timestamp since the creation in milliseconds.
* -1 indicates the value is unknown.
+ * @see #getTimeSinceCreatedMillis()
*/
public @NonNull Builder setTimeSinceCreatedMillis(@IntRange(from = -1) long value) {
checkNotUsed();
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 9eacc74..e7d30ebb 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.LongDef;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -492,26 +493,15 @@
/**
* Returns whether this is considered as an active playback state.
- * <p>
- * The playback state is considered as an active if the state is one of the following:
- * <ul>
- * <li>{@link #STATE_BUFFERING}</li>
- * <li>{@link #STATE_CONNECTING}</li>
- * <li>{@link #STATE_FAST_FORWARDING}</li>
- * <li>{@link #STATE_PLAYING}</li>
- * <li>{@link #STATE_REWINDING}</li>
- * <li>{@link #STATE_SKIPPING_TO_NEXT}</li>
- * <li>{@link #STATE_SKIPPING_TO_PREVIOUS}</li>
- * <li>{@link #STATE_SKIPPING_TO_QUEUE_ITEM}</li>
- * </ul>
+ * @hide
*/
- public boolean isActive() {
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public boolean isActiveState() {
switch (mState) {
case PlaybackState.STATE_FAST_FORWARDING:
case PlaybackState.STATE_REWINDING:
case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
case PlaybackState.STATE_SKIPPING_TO_NEXT:
- case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM:
case PlaybackState.STATE_BUFFERING:
case PlaybackState.STATE_CONNECTING:
case PlaybackState.STATE_PLAYING:
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 0aae940..4bdbe36 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -3029,7 +3029,8 @@
}
}
-static FrontendSettingsExt1_1 getFrontendSettingsExt1_1(JNIEnv *env, int type, jobject settings) {
+static FrontendSettingsExt1_1 getFrontendSettingsExt1_1(
+ JNIEnv *env, int type, jobject settings, int tunerVersion) {
ALOGD("getFrontendSettingsExt1_1 %d", type);
FrontendSettingsExt1_1 settingsExt1_1 {
@@ -3038,6 +3039,10 @@
};
settingsExt1_1.settingExt.noinit();
+ if (tunerVersion < TUNER_VERSION_1_1) {
+ return settingsExt1_1;
+ }
+
if (type == static_cast<int>(::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) {
getDtmbFrontendSettings(env, settings, settingsExt1_1);
} else {
@@ -3220,7 +3225,8 @@
static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) {
sp<JTuner> tuner = getTuner(env, thiz);
FrontendSettings setting = getFrontendSettings(env, type, settings);
- FrontendSettingsExt1_1 settingExt = getFrontendSettingsExt1_1(env, type, settings);
+ FrontendSettingsExt1_1 settingExt = getFrontendSettingsExt1_1(
+ env, type, settings, tuner->getTunerVersion());
return tuner->tune(setting, settingExt);
}
@@ -3233,7 +3239,8 @@
JNIEnv *env, jobject thiz, jint settingsType, jobject settings, jint scanType) {
sp<JTuner> tuner = getTuner(env, thiz);
FrontendSettings setting = getFrontendSettings(env, settingsType, settings);
- FrontendSettingsExt1_1 settingExt = getFrontendSettingsExt1_1(env, settingsType, settings);
+ FrontendSettingsExt1_1 settingExt = getFrontendSettingsExt1_1(
+ env, settingsType, settings, tuner->getTunerVersion());
return tuner->scan(setting, static_cast<FrontendScanType>(scanType), settingExt);
}
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index fafef42..2a933b2 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -73,6 +73,8 @@
using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+const static int TUNER_VERSION_1_1 = ((1 << 16) | 1);
+
namespace android {
struct LnbClientCallbackImpl : public LnbClientCallback {
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index 2995572..dc1d8b7 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -596,11 +596,11 @@
sectionBits.mask.resize(hidlSectionBits.mask.size());
sectionBits.mode.resize(hidlSectionBits.mode.size());
copy(hidlSectionBits.filter.begin(), hidlSectionBits.filter.end(),
- hidlSectionBits.filter.begin());
+ sectionBits.filter.begin());
copy(hidlSectionBits.mask.begin(), hidlSectionBits.mask.end(),
- hidlSectionBits.mask.begin());
+ sectionBits.mask.begin());
copy(hidlSectionBits.mode.begin(), hidlSectionBits.mode.end(),
- hidlSectionBits.mode.begin());
+ sectionBits.mode.begin());
aidlSection.condition.set<TunerFilterSectionCondition::sectionBits>(sectionBits);
break;
}
@@ -921,7 +921,7 @@
Status s = mTunerFilter->getQueueDesc(&aidlMqDesc);
res = ClientHelper::getServiceSpecificErrorCode(s);
if (res == Result::SUCCESS) {
- mFilterMQ = new (nothrow) AidlMQ(aidlMqDesc);
+ mFilterMQ = new (nothrow) AidlMQ(aidlMqDesc, false/*resetPointer*/);
EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
}
return res;
@@ -938,7 +938,7 @@
AidlMQDesc aidlMQDesc;
unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(
filterMQDesc, &aidlMQDesc);
- mFilterMQ = new (nothrow) AidlMessageQueue(aidlMQDesc);
+ mFilterMQ = new (nothrow) AidlMessageQueue(aidlMQDesc, false/*resetPointer*/);
EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
}
}
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 48d7380..60b0f1e 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -54,21 +54,51 @@
XmlCharUniquePtr mLocale;
};
-struct ASystemFontIterator {
- XmlDocUniquePtr mXmlDoc;
- ParserState state;
-
- // The OEM customization XML.
- XmlDocUniquePtr mCustomizationXmlDoc;
-};
-
struct AFont {
std::string mFilePath;
- std::unique_ptr<std::string> mLocale;
+ std::optional<std::string> mLocale;
uint16_t mWeight;
bool mItalic;
uint32_t mCollectionIndex;
std::vector<std::pair<uint32_t, float>> mAxes;
+
+ bool operator==(const AFont& o) const {
+ return mFilePath == o.mFilePath && mLocale == o.mLocale && mWeight == o.mWeight &&
+ mItalic == o.mItalic && mCollectionIndex == o.mCollectionIndex && mAxes == o.mAxes;
+ }
+
+ AFont() = default;
+ AFont(const AFont&) = default;
+};
+
+struct FontHasher {
+ std::size_t operator()(const AFont& font) const {
+ std::size_t r = std::hash<std::string>{}(font.mFilePath);
+ if (font.mLocale) {
+ r = combine(r, std::hash<std::string>{}(*font.mLocale));
+ }
+ r = combine(r, std::hash<uint16_t>{}(font.mWeight));
+ r = combine(r, std::hash<uint32_t>{}(font.mCollectionIndex));
+ for (const auto& [tag, value] : font.mAxes) {
+ r = combine(r, std::hash<uint32_t>{}(tag));
+ r = combine(r, std::hash<float>{}(value));
+ }
+ return r;
+ }
+
+ std::size_t combine(std::size_t l, std::size_t r) const { return l ^ (r << 1); }
+};
+
+struct ASystemFontIterator {
+ std::vector<AFont> fonts;
+ uint32_t index;
+
+ XmlDocUniquePtr mXmlDoc;
+
+ ParserState state;
+
+ // The OEM customization XML.
+ XmlDocUniquePtr mCustomizationXmlDoc;
};
struct AFontMatcher {
@@ -147,10 +177,9 @@
out->mCollectionIndex = indexStr ?
strtol(reinterpret_cast<const char*>(indexStr.get()), nullptr, 10) : 0;
- out->mLocale.reset(
- state.mLocale ?
- new std::string(reinterpret_cast<const char*>(state.mLocale.get()))
- : nullptr);
+ if (state.mLocale) {
+ out->mLocale.emplace(reinterpret_cast<const char*>(state.mLocale.get()));
+ }
const xmlChar* TAG_ATTR_NAME = BAD_CAST("tag");
const xmlChar* STYLEVALUE_ATTR_NAME = BAD_CAST("stylevalue");
@@ -214,8 +243,44 @@
ASystemFontIterator* ASystemFontIterator_open() {
std::unique_ptr<ASystemFontIterator> ite(new ASystemFontIterator());
- ite->mXmlDoc.reset(xmlReadFile("/system/etc/fonts.xml", nullptr, 0));
- ite->mCustomizationXmlDoc.reset(xmlReadFile("/product/etc/fonts_customization.xml", nullptr, 0));
+
+ std::unordered_set<AFont, FontHasher> fonts;
+ minikin::SystemFonts::getFontMap(
+ [&fonts](const std::vector<std::shared_ptr<minikin::FontCollection>>& collections) {
+ for (const auto& fc : collections) {
+ for (const auto& family : fc->getFamilies()) {
+ for (uint32_t i = 0; i < family->getNumFonts(); ++i) {
+ const minikin::Font* font = family->getFont(i);
+
+ std::optional<std::string> locale;
+ uint32_t localeId = font->getLocaleListId();
+ if (localeId != minikin::kEmptyLocaleListId) {
+ locale.emplace(minikin::getLocaleString(localeId));
+ }
+ std::vector<std::pair<uint32_t, float>> axes;
+ for (const auto& [tag, value] : font->typeface()->GetAxes()) {
+ axes.push_back(std::make_pair(tag, value));
+ }
+
+ fonts.insert(
+ {font->typeface()->GetFontPath(), std::move(locale),
+ font->style().weight(),
+ font->style().slant() == minikin::FontStyle::Slant::ITALIC,
+ static_cast<uint32_t>(font->typeface()->GetFontIndex()),
+ axes});
+ }
+ }
+ }
+ });
+
+ if (fonts.empty()) {
+ ite->mXmlDoc.reset(xmlReadFile("/system/etc/fonts.xml", nullptr, 0));
+ ite->mCustomizationXmlDoc.reset(
+ xmlReadFile("/product/etc/fonts_customization.xml", nullptr, 0));
+ } else {
+ ite->index = 0;
+ ite->fonts.assign(fonts.begin(), fonts.end());
+ }
return ite.release();
}
@@ -308,6 +373,13 @@
AFont* ASystemFontIterator_next(ASystemFontIterator* ite) {
LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument");
+ if (!ite->fonts.empty()) {
+ if (ite->index >= ite->fonts.size()) {
+ return nullptr;
+ }
+ return new AFont(ite->fonts[ite->index++]);
+ }
+
if (ite->mXmlDoc) {
if (!findNextFontNode(ite->mXmlDoc, &ite->state)) {
// Reached end of the XML file. Continue OEM customization.
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index 83cc263..07d04aa 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -22,6 +22,6 @@
<string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string>
<string name="confirmation_title" msgid="8455544820286920304">"مجاز کردن <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> برای مدیریت کردن <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string>
<string name="profile_summary" msgid="2059360676631420073">"این برنامه برای مدیریت <xliff:g id="PROFILE_NAME">%1$s</xliff:g> شما لازم است. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
- <string name="consent_yes" msgid="8344487259618762872">"مجاز"</string>
- <string name="consent_no" msgid="2640796915611404382">"مجاز نیست"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"مجاز بودن"</string>
+ <string name="consent_no" msgid="2640796915611404382">"مجاز نبودن"</string>
</resources>
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
index c62402d..bb93af9 100644
--- a/packages/Connectivity/framework/Android.bp
+++ b/packages/Connectivity/framework/Android.bp
@@ -100,6 +100,7 @@
"//frameworks/base",
// Tests using hidden APIs
+ "//cts/tests/netlegacy22.api",
"//external/sl4a:__subpackages__",
"//frameworks/base/tests/net:__subpackages__",
"//frameworks/libs/net/common/testutils",
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 4719772..c8b04a3 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -7,13 +7,13 @@
public class ConnectivityManager {
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void factoryReset();
- method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshot();
+ method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshots();
method @Nullable public android.net.ProxyInfo getGlobalProxy();
method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange();
method @NonNull public static String getPrivateDnsMode(@NonNull android.content.Context);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerDefaultNetworkCallbackAsUid(int, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
- method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
method @Deprecated public boolean requestRouteToHostAddress(int, java.net.InetAddress);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAcceptPartialConnectivity(@NonNull android.net.Network, boolean, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAcceptUnvalidated(@NonNull android.net.Network, boolean, boolean);
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index 63e0fe9..3ca7475 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -239,6 +239,7 @@
method public final void sendQosSessionLost(int, int, int);
method public final void sendSocketKeepaliveEvent(int, int);
method @Deprecated public void setLegacySubtype(int, @NonNull String);
+ method public void setLingerDuration(@NonNull java.time.Duration);
method public void setTeardownDelayMs(@IntRange(from=0, to=0x1388) int);
method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
method public void unregister();
@@ -276,7 +277,7 @@
method @NonNull public int[] getAdministratorUids();
method @Nullable public static String getCapabilityCarrierName(int);
method @Nullable public String getSsid();
- method @NonNull public java.util.Set<java.lang.Integer> getSubIds();
+ method @NonNull public java.util.Set<java.lang.Integer> getSubscriptionIds();
method @NonNull public int[] getTransportTypes();
method public boolean isPrivateDnsBroken();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
@@ -307,7 +308,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int);
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int);
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
- method @NonNull public android.net.NetworkCapabilities.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
+ method @NonNull public android.net.NetworkCapabilities.Builder setSubscriptionIds(@NonNull java.util.Set<java.lang.Integer>);
method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
}
@@ -337,7 +338,7 @@
public static class NetworkRequest.Builder {
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
- method @NonNull public android.net.NetworkRequest.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
+ method @NonNull public android.net.NetworkRequest.Builder setSubscriptionIds(@NonNull java.util.Set<java.lang.Integer>);
}
public final class NetworkScore implements android.os.Parcelable {
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
index 92a792b..a2e218d 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java
@@ -68,5 +68,11 @@
return cm.startOrGetTestNetworkManager();
}
);
+
+ SystemServiceRegistry.registerContextAwareService(
+ DnsResolverServiceManager.DNS_RESOLVER_SERVICE,
+ DnsResolverServiceManager.class,
+ (context, serviceBinder) -> new DnsResolverServiceManager(serviceBinder)
+ );
}
}
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index c4a0d69..b3c1997 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -1425,9 +1425,9 @@
android.Manifest.permission.NETWORK_STACK,
android.Manifest.permission.NETWORK_SETTINGS})
@NonNull
- public List<NetworkStateSnapshot> getAllNetworkStateSnapshot() {
+ public List<NetworkStateSnapshot> getAllNetworkStateSnapshots() {
try {
- return mService.getAllNetworkStateSnapshot();
+ return mService.getAllNetworkStateSnapshots();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -5362,10 +5362,10 @@
* {@link #unregisterNetworkCallback(NetworkCallback)}.
*
* @param request {@link NetworkRequest} describing this request.
- * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
- * If null, the callback is invoked on the default internal Handler.
* @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
* the callback must not be shared - it uniquely specifies this request.
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * If null, the callback is invoked on the default internal Handler.
* @throws IllegalArgumentException if {@code request} contains invalid network capabilities.
* @throws SecurityException if missing the appropriate permissions.
* @throws RuntimeException if the app already has too many callbacks registered.
@@ -5380,7 +5380,8 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
})
public void requestBackgroundNetwork(@NonNull NetworkRequest request,
- @NonNull Handler handler, @NonNull NetworkCallback networkCallback) {
+ @NonNull NetworkCallback networkCallback,
+ @SuppressLint("ListenerLast") @NonNull Handler handler) {
final NetworkCapabilities nc = request.networkCapabilities;
sendRequestForNetwork(nc, networkCallback, 0, BACKGROUND_REQUEST,
TYPE_NONE, new CallbackHandler(handler));
diff --git a/packages/Connectivity/framework/src/android/net/DnsResolverServiceManager.java b/packages/Connectivity/framework/src/android/net/DnsResolverServiceManager.java
new file mode 100644
index 0000000..79009e8
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/DnsResolverServiceManager.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+
+/**
+ * Provides a way to obtain the DnsResolver binder objects.
+ *
+ * @hide
+ */
+public class DnsResolverServiceManager {
+ /** Service name for the DNS resolver. Keep in sync with DnsResolverService.h */
+ public static final String DNS_RESOLVER_SERVICE = "dnsresolver";
+
+ private final IBinder mResolver;
+
+ DnsResolverServiceManager(IBinder resolver) {
+ mResolver = resolver;
+ }
+
+ /**
+ * Get an {@link IBinder} representing the DnsResolver stable AIDL interface
+ *
+ * @return {@link android.net.IDnsResolver} IBinder.
+ */
+ @NonNull
+ public IBinder getService() {
+ return mResolver;
+ }
+}
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index 728f375..c434bbc 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -82,7 +82,7 @@
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
NetworkState[] getAllNetworkState();
- List<NetworkStateSnapshot> getAllNetworkStateSnapshot();
+ List<NetworkStateSnapshot> getAllNetworkStateSnapshots();
boolean isActiveNetworkMetered();
diff --git a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
index 26cb1ed..9a58add 100644
--- a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
+++ b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl
@@ -42,4 +42,5 @@
void sendQosSessionLost(int qosCallbackId, in QosSession session);
void sendQosCallbackError(int qosCallbackId, int exceptionType);
void sendTeardownDelayMs(int teardownDelayMs);
+ void sendLingerDuration(int durationMs);
}
diff --git a/packages/Connectivity/framework/src/android/net/LinkProperties.java b/packages/Connectivity/framework/src/android/net/LinkProperties.java
index e41ed72..99f48b4 100644
--- a/packages/Connectivity/framework/src/android/net/LinkProperties.java
+++ b/packages/Connectivity/framework/src/android/net/LinkProperties.java
@@ -686,8 +686,8 @@
}
/**
- * Adds a {@link RouteInfo} to this {@code LinkProperties}, if a {@link RouteInfo}
- * with the same {@link RouteInfo.RouteKey} with different properties
+ * Adds a {@link RouteInfo} to this {@code LinkProperties}. If there is a {@link RouteInfo}
+ * with the same destination, gateway and interface with different properties
* (e.g., different MTU), it will be updated. If the {@link RouteInfo} had an
* interface name set and that differs from the interface set for this
* {@code LinkProperties} an {@link IllegalArgumentException} will be thrown.
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index 3622c1c..518d3f3 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
@@ -106,6 +107,9 @@
private final String LOG_TAG;
private static final boolean DBG = true;
private static final boolean VDBG = false;
+ /** @hide */
+ @TestApi
+ public static final int MIN_LINGER_TIMER_MS = 2000;
private final ArrayList<RegistryAction> mPreConnectedQueue = new ArrayList<>();
private volatile long mLastBwRefreshTime = 0;
private static final long BW_REFRESH_MIN_WIN_MS = 500;
@@ -391,6 +395,15 @@
*/
public static final int CMD_NETWORK_DESTROYED = BASE + 23;
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to set the linger duration for this network
+ * agent.
+ * arg1 = the linger duration, represents by {@link Duration}.
+ *
+ * @hide
+ */
+ public static final int EVENT_LINGER_DURATION_CHANGED = BASE + 24;
+
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
config.legacyTypeName, config.legacySubTypeName);
@@ -1287,6 +1300,22 @@
queueOrSendMessage(ra -> ra.sendQosCallbackError(qosCallbackId, exceptionType));
}
+ /**
+ * Set the linger duration for this network agent.
+ * @param duration the delay between the moment the network becomes unneeded and the
+ * moment the network is disconnected or moved into the background.
+ * Note that If this duration has greater than millisecond precision, then
+ * the internal implementation will drop any excess precision.
+ */
+ public void setLingerDuration(@NonNull final Duration duration) {
+ Objects.requireNonNull(duration);
+ final long durationMs = duration.toMillis();
+ if (durationMs < MIN_LINGER_TIMER_MS || durationMs > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("Duration must be within ["
+ + MIN_LINGER_TIMER_MS + "," + Integer.MAX_VALUE + "]ms");
+ }
+ queueOrSendMessage(ra -> ra.sendLingerDuration((int) durationMs));
+ }
/** @hide */
protected void log(final String s) {
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index c4277c3..775c88f 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -1749,7 +1749,7 @@
combineSSIDs(nc);
combineRequestor(nc);
combineAdministratorUids(nc);
- combineSubIds(nc);
+ combineSubscriptionIds(nc);
}
/**
@@ -1771,7 +1771,7 @@
&& (onlyImmutable || satisfiedByUids(nc))
&& (onlyImmutable || satisfiedBySSID(nc))
&& (onlyImmutable || satisfiedByRequestor(nc))
- && (onlyImmutable || satisfiedBySubIds(nc)));
+ && (onlyImmutable || satisfiedBySubscriptionIds(nc)));
}
/**
@@ -1868,7 +1868,7 @@
&& equalsPrivateDnsBroken(that)
&& equalsRequestor(that)
&& equalsAdministratorUids(that)
- && equalsSubIds(that);
+ && equalsSubscriptionIds(that);
}
@Override
@@ -2346,7 +2346,7 @@
* @hide
*/
@NonNull
- public NetworkCapabilities setSubIds(@NonNull Set<Integer> subIds) {
+ public NetworkCapabilities setSubscriptionIds(@NonNull Set<Integer> subIds) {
mSubIds = new ArraySet(Objects.requireNonNull(subIds));
return this;
}
@@ -2362,14 +2362,14 @@
*/
@NonNull
@SystemApi
- public Set<Integer> getSubIds() {
+ public Set<Integer> getSubscriptionIds() {
return new ArraySet<>(mSubIds);
}
/**
* Tests if the subscription ID set of this network is the same as that of the passed one.
*/
- private boolean equalsSubIds(@NonNull NetworkCapabilities nc) {
+ private boolean equalsSubscriptionIds(@NonNull NetworkCapabilities nc) {
return Objects.equals(mSubIds, nc.mSubIds);
}
@@ -2378,7 +2378,7 @@
* If specified in the request, the passed one need to have at least one subId and at least
* one of them needs to be in the request set.
*/
- private boolean satisfiedBySubIds(@NonNull NetworkCapabilities nc) {
+ private boolean satisfiedBySubscriptionIds(@NonNull NetworkCapabilities nc) {
if (mSubIds.isEmpty()) return true;
if (nc.mSubIds.isEmpty()) return false;
for (final Integer subId : nc.mSubIds) {
@@ -2395,7 +2395,7 @@
* <p>If both subscription IDs are not equal, they belong to different subscription
* (or no subscription). In this case, it would not make sense to add them together.
*/
- private void combineSubIds(@NonNull NetworkCapabilities nc) {
+ private void combineSubscriptionIds(@NonNull NetworkCapabilities nc) {
if (!Objects.equals(mSubIds, nc.mSubIds)) {
throw new IllegalStateException("Can't combine two subscription ID sets");
}
@@ -2737,8 +2737,8 @@
*/
@NonNull
@SystemApi
- public Builder setSubIds(@NonNull final Set<Integer> subIds) {
- mCaps.setSubIds(subIds);
+ public Builder setSubscriptionIds(@NonNull final Set<Integer> subIds) {
+ mCaps.setSubscriptionIds(subIds);
return this;
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkProvider.java b/packages/Connectivity/framework/src/android/net/NetworkProvider.java
index cfb7325..0665af5 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkProvider.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkProvider.java
@@ -167,7 +167,15 @@
ConnectivityManager.from(mContext).declareNetworkRequestUnfulfillable(request);
}
- /** @hide */
+ /**
+ * A callback for parties registering a NetworkOffer.
+ *
+ * This is used with {@link ConnectivityManager#offerNetwork}. When offering a network,
+ * the system will use this callback to inform the caller that a network corresponding to
+ * this offer is needed or unneeded.
+ *
+ * @hide
+ */
@SystemApi
public interface NetworkOfferCallback {
/**
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index 78e1011..90aac0e 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -508,8 +508,8 @@
*/
@NonNull
@SystemApi
- public Builder setSubIds(@NonNull Set<Integer> subIds) {
- mNetworkCapabilities.setSubIds(subIds);
+ public Builder setSubscriptionIds(@NonNull Set<Integer> subIds) {
+ mNetworkCapabilities.setSubscriptionIds(subIds);
return this;
}
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkScore.java b/packages/Connectivity/framework/src/android/net/NetworkScore.java
index b0752e9..7be7deb 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkScore.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkScore.java
@@ -48,7 +48,14 @@
})
public @interface KeepConnectedReason { }
+ /**
+ * Do not keep this network connected if there is no outstanding request for it.
+ */
public static final int KEEP_CONNECTED_NONE = 0;
+ /**
+ * Keep this network connected even if there is no outstanding request for it, because it
+ * is being considered for handover.
+ */
public static final int KEEP_CONNECTED_FOR_HANDOVER = 1;
// Agent-managed policies
diff --git a/packages/DynamicSystemInstallationService/res/values-fi/strings.xml b/packages/DynamicSystemInstallationService/res/values-fi/strings.xml
index f32fc37..3e474d8 100644
--- a/packages/DynamicSystemInstallationService/res/values-fi/strings.xml
+++ b/packages/DynamicSystemInstallationService/res/values-fi/strings.xml
@@ -7,7 +7,7 @@
<string name="notification_install_failed" msgid="4066039210317521404">"Asennus epäonnistui"</string>
<string name="notification_image_validation_failed" msgid="2720357826403917016">"Levykuvan vahvistus epäonnistui. Keskeytä asennus."</string>
<string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Käyttää tällä hetkellä dynaamista järjestelmää. Käynnistä uudelleen käyttääksesi alkuperäistä Android-versiota."</string>
- <string name="notification_action_cancel" msgid="5929299408545961077">"Peruuta"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Peru"</string>
<string name="notification_action_discard" msgid="1817481003134947493">"Hylkää"</string>
<string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Käynn. uudelleen"</string>
<string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Käynn. uudelleen"</string>
diff --git a/packages/DynamicSystemInstallationService/res/values-ky/strings.xml b/packages/DynamicSystemInstallationService/res/values-ky/strings.xml
index 320faff..4e1ba66 100644
--- a/packages/DynamicSystemInstallationService/res/values-ky/strings.xml
+++ b/packages/DynamicSystemInstallationService/res/values-ky/strings.xml
@@ -2,11 +2,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="keyguard_description" msgid="8582605799129954556">"Сырсөзүңүздү киргизип, системаны динамикалык жаңыртууга өтүңүз"</string>
- <string name="notification_install_completed" msgid="6252047868415172643">"Динамикалык система даяр. Аны колдонуу үчүн, түзмөктү өчүрүп күйгүзүңүз."</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Динамикалык система даяр. Аны колдонуу үчүн түзмөктү өчүрүп күйгүзүңүз."</string>
<string name="notification_install_inprogress" msgid="7383334330065065017">"Орнотулууда"</string>
<string name="notification_install_failed" msgid="4066039210317521404">"Орнотулбай койду"</string>
<string name="notification_image_validation_failed" msgid="2720357826403917016">"Дисктин сүрөтү текшерилбей калды. Орнотууну токтотуңуз."</string>
- <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Учурда динамикалык система колдонулууда. Android\'дин түпнуска версиясын колдонуу үчүн, өчүрүп күйгүзүңүз."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Учурда динамикалык система колдонулууда. Android\'дин түпнуска версиясын колдонуу үчүн өчүрүп күйгүзүңүз."</string>
<string name="notification_action_cancel" msgid="5929299408545961077">"Жок"</string>
<string name="notification_action_discard" msgid="1817481003134947493">"Жоюу"</string>
<string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Өчүрүп күйгүзүү"</string>
diff --git a/packages/DynamicSystemInstallationService/res/values-ne/strings.xml b/packages/DynamicSystemInstallationService/res/values-ne/strings.xml
index 7afbfd9..de92306 100644
--- a/packages/DynamicSystemInstallationService/res/values-ne/strings.xml
+++ b/packages/DynamicSystemInstallationService/res/values-ne/strings.xml
@@ -2,11 +2,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="keyguard_description" msgid="8582605799129954556">"कृपया आफ्नो पासवर्ड प्रविष्टि गर्नुहोस् र Dynamic System Updates को प्रक्रियालाई निरन्तरता दिनुहोस्"</string>
- <string name="notification_install_completed" msgid="6252047868415172643">"Dynamic System तयार छ। यसको प्रयोग सुरु गर्न आफ्नो यन्त्र रिस्टार्ट गर्नुहोस्।"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynamic System तयार छ। यसको प्रयोग सुरु गर्न आफ्नो डिभाइस रिस्टार्ट गर्नुहोस्।"</string>
<string name="notification_install_inprogress" msgid="7383334330065065017">"इन्स्टल हुँदै छ"</string>
<string name="notification_install_failed" msgid="4066039210317521404">"स्थापना गर्न सकिएन"</string>
<string name="notification_image_validation_failed" msgid="2720357826403917016">"डिस्कको इमेज पुष्टि गर्न सकिएन। स्थापना गर्ने प्रक्रिया रद्द गर्नुहोस्।"</string>
- <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"हाल Dynamic System चलिरहेको छ। Android को मूल संस्करण प्रयोग गर्न यन्त्र रिस्टार्ट गर्नुहोस्।"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"हाल Dynamic System चलिरहेको छ। Android को मूल संस्करण प्रयोग गर्न डिभाइस रिस्टार्ट गर्नुहोस्।"</string>
<string name="notification_action_cancel" msgid="5929299408545961077">"रद्द गर्नुहोस्"</string>
<string name="notification_action_discard" msgid="1817481003134947493">"खारेज गर्नुहोस्"</string>
<string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"रिस्टार्ट गर्नु…"</string>
diff --git a/packages/DynamicSystemInstallationService/res/values-pa/strings.xml b/packages/DynamicSystemInstallationService/res/values-pa/strings.xml
index 8f2fd18..ac12bb7 100644
--- a/packages/DynamicSystemInstallationService/res/values-pa/strings.xml
+++ b/packages/DynamicSystemInstallationService/res/values-pa/strings.xml
@@ -8,7 +8,7 @@
<string name="notification_image_validation_failed" msgid="2720357826403917016">"ਇਮੇਜ ਪ੍ਰਮਾਣਿਕਤਾ ਅਸਫਲ ਰਹੀ। ਸਥਾਪਨਾ ਨੂੰ ਰੱਦ ਕਰੋ।"</string>
<string name="notification_dynsystem_in_use" msgid="1053194595682188396">"ਫ਼ਿਲਹਾਲ ਪਰਿਵਰਤਨਸ਼ੀਲ ਸਿਸਟਮ ਚੱਲ ਰਿਹਾ ਹੈ। ਮੂਲ Android ਵਰਜਨ ਵਰਤਣ ਲਈ ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ।"</string>
<string name="notification_action_cancel" msgid="5929299408545961077">"ਰੱਦ ਕਰੋ"</string>
- <string name="notification_action_discard" msgid="1817481003134947493">"ਖਾਰਜ ਕਰੋ"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"ਬਰਖਾਸਤ ਕਰੋ"</string>
<string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ"</string>
<string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ"</string>
<string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ਪਰਿਵਰਤਨਸ਼ੀਲ ਸਿਸਟਮ ਰੱਦ ਕੀਤਾ ਗਿਆ"</string>
diff --git a/packages/PackageInstaller/res/values-es/strings.xml b/packages/PackageInstaller/res/values-es/strings.xml
index 3a0757b..7431241 100644
--- a/packages/PackageInstaller/res/values-es/strings.xml
+++ b/packages/PackageInstaller/res/values-es/strings.xml
@@ -19,7 +19,7 @@
<string name="app_name" msgid="7488448184431507488">"Instalador de paquetes"</string>
<string name="install" msgid="711829760615509273">"Instalar"</string>
<string name="update" msgid="3932142540719227615">"Actualizar"</string>
- <string name="done" msgid="6632441120016885253">"Listo"</string>
+ <string name="done" msgid="6632441120016885253">"Hecho"</string>
<string name="cancel" msgid="1018267193425558088">"Cancelar"</string>
<string name="installing" msgid="4921993079741206516">"Instalando…"</string>
<string name="installing_app" msgid="1165095864863849422">"Instalando <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
diff --git a/packages/PackageInstaller/res/values-gl/strings.xml b/packages/PackageInstaller/res/values-gl/strings.xml
index 9d3b775..637e15a 100644
--- a/packages/PackageInstaller/res/values-gl/strings.xml
+++ b/packages/PackageInstaller/res/values-gl/strings.xml
@@ -46,7 +46,7 @@
<string name="out_of_space_dlg_text" msgid="8727714096031856231">"Non se puido instalar a aplicación <xliff:g id="APP_NAME">%1$s</xliff:g>. Libera espazo e téntao de novo."</string>
<string name="app_not_found_dlg_title" msgid="5107924008597470285">"Non se atopou a aplicación"</string>
<string name="app_not_found_dlg_text" msgid="5219983779377811611">"Non se atopou a aplicación na lista de aplicacións instaladas."</string>
- <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"Acción non-permitida"</string>
+ <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"Sen permiso"</string>
<string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"O usuario actual non pode realizar esta desinstalación."</string>
<string name="generic_error_dlg_title" msgid="5863195085927067752">"Erro"</string>
<string name="generic_error_dlg_text" msgid="5287861443265795232">"Non se puido desinstalar a aplicación."</string>
@@ -54,10 +54,10 @@
<string name="uninstall_update_title" msgid="824411791011583031">"Desinstalar actualización"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> forma parte da seguinte aplicación:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Queres desinstalar esta aplicación?"</string>
- <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Queres desinstalar esta aplicación para "<b>"todos"</b>" os usuarios? A aplicación e os seus datos eliminaranse de "<b>"todos"</b>" os usuarios do dispositivo."</string>
+ <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Queres desinstalar esta aplicación para "<b>"todos"</b>" os usuarios? A aplicación e os seus datos quitaranse de "<b>"todos"</b>" os usuarios do dispositivo."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"Queres desinstalar esta aplicación para o usuario que se chama <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
- <string name="uninstall_update_text" msgid="863648314632448705">"Queres substituír esta aplicación pola versión que viña de fábrica? Eliminaranse todos os datos."</string>
- <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Queres substituír esta aplicación pola versión que viña de fábrica? Eliminaranse todos os datos. Isto afectará a todos os usuarios do dispositivo, incluídos os que teñan perfís de traballo."</string>
+ <string name="uninstall_update_text" msgid="863648314632448705">"Queres substituír esta aplicación pola versión que viña de fábrica? Quitaranse todos os datos."</string>
+ <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Queres substituír esta aplicación pola versión que viña de fábrica? Quitaranse todos os datos. Isto afectará a todos os usuarios do dispositivo, incluídos os que teñan perfís de traballo."</string>
<string name="uninstall_keep_data" msgid="7002379587465487550">"Conservar os datos da aplicación, que ocupan <xliff:g id="SIZE">%1$s</xliff:g>."</string>
<string name="uninstalling_notification_channel" msgid="840153394325714653">"Desinstalacións en curso"</string>
<string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Erros nas desinstalacións"</string>
diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml
index 42c3cfe..c554aab 100644
--- a/packages/PackageInstaller/res/values-it/strings.xml
+++ b/packages/PackageInstaller/res/values-it/strings.xml
@@ -46,7 +46,7 @@
<string name="out_of_space_dlg_text" msgid="8727714096031856231">"Impossibile installare <xliff:g id="APP_NAME">%1$s</xliff:g>. Libera dello spazio e riprova."</string>
<string name="app_not_found_dlg_title" msgid="5107924008597470285">"App non trovata"</string>
<string name="app_not_found_dlg_text" msgid="5219983779377811611">"Impossibile trovare l\'applicazione nell\'elenco di applicazioni installate."</string>
- <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"Non consentita"</string>
+ <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"Autorizzazione non concessa"</string>
<string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"L\'utente corrente non è autorizzato a eseguire questa disinstallazione."</string>
<string name="generic_error_dlg_title" msgid="5863195085927067752">"Errore"</string>
<string name="generic_error_dlg_text" msgid="5287861443265795232">"Impossibile disinstallare l\'app."</string>
diff --git a/packages/PrintSpooler/res/values-az/strings.xml b/packages/PrintSpooler/res/values-az/strings.xml
index 4193afc..887434b 100644
--- a/packages/PrintSpooler/res/values-az/strings.xml
+++ b/packages/PrintSpooler/res/values-az/strings.xml
@@ -83,7 +83,7 @@
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ləğv edilir"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printer xətası <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="blocked_notification_title_template" msgid="1175435827331588646">"Printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> işini blokladı"</string>
- <string name="cancel" msgid="4373674107267141885">"Ləğv et"</string>
+ <string name="cancel" msgid="4373674107267141885">"Ləğv edin"</string>
<string name="restart" msgid="2472034227037808749">"Yenidən başlat"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printerə heç bir bağlantı yoxdur"</string>
<string name="reason_unknown" msgid="5507940196503246139">"naməlum"</string>
diff --git a/packages/PrintSpooler/res/values-eu/strings.xml b/packages/PrintSpooler/res/values-eu/strings.xml
index 7ccccc9f..49ca881 100644
--- a/packages/PrintSpooler/res/values-eu/strings.xml
+++ b/packages/PrintSpooler/res/values-eu/strings.xml
@@ -49,10 +49,10 @@
<string name="print_options_collapsed" msgid="7455930445670414332">"Inprimatzeko aukerak tolestuta daude"</string>
<string name="search" msgid="5421724265322228497">"Bilatu"</string>
<string name="all_printers_label" msgid="3178848870161526399">"Inprimagailu guztiak"</string>
- <string name="add_print_service_label" msgid="5356702546188981940">"Gehitu zerbitzua"</string>
+ <string name="add_print_service_label" msgid="5356702546188981940">"Gehitu zerbitzu bat"</string>
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Bilaketa-koadroa erakutsi da"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Bilaketa-koadroa ezkutatu da"</string>
- <string name="print_add_printer" msgid="1088656468360653455">"Gehitu inprimagailua"</string>
+ <string name="print_add_printer" msgid="1088656468360653455">"Gehitu inprimagailu bat"</string>
<string name="print_select_printer" msgid="7388760939873368698">"Hautatu inprimagailua"</string>
<string name="print_forget_printer" msgid="5035287497291910766">"Ahaztu inprimagailua"</string>
<plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
diff --git a/packages/PrintSpooler/res/values-fi/strings.xml b/packages/PrintSpooler/res/values-fi/strings.xml
index 724d1d7..4289399 100644
--- a/packages/PrintSpooler/res/values-fi/strings.xml
+++ b/packages/PrintSpooler/res/values-fi/strings.xml
@@ -83,7 +83,7 @@
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Peruutetaan työ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Tulostinvirhe työlle <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="blocked_notification_title_template" msgid="1175435827331588646">"Tulostin esti työn <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
- <string name="cancel" msgid="4373674107267141885">"Peruuta"</string>
+ <string name="cancel" msgid="4373674107267141885">"Peru"</string>
<string name="restart" msgid="2472034227037808749">"Käynnistä uudelleen"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ei yhteyttä tulostimeen"</string>
<string name="reason_unknown" msgid="5507940196503246139">"tuntematon"</string>
diff --git a/packages/PrintSpooler/res/values-gu/strings.xml b/packages/PrintSpooler/res/values-gu/strings.xml
index 4149a86..7419c2a 100644
--- a/packages/PrintSpooler/res/values-gu/strings.xml
+++ b/packages/PrintSpooler/res/values-gu/strings.xml
@@ -65,7 +65,7 @@
<string name="notification_channel_failure" msgid="9042250774797916414">"નિષ્ફળ થયેલ છાપવાના Tasks"</string>
<string name="could_not_create_file" msgid="3425025039427448443">"ફાઇલ બનાવી શક્યાં નથી"</string>
<string name="print_services_disabled_toast" msgid="9089060734685174685">"કેટલીક છાપવાની સેવાઓ અક્ષમ કરેલ છે"</string>
- <string name="print_searching_for_printers" msgid="6550424555079932867">"પ્રિન્ટર્સ માટે શોધી રહ્યું છે"</string>
+ <string name="print_searching_for_printers" msgid="6550424555079932867">"પ્રિન્ટર માટે શોધી રહ્યું છે"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"કોઈ છાપ સેવાઓ સક્ષમ કરેલ નથી"</string>
<string name="print_no_printers" msgid="4869403323900054866">"કોઈ પ્રિન્ટર મળ્યા નથી"</string>
<string name="cannot_add_printer" msgid="7840348733668023106">"પ્રિન્ટર્સ ઉમેરી શકતાં નથી"</string>
diff --git a/packages/PrintSpooler/res/values-kk/strings.xml b/packages/PrintSpooler/res/values-kk/strings.xml
index a822d1c..29126bc 100644
--- a/packages/PrintSpooler/res/values-kk/strings.xml
+++ b/packages/PrintSpooler/res/values-kk/strings.xml
@@ -94,7 +94,7 @@
<item msgid="2762241247228983754">"Түс"</item>
</string-array>
<string-array name="duplex_mode_labels">
- <item msgid="3882302912790928315">"Ешқандай"</item>
+ <item msgid="3882302912790928315">"Жоқ"</item>
<item msgid="7296563835355641719">"Ұзын жиек"</item>
<item msgid="79513688117503758">"Қысқа жиек"</item>
</string-array>
diff --git a/packages/PrintSpooler/res/values-mk/strings.xml b/packages/PrintSpooler/res/values-mk/strings.xml
index 504c956..3fd32b1 100644
--- a/packages/PrintSpooler/res/values-mk/strings.xml
+++ b/packages/PrintSpooler/res/values-mk/strings.xml
@@ -49,7 +49,7 @@
<string name="print_options_collapsed" msgid="7455930445670414332">"Опциите на печатачот се сокриени"</string>
<string name="search" msgid="5421724265322228497">"Пребарај"</string>
<string name="all_printers_label" msgid="3178848870161526399">"Сите печатачи"</string>
- <string name="add_print_service_label" msgid="5356702546188981940">"Додај услуга"</string>
+ <string name="add_print_service_label" msgid="5356702546188981940">"Додајте услуга"</string>
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Полето за пребарување е прикажано"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Полето за пребарување е скриено"</string>
<string name="print_add_printer" msgid="1088656468360653455">"Додај печатач"</string>
diff --git a/packages/PrintSpooler/res/values-my/strings.xml b/packages/PrintSpooler/res/values-my/strings.xml
index a6b07e1..cb0b899 100644
--- a/packages/PrintSpooler/res/values-my/strings.xml
+++ b/packages/PrintSpooler/res/values-my/strings.xml
@@ -52,7 +52,7 @@
<string name="add_print_service_label" msgid="5356702546188981940">"ဝန်ဆောင်မှုထည့်ရန်"</string>
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"ရှာဖွေစရာ နေရာ မြင်တွေ့ရပါသည်"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"ရှာဖွေရန် နေရာ ပျောက်ကွယ်နေပါသည်"</string>
- <string name="print_add_printer" msgid="1088656468360653455">"စာထုတ်စက်ကို ထည့်ပါ"</string>
+ <string name="print_add_printer" msgid="1088656468360653455">"ပရင်တာထည့်ရန်"</string>
<string name="print_select_printer" msgid="7388760939873368698">"စာထုတ်စက်ကို ရွေးရန်"</string>
<string name="print_forget_printer" msgid="5035287497291910766">"စာထုတ်စက်ကို မေ့လိုက်ရန်"</string>
<plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
@@ -65,7 +65,7 @@
<string name="notification_channel_failure" msgid="9042250774797916414">"မအောင်မြင်သည့် ပရင့်ထုတ်မှုများ"</string>
<string name="could_not_create_file" msgid="3425025039427448443">"ဖိုင်အမည်ကို ထည့်၍မရပါ"</string>
<string name="print_services_disabled_toast" msgid="9089060734685174685">"အချို့ပုံနှိပ်ဝန်ဆောင်မှုများကို ပိတ်ထားပါသည်"</string>
- <string name="print_searching_for_printers" msgid="6550424555079932867">"ပုံနှိပ်စက်များကို ရှာနေသည်"</string>
+ <string name="print_searching_for_printers" msgid="6550424555079932867">"ပရင်တာများကို ရှာနေသည်"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ပုံနှိပ်ထုတ်ယူရေး ဝန်ဆောင်မှုများ ဖွင့်မထားပါ"</string>
<string name="print_no_printers" msgid="4869403323900054866">"စာထုတ်စက် တစ်ခုမှ မတွေ့ရှိပါ"</string>
<string name="cannot_add_printer" msgid="7840348733668023106">"ပုံနှိပ်စက်များကို ထည့်၍မရပါ"</string>
diff --git a/packages/PrintSpooler/res/values-ne/strings.xml b/packages/PrintSpooler/res/values-ne/strings.xml
index d0b7a5e4..be7af70 100644
--- a/packages/PrintSpooler/res/values-ne/strings.xml
+++ b/packages/PrintSpooler/res/values-ne/strings.xml
@@ -35,7 +35,7 @@
<string name="install_for_print_preview" msgid="6366303997385509332">"पूर्वावलोकनको लागि PDF भ्यूअर स्थापना गर्नुहोस्"</string>
<string name="printing_app_crashed" msgid="854477616686566398">"प्रिन्टिङ एप क्र्यास भयो"</string>
<string name="generating_print_job" msgid="3119608742651698916">"प्रिन्ट कार्य निर्माण गरिँदै"</string>
- <string name="save_as_pdf" msgid="5718454119847596853">"PDF को रूपमा सुरक्षित गर्नुहोस्"</string>
+ <string name="save_as_pdf" msgid="5718454119847596853">"PDF को रूपमा सेभ गर्नुहोस्"</string>
<string name="all_printers" msgid="5018829726861876202">"सबै प्रिन्टरहरू..."</string>
<string name="print_dialog" msgid="32628687461331979">"सम्वाद प्रिन्ट गर्नुहोस्"</string>
<string name="current_page_template" msgid="5145005201131935302">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
@@ -44,7 +44,7 @@
<string name="expand_handle" msgid="7282974448109280522">"ह्यान्डल विस्तार गर्नुहोस्"</string>
<string name="collapse_handle" msgid="6886637989442507451">"ह्यान्डल कोल्याप्स गर्नुहोस्"</string>
<string name="print_button" msgid="645164566271246268">"प्रिन्ट गर्नुहोस्"</string>
- <string name="savetopdf_button" msgid="2976186791686924743">"PDF सुरक्षित गर्नुहोस्"</string>
+ <string name="savetopdf_button" msgid="2976186791686924743">"PDF सेभ गर्नुहोस्"</string>
<string name="print_options_expanded" msgid="6944679157471691859">"विस्तार गरेका विकल्पहरू प्रिन्ट गर्नुहोस्"</string>
<string name="print_options_collapsed" msgid="7455930445670414332">"कोल्याप्स गरेका विकल्पहरू प्रिन्ट गर्नुहोस्"</string>
<string name="search" msgid="5421724265322228497">"खोज्नुहोस्"</string>
@@ -65,7 +65,7 @@
<string name="notification_channel_failure" msgid="9042250774797916414">"कार्यहरूलाई छाप्न सकिएन"</string>
<string name="could_not_create_file" msgid="3425025039427448443">"फाइल सिर्जना गर्न सकिएन"</string>
<string name="print_services_disabled_toast" msgid="9089060734685174685">"केही प्रिन्टिङ सम्बन्धी सेवाहरूलाई असक्षम गरिएको छ"</string>
- <string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिन्टरहरू खोज्दै"</string>
+ <string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिन्टरहरू खोजिँदै छ"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"कुनै पनि प्रिन्टिङ सेवाहरू सक्रिय छैनन्"</string>
<string name="print_no_printers" msgid="4869403323900054866">"कुनै प्रिन्टरहरू भेटाइएन"</string>
<string name="cannot_add_printer" msgid="7840348733668023106">"प्रिन्टरहरू थप्न सक्दैन"</string>
diff --git a/packages/PrintSpooler/res/values-pa/strings.xml b/packages/PrintSpooler/res/values-pa/strings.xml
index 1cacc10..601fa83 100644
--- a/packages/PrintSpooler/res/values-pa/strings.xml
+++ b/packages/PrintSpooler/res/values-pa/strings.xml
@@ -65,7 +65,7 @@
<string name="notification_channel_failure" msgid="9042250774797916414">"ਅਸਫਲ ਰਹੇ ਪ੍ਰਿੰਟ ਜੌਬ"</string>
<string name="could_not_create_file" msgid="3425025039427448443">"ਫ਼ਾਈਲ ਨੂੰ ਬਣਾਇਆ ਨਹੀਂ ਜਾ ਸਕਿਆ"</string>
<string name="print_services_disabled_toast" msgid="9089060734685174685">"ਕੁਝ ਪ੍ਰਿੰਟ ਸੇਵਾਵਾਂ ਬੰਦ ਕੀਤੀਆਂ ਗਈਆਂ ਹਨ"</string>
- <string name="print_searching_for_printers" msgid="6550424555079932867">"ਪ੍ਰਿੰਟਰ ਖੋਜ ਰਿਹਾ ਹੈ"</string>
+ <string name="print_searching_for_printers" msgid="6550424555079932867">"ਪ੍ਰਿੰਟਰ ਖੋਜਿਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ਪ੍ਰਿੰਟ ਸੇਵਾਵਾਂ ਨੂੰ ਚਾਲੂ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
<string name="print_no_printers" msgid="4869403323900054866">"ਕੋਈ ਪ੍ਰਿੰਟਰ ਨਹੀਂ ਮਿਲੇ"</string>
<string name="cannot_add_printer" msgid="7840348733668023106">"ਪ੍ਰਿੰਟਰ ਸ਼ਾਮਲ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ"</string>
diff --git a/packages/PrintSpooler/res/values-te/strings.xml b/packages/PrintSpooler/res/values-te/strings.xml
index 79944bb..cf0e0f6 100644
--- a/packages/PrintSpooler/res/values-te/strings.xml
+++ b/packages/PrintSpooler/res/values-te/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"ముద్రణ స్పూలర్"</string>
- <string name="more_options_button" msgid="2243228396432556771">"మరిన్ని ఎంపికలు"</string>
+ <string name="more_options_button" msgid="2243228396432556771">"మరిన్ని ఆప్షన్లు"</string>
<string name="label_destination" msgid="9132510997381599275">"గమ్యం"</string>
<string name="label_copies" msgid="3634531042822968308">"కాపీలు"</string>
<string name="label_copies_summary" msgid="3861966063536529540">"కాపీలు:"</string>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
index a24456e..f57061a 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-gu/strings.xml
@@ -18,5 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"વ્યવસ્થાપકે ચાલુ કરેલ"</string>
- <string name="disabled_by_admin" msgid="4023569940620832713">"વ્યવસ્થાપકે બંધ કરેલ"</string>
+ <string name="disabled_by_admin" msgid="4023569940620832713">"વ્યવસ્થાપકે બંધ કરેલું"</string>
</resources>
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
index 199a2d6..bddf43c 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values-it/strings.xml
@@ -18,5 +18,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enabled_by_admin" msgid="6630472777476410137">"Attivata dall\'amministratore"</string>
- <string name="disabled_by_admin" msgid="4023569940620832713">"Disattivata dall\'amministratore"</string>
+ <string name="disabled_by_admin" msgid="4023569940620832713">"Opzione disattivata dall\'amministratore"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-or/strings.xml b/packages/SettingsLib/SearchWidget/res/values-or/strings.xml
index c2379ac..cf824de 100644
--- a/packages/SettingsLib/SearchWidget/res/values-or/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-or/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1914043873178389845">"ସନ୍ଧାନ ସେଟିଂସ୍"</string>
+ <string name="search_menu" msgid="1914043873178389845">"ସେଟିଂସ୍ ସନ୍ଧାନ କରନ୍ତୁ"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
index 5fe116e..02229b8 100644
--- a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1914043873178389845">"Pesquisar nas definições"</string>
+ <string name="search_menu" msgid="1914043873178389845">"Pesquise nas definições"</string>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles_preference.xml b/packages/SettingsLib/SettingsTheme/res/values/styles_preference.xml
index dcbdc07..cec8b32 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles_preference.xml
@@ -15,7 +15,7 @@
limitations under the License.
-->
<resources>
- <!--DEPRECATED. It will remove after all of client team migrated to new style. -->
+ <!--DEPRECATED. It will be removed after all of client teams migrated to new style. -->
<style name="PreferenceTheme" parent="@style/PreferenceThemeOverlay">
<item name="preferenceCategoryStyle">@style/SettingsCategoryPreference</item>
<item name="preferenceStyle">@style/SettingsPreference</item>
@@ -28,7 +28,7 @@
<item name="footerPreferenceStyle">@style/Preference.Material</item>
</style>
- <style name="SettingsPreferenceTheme" parent="@style/PreferenceThemeOverlay">
+ <style name="PreferenceTheme.SettingsBase" parent="@style/PreferenceThemeOverlay">
<item name="preferenceCategoryStyle">@style/SettingsCategoryPreference</item>
<item name="preferenceStyle">@style/SettingsPreference</item>
<item name="checkBoxPreferenceStyle">@style/SettingsCheckBoxPreference</item>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/themes.xml b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
index 13c7523..9c096d2 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
@@ -21,7 +21,7 @@
<item name="android:textAppearanceListItem">@style/TextAppearance.PreferenceTitle</item>
<item name="android:listPreferredItemPaddingStart">@dimen/preference_padding_start</item>
<item name="android:listPreferredItemPaddingEnd">@dimen/preference_padding_end</item>
- <item name="preferenceTheme">@style/SettingsPreferenceTheme</item>
+ <item name="preferenceTheme">@style/PreferenceTheme.SettingsBase</item>
</style>
<!-- Using in SubSettings page including injected settings page -->
diff --git a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
index 31b3fe5..d2c6fa2 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
+++ b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
@@ -24,29 +24,27 @@
android:orientation="vertical"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
- android:paddingTop="32dp"
- android:paddingBottom="32dp">
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/usage_summary"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- app:layout_constraintWidth_percent="0.45"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBaseline_toBaselineOf="@id/total_summary"
android:ellipsize="marquee"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Display1"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"
android:textSize="14sp"
android:textAlignment="viewStart"/>
<TextView
android:id="@+id/total_summary"
android:layout_width="0dp"
android:layout_height="wrap_content"
- app:layout_constraintWidth_percent="0.45"
+ app:layout_constraintStart_toEndOf="@id/usage_summary"
app:layout_constraintEnd_toStartOf="@id/custom_content"
android:ellipsize="marquee"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"
@@ -54,12 +52,11 @@
android:textAlignment="viewEnd"/>
<FrameLayout
android:id="@+id/custom_content"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="@id/total_summary"
- app:layout_constraintWidth_percent="0.1"/>
+ app:layout_constraintBottom_toBottomOf="@id/total_summary"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<ProgressBar
@@ -67,8 +64,7 @@
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:scaleY="2"
- android:layout_marginTop="4dp"
+ android:scaleY="4"
android:max="100"/>
<TextView
diff --git a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
index a2b1de2..fbf325c 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
+++ b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
@@ -20,7 +20,7 @@
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
-import android.text.style.RelativeSizeSpan;
+import android.text.style.AbsoluteSizeSpan;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
@@ -192,7 +192,7 @@
final Matcher matcher = mNumberPattern.matcher(summary);
if (matcher.find()) {
final SpannableString spannableSummary = new SpannableString(summary);
- spannableSummary.setSpan(new RelativeSizeSpan(2.4f), matcher.start(),
+ spannableSummary.setSpan(new AbsoluteSizeSpan(64, true /* dip */), matcher.start(),
matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableSummary;
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 14ccd80..70b826a 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1014,6 +1014,9 @@
<!-- Settings item title to select whether to show transcoding notifications. [CHAR LIMIT=85] -->
<string name="transcode_notification">Show transcoding notifications</string>
+ <!-- Settings item title to select whether to disable cache for transcoding. [CHAR LIMIT=85] -->
+ <string name="transcode_disable_cache">Disable transcoding cache</string>
+
<!-- Services settings screen, setting option name for the user to go to the screen to view running services -->
<string name="runningservices_settings_title">Running services</string>
<!-- Services settings screen, setting option summary for the user to go to the screen to view running services -->
diff --git a/packages/SettingsProvider/res/values-es/strings.xml b/packages/SettingsProvider/res/values-es/strings.xml
index a3d3469..c799689 100644
--- a/packages/SettingsProvider/res/values-es/strings.xml
+++ b/packages/SettingsProvider/res/values-es/strings.xml
@@ -20,6 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4567566098528588863">"Almacenamiento de configuración"</string>
- <string name="wifi_softap_config_change" msgid="5688373762357941645">"Se han cambiado los ajustes del punto de acceso"</string>
+ <string name="wifi_softap_config_change" msgid="5688373762357941645">"Se han cambiado los ajustes de Compartir Internet"</string>
<string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"Toca para ver información detallada"</string>
</resources>
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1f7672f..0c47cf8 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -162,6 +162,7 @@
<uses-permission android:name="android.permission.READ_INPUT_STATE" />
<uses-permission android:name="android.permission.SET_ORIENTATION" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+ <uses-permission android:name="com.android.permission.USE_INSTALLER_V2" />
<uses-permission android:name="com.android.permission.USE_SYSTEM_DATA_LOADERS" />
<uses-permission android:name="android.permission.MOVE_PACKAGE" />
<uses-permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES" />
@@ -541,6 +542,12 @@
<!-- Permission required for CTS test - CtsRotationResolverServiceDeviceTestCases -->
<uses-permission android:name="android.permission.MANAGE_ROTATION_RESOLVER" />
+ <!-- Permission required for CTS test - CtsUwbTestCases -->
+ <uses-permission android:name="android.permission.UWB_PRIVILEGED" />
+
+ <!-- Permission required for CTS test - CtsAlarmManagerTestCases -->
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/Shell/TEST_MAPPING b/packages/Shell/TEST_MAPPING
index a149b5c..9bb1b4b 100644
--- a/packages/Shell/TEST_MAPPING
+++ b/packages/Shell/TEST_MAPPING
@@ -18,6 +18,20 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CtsUiAutomationTestCases",
+ "options": [
+ {
+ "include-filter": "android.app.uiautomation.cts.UiAutomationTest#testAdoptAllShellPermissions"
+ },
+ {
+ "include-filter": "android.app.uiautomation.cts.UiAutomationTest#testAdoptSomeShellPermissions"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
],
"postsubmit": [
diff --git a/packages/Shell/res/values-ne/strings.xml b/packages/Shell/res/values-ne/strings.xml
index 3c58796..69da552 100644
--- a/packages/Shell/res/values-ne/strings.xml
+++ b/packages/Shell/res/values-ne/strings.xml
@@ -42,6 +42,6 @@
<string name="bugreport_info_name" msgid="4414036021935139527">"फाइलको नाम"</string>
<string name="bugreport_info_title" msgid="2306030793918239804">"बगको शीर्षक"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"बगको सारांश"</string>
- <string name="save" msgid="4781509040564835759">"सुरक्षित गर्नुहोस्"</string>
+ <string name="save" msgid="4781509040564835759">"सेभ गर्नुहोस्"</string>
<string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"बग रिपोर्ट सेयर गर्नुहोस्"</string>
</resources>
diff --git a/packages/Shell/res/values-vi/strings.xml b/packages/Shell/res/values-vi/strings.xml
index 1f91a5a..2aaa094 100644
--- a/packages/Shell/res/values-vi/strings.xml
+++ b/packages/Shell/res/values-vi/strings.xml
@@ -29,7 +29,7 @@
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Bấm để chia sẻ báo cáo lỗi mà không cần ảnh chụp màn hình hoặc đợi hoàn tất ảnh chụp màn hình"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Bấm để chia sẻ báo cáo lỗi mà không cần ảnh chụp màn hình hoặc đợi hoàn tất ảnh chụp màn hình"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Các báo cáo lỗi chứa dữ liệu từ nhiều tệp nhật ký khác nhau của hệ thống, có thể bao gồm dữ liệu mà bạn coi là nhạy cảm (chẳng hạn như dữ liệu vị trí và dữ liệu sử dụng ứng dụng). Chỉ chia sẻ báo cáo lỗi với những người và ứng dụng mà bạn tin tưởng."</string>
- <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Không hiển thị lại"</string>
+ <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Không hiện lại"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Báo cáo lỗi"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"Không thể đọc tệp báo cáo lỗi"</string>
<string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Không thể thêm chi tiết báo cáo lỗi vào tệp zip"</string>
diff --git a/packages/SoundPicker/res/values-gu/strings.xml b/packages/SoundPicker/res/values-gu/strings.xml
index f50dc9a..209769f 100644
--- a/packages/SoundPicker/res/values-gu/strings.xml
+++ b/packages/SoundPicker/res/values-gu/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="ringtone_default" msgid="798836092118824500">"ડિફોલ્ટ રિંગટોન"</string>
<string name="notification_sound_default" msgid="8133121186242636840">"ડિફૉલ્ટ નોટિફિકેશન સાઉન્ડ"</string>
- <string name="alarm_sound_default" msgid="4787646764557462649">"ડિફૉલ્ટ એલાર્મ સાઉન્ડ"</string>
+ <string name="alarm_sound_default" msgid="4787646764557462649">"ડિફૉલ્ટ અલાર્મ સાઉન્ડ"</string>
<string name="add_ringtone_text" msgid="6642389991738337529">"રિંગટોન ઉમેરો"</string>
<string name="add_alarm_text" msgid="3545497316166999225">"અલાર્મ ઉમેરો"</string>
<string name="add_notification_text" msgid="4431129543300614788">"નોટિફિકેશન ઉમેરો"</string>
diff --git a/packages/SoundPicker/res/values-it/strings.xml b/packages/SoundPicker/res/values-it/strings.xml
index 20965d0..632cb41 100644
--- a/packages/SoundPicker/res/values-it/strings.xml
+++ b/packages/SoundPicker/res/values-it/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="ringtone_default" msgid="798836092118824500">"Suoneria predefinita"</string>
<string name="notification_sound_default" msgid="8133121186242636840">"Suono di notifica predefinito"</string>
- <string name="alarm_sound_default" msgid="4787646764557462649">"Suono sveglia predefinito"</string>
+ <string name="alarm_sound_default" msgid="4787646764557462649">"Suoneria sveglia predefinita"</string>
<string name="add_ringtone_text" msgid="6642389991738337529">"Aggiungi suoneria"</string>
<string name="add_alarm_text" msgid="3545497316166999225">"Aggiungi sveglia"</string>
<string name="add_notification_text" msgid="4431129543300614788">"Aggiungi notifica"</string>
diff --git a/packages/SoundPicker/res/values-kk/strings.xml b/packages/SoundPicker/res/values-kk/strings.xml
index ad6d0e0..8c4c169 100644
--- a/packages/SoundPicker/res/values-kk/strings.xml
+++ b/packages/SoundPicker/res/values-kk/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="ringtone_default" msgid="798836092118824500">"Әдепкі рингтон"</string>
<string name="notification_sound_default" msgid="8133121186242636840">"Әдепкі хабарландыру дыбысы"</string>
- <string name="alarm_sound_default" msgid="4787646764557462649">"Әдепкі дабыл дыбысы"</string>
+ <string name="alarm_sound_default" msgid="4787646764557462649">"Әдепкі оятқыш дыбысы"</string>
<string name="add_ringtone_text" msgid="6642389991738337529">"Рингтон қосу"</string>
<string name="add_alarm_text" msgid="3545497316166999225">"Оятқыш енгізу"</string>
<string name="add_notification_text" msgid="4431129543300614788">"Хабарландыру енгізу"</string>
diff --git a/packages/SoundPicker/res/values-mr/strings.xml b/packages/SoundPicker/res/values-mr/strings.xml
index eb55fc7..3ddb991 100644
--- a/packages/SoundPicker/res/values-mr/strings.xml
+++ b/packages/SoundPicker/res/values-mr/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="ringtone_default" msgid="798836092118824500">"डीफॉल्ट रिंगटोन"</string>
<string name="notification_sound_default" msgid="8133121186242636840">"डीफॉल्ट सूचना आवाज"</string>
- <string name="alarm_sound_default" msgid="4787646764557462649">"डीफॉल्ट अलार्म ध्वनी"</string>
+ <string name="alarm_sound_default" msgid="4787646764557462649">"डीफॉल्ट अलार्म आवाज"</string>
<string name="add_ringtone_text" msgid="6642389991738337529">"रिंगटोन जोडा"</string>
<string name="add_alarm_text" msgid="3545497316166999225">"अलार्म जोडा"</string>
<string name="add_notification_text" msgid="4431129543300614788">"सूचना जोडा"</string>
diff --git a/packages/SoundPicker/res/values-ne/strings.xml b/packages/SoundPicker/res/values-ne/strings.xml
index 7dc7893..0a2bceb 100644
--- a/packages/SoundPicker/res/values-ne/strings.xml
+++ b/packages/SoundPicker/res/values-ne/strings.xml
@@ -16,9 +16,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="ringtone_default" msgid="798836092118824500">"पूर्वनिर्धारित रिङटोन"</string>
- <string name="notification_sound_default" msgid="8133121186242636840">"सूचनाको पूर्वनिर्धारित ध्वनि"</string>
- <string name="alarm_sound_default" msgid="4787646764557462649">"अलार्मका लागि पूर्वनिर्धारित ध्वनि"</string>
+ <string name="ringtone_default" msgid="798836092118824500">"डिफल्ट रिङटोन"</string>
+ <string name="notification_sound_default" msgid="8133121186242636840">"सूचनाको डिफल्ट साउन्ड"</string>
+ <string name="alarm_sound_default" msgid="4787646764557462649">"अलार्मको डिफल्ट साउन्ड"</string>
<string name="add_ringtone_text" msgid="6642389991738337529">"रिङटोन थप्नुहोस्"</string>
<string name="add_alarm_text" msgid="3545497316166999225">"अलार्म थप्नुहोस्"</string>
<string name="add_notification_text" msgid="4431129543300614788">"सूचना थप्नुहोस्"</string>
diff --git a/packages/SoundPicker/res/values-pa/strings.xml b/packages/SoundPicker/res/values-pa/strings.xml
index eb630c9..1e62f64 100644
--- a/packages/SoundPicker/res/values-pa/strings.xml
+++ b/packages/SoundPicker/res/values-pa/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="ringtone_default" msgid="798836092118824500">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਰਿੰਗਟੋਨ"</string>
<string name="notification_sound_default" msgid="8133121186242636840">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੂਚਨਾ ਧੁਨੀ"</string>
- <string name="alarm_sound_default" msgid="4787646764557462649">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਅਲਾਰਮ ਧੁਨੀ"</string>
+ <string name="alarm_sound_default" msgid="4787646764557462649">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਅਲਾਰਮ ਧੁਨੀ"</string>
<string name="add_ringtone_text" msgid="6642389991738337529">"ਰਿੰਗਟੋਨ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="add_alarm_text" msgid="3545497316166999225">"ਅਲਾਰਮ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="add_notification_text" msgid="4431129543300614788">"ਸੂਚਨਾ ਸ਼ਾਮਲ ਕਰੋ"</string>
diff --git a/packages/SoundPicker/res/values-vi/strings.xml b/packages/SoundPicker/res/values-vi/strings.xml
index bf5c33a..bed0e96 100644
--- a/packages/SoundPicker/res/values-vi/strings.xml
+++ b/packages/SoundPicker/res/values-vi/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="ringtone_default" msgid="798836092118824500">"Nhạc chuông mặc định"</string>
<string name="notification_sound_default" msgid="8133121186242636840">"Âm thanh thông báo mặc định"</string>
- <string name="alarm_sound_default" msgid="4787646764557462649">"Âm thanh báo thức mặc định"</string>
+ <string name="alarm_sound_default" msgid="4787646764557462649">"Âm thanh chuông báo mặc định"</string>
<string name="add_ringtone_text" msgid="6642389991738337529">"Thêm nhạc chuông"</string>
<string name="add_alarm_text" msgid="3545497316166999225">"Thêm báo thức"</string>
<string name="add_notification_text" msgid="4431129543300614788">"Thêm thông báo"</string>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a834784..173e959 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -762,7 +762,7 @@
android:showForAllUsers="true"
android:finishOnTaskLaunch="true"
android:launchMode="singleInstance"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
android:visibleToInstantApps="true">
</activity>
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index da78a7c..03d844a 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -31,14 +31,16 @@
companion object {
const val ANIMATION_DURATION = 500L
const val ANIMATION_DURATION_FADE_OUT_CONTENT = 183L
- const val ANIMATION_DURATION_FADE_IN_WINDOW = 216L
- const val ANIMATION_DELAY_FADE_IN_WINDOW = 166L
+ const val ANIMATION_DURATION_FADE_IN_WINDOW = 217L
+ const val ANIMATION_DELAY_FADE_IN_WINDOW = 167L
private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L
private const val ANIMATION_DELAY_NAV_FADE_IN =
ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN
private const val LAUNCH_TIMEOUT = 1000L
+ private val CONTENT_FADE_OUT_INTERPOLATOR = PathInterpolator(0f, 0f, 0.2f, 1f)
+ private val WINDOW_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0.6f, 1f)
private val NAV_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0f, 1f)
private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
@@ -313,12 +315,14 @@
}
context.mainExecutor.execute {
- startAnimation(remoteAnimationTargets, iRemoteAnimationFinishedCallback)
+ startAnimation(remoteAnimationTargets, remoteAnimationNonAppTargets,
+ iRemoteAnimationFinishedCallback)
}
}
private fun startAnimation(
remoteAnimationTargets: Array<out RemoteAnimationTarget>,
+ remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>,
iCallback: IRemoteAnimationFinishedCallback
) {
val window = remoteAnimationTargets.firstOrNull {
@@ -332,7 +336,7 @@
return
}
- val navigationBar = remoteAnimationTargets.firstOrNull {
+ val navigationBar = remoteAnimationNonAppTargets.firstOrNull {
it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
}
@@ -416,12 +420,12 @@
val contentAlphaProgress = getProgress(linearProgress, 0,
ANIMATION_DURATION_FADE_OUT_CONTENT)
state.contentAlpha =
- 1 - Interpolators.ALPHA_OUT.getInterpolation(contentAlphaProgress)
+ 1 - CONTENT_FADE_OUT_INTERPOLATOR.getInterpolation(contentAlphaProgress)
val backgroundAlphaProgress = getProgress(linearProgress,
ANIMATION_DELAY_FADE_IN_WINDOW, ANIMATION_DURATION_FADE_IN_WINDOW)
state.backgroundAlpha =
- 1 - Interpolators.ALPHA_IN.getInterpolation(backgroundAlphaProgress)
+ 1 - WINDOW_FADE_IN_INTERPOLATOR.getInterpolation(backgroundAlphaProgress)
applyStateToWindow(window, state)
navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 01ec447..3da4521 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -9,6 +9,7 @@
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.InsetDrawable
import android.graphics.drawable.LayerDrawable
import android.view.GhostView
import android.view.View
@@ -186,6 +187,10 @@
return drawable
}
+ if (drawable is InsetDrawable) {
+ return drawable.drawable?.let { findGradientDrawable(it) }
+ }
+
if (drawable is LayerDrawable) {
for (i in 0 until drawable.numberOfLayers) {
val maybeGradient = drawable.getDrawable(i)
@@ -255,6 +260,11 @@
}
private fun setXfermode(background: Drawable, mode: PorterDuffXfermode?) {
+ if (background is InsetDrawable) {
+ background.drawable?.let { setXfermode(it, mode) }
+ return
+ }
+
if (background !is LayerDrawable) {
background.setXfermode(mode)
return
@@ -323,6 +333,11 @@
return
}
+ if (drawable is InsetDrawable) {
+ drawable.drawable?.let { applyBackgroundRadii(it, radii) }
+ return
+ }
+
if (drawable !is LayerDrawable) {
return
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 00bea8d..47a373e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -60,6 +60,8 @@
*/
void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags);
void startActivity(Intent intent, boolean dismissShade);
+ void startActivity(Intent intent, boolean dismissShade,
+ @Nullable ActivityLaunchAnimator.Controller animationController);
void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
void startActivity(Intent intent, boolean dismissShade, Callback callback);
void postStartActivityDismissingKeyguard(Intent intent, int delay);
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 00c27bf..1cef44b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -79,7 +79,6 @@
android:id="@+id/left_aligned_notification_icon_container"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_shelf_height"
- android:layout_marginTop="@dimen/widget_vertical_padding"
android:layout_below="@id/keyguard_status_area"
android:paddingStart="@dimen/below_clock_padding_start"
/>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 93fe516..9f00e66 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -106,7 +106,7 @@
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"PIN kodea idatzi beharko duzu gailua berrabiarazten denean"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"Pasahitza idatzi beharko duzu gailua berrabiarazten denean"</string>
<string name="kg_prompt_reason_timeout_pattern" msgid="9170360502528959889">"Eredua behar da gailua babestuago izateko"</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="5945186097160029201">"PIN kodea behar da gailua babestuago izateko"</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="5945186097160029201">"PINa behar da gailua babestuago izateko"</string>
<string name="kg_prompt_reason_timeout_password" msgid="2258263949430384278">"Pasahitza behar da gailua babestuago izateko"</string>
<string name="kg_prompt_reason_switch_profiles_pattern" msgid="1922016914701991230">"Eredua marraztu beharko duzu profilez aldatzen baduzu"</string>
<string name="kg_prompt_reason_switch_profiles_pin" msgid="6490434826361055400">"PIN kodea idatzi beharko duzu profilez aldatzen baduzu"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index 6f8a108..707488d 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -51,7 +51,7 @@
<string name="keyguard_sim_puk_locked_message" msgid="6253830777745450550">"SIM कार्ड PUK-लक भएको छ।"</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="2394023844117630429">"SIM कार्ड अनलक गरिँदै..."</string>
<string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"PIN क्षेत्र"</string>
- <string name="keyguard_accessibility_password" msgid="3524161948484801450">"यन्त्रको पासवर्ड"</string>
+ <string name="keyguard_accessibility_password" msgid="3524161948484801450">"डिभाइसको पासवर्ड"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"SIM को PIN क्षेत्र"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="5537294043180237374">"SIM को PUK क्षेत्र"</string>
<string name="keyguard_accessibility_next_alarm" msgid="4492876946798984630">"अर्को अलार्म <xliff:g id="ALARM">%1$s</xliff:g> का लागि सेट गरियो"</string>
@@ -71,7 +71,7 @@
<string name="kg_pattern_instructions" msgid="5376036737065051736">"आफ्नो ढाँचा कोर्नुहोस्"</string>
<string name="kg_sim_pin_instructions" msgid="1942424305184242951">"SIM को PIN प्रविष्टि गर्नुहोस्।"</string>
<string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" को SIM को PIN प्रविष्टि गर्नुहोस्।"</string>
- <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> मोबाइल सेवा बिना यन्त्रको प्रयोग गर्न eSIM लाई असक्षम पार्नुहोस्।"</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> मोबाइल सेवा बिना डिभाइसको प्रयोग गर्न eSIM लाई असक्षम पार्नुहोस्।"</string>
<string name="kg_pin_instructions" msgid="822353548385014361">"PIN प्रविष्टि गर्नुहोस्"</string>
<string name="kg_password_instructions" msgid="324455062831719903">"पासवर्ड प्रविष्टि गर्नुहोस्"</string>
<string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"SIM कार्ड अहिले असक्षम छ। सुचारु गर्नको लागि PUK कोड प्रविष्टि गर्नुहोस्। विवरणको लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।"</string>
@@ -85,7 +85,7 @@
<string name="kg_login_too_many_attempts" msgid="4519957179182578690">"अत्यन्त धेरै ढाँचा कोर्ने प्रयासहरू"</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"तपाईंले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले आफ्नो PIN प्रविष्ट गर्नुभएको छ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"तपाईंले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक आफ्नो गलत पासवर्ड प्रविष्ट गर्नुभएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"तपाईंले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले आफ्नो अनलक ढाँचा कोर्नुभएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि कोसिस गर्नुहोस्।"</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"तपाईंले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले आफ्नो अनलक प्याटर्न कोर्नुभएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि कोसिस गर्नुहोस्।"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM को PIN कोड गलत छ। तपाईंले अब आफ्नो यन्त्र खोल्न आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नै पर्ने हुन्छ।"</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
<item quantity="other">SIM को PIN कोड गलत छ, तपाईं अझै <xliff:g id="NUMBER_1">%d</xliff:g> पटक प्रयास गर्न सक्नुहुन्छ।</item>
@@ -135,7 +135,7 @@
<item quantity="other">SIM लाई असक्षम पारिएको छ। जारी राख्न PUK कोड प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="_NUMBER_1">%d</xliff:g> प्रयासहरू बाँकी छन्, त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुन्छ। विवरणहरूका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।</item>
<item quantity="one">SIM लाई असक्षम पारिएको छ। जारी राख्न PUK कोड प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="_NUMBER_0">%d</xliff:g> प्रयास बाँकी छ, त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुन्छ। विवरणहरूका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।</item>
</plurals>
- <string name="clock_title_default" msgid="6342735240617459864">"पूर्वनिर्धारित"</string>
+ <string name="clock_title_default" msgid="6342735240617459864">"डिफल्ट"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"बबल"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"एनालग"</string>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 0fef9f1..72b027a 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -17,7 +17,7 @@
*/
-->
-<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+<resources>
<!-- Keyguard PIN pad styles -->
<style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView">
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
@@ -32,7 +32,9 @@
<item name="android:stateListAnimator">@null</item>
</style>
<style name="NumPadKey" parent="Theme.SystemUI">
- <item name="android:colorControlNormal">?androidprv:attr/colorSurface</item>
+ <!-- Studio can't directly reference ?androidprv:attr/colorSurface here, so this value
+ is resolved in {@link NumPadAnimator}. -->
+ <item name="android:colorControlNormal">@null</item>
<item name="android:colorControlHighlight">?android:attr/colorAccent</item>
<item name="android:background">@drawable/num_pad_key_background</item>
</style>
diff --git a/packages/SystemUI/res-product/values-ne/strings.xml b/packages/SystemUI/res-product/values-ne/strings.xml
index 148cb51..1d368f4 100644
--- a/packages/SystemUI/res-product/values-ne/strings.xml
+++ b/packages/SystemUI/res-product/values-ne/strings.xml
@@ -22,7 +22,7 @@
<string name="dock_alignment_slow_charging" product="default" msgid="6997633396534416792">"अझ छिटो चार्ज गर्न फोनलाई फेरि मिलाउनुहोस्"</string>
<string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"तारविनै चार्ज गर्न फोनलाई फेरि मिलाउनुहोस्"</string>
<string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Android टिभी यन्त्र चाँडै निष्क्रिय हुने छ; सक्रिय राख्न कुनै बटन थिच्नुहोस्।"</string>
- <string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"यो यन्त्र चाँडै निष्क्रिय हुने छ; सक्रिय राख्न थिच्नुहोस्।"</string>
+ <string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"यो डिभाइस चाँडै निष्क्रिय हुने छ; सक्रिय राख्न थिच्नुहोस्।"</string>
<string name="keyguard_missing_sim_message" product="tablet" msgid="5018086454277963787">"ट्याब्लेटमा SIM कार्ड छैन।"</string>
<string name="keyguard_missing_sim_message" product="default" msgid="7053347843877341391">"फोनमा SIM कार्ड छैन।"</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"PIN कोडहरू मिलेनन्"</string>
diff --git a/packages/SystemUI/res/drawable/ic_brightness.xml b/packages/SystemUI/res/drawable/ic_brightness.xml
index f443332..842af26 100644
--- a/packages/SystemUI/res/drawable/ic_brightness.xml
+++ b/packages/SystemUI/res/drawable/ic_brightness.xml
@@ -1,29 +1,23 @@
<!--
-Copyright (C) 2020 The Android Open Source Project
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ 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.
+ -->
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
-
- <path
- android:pathData="M18,14.48V18h-3.52L12,20.48L9.52,18H6v-3.52L3.52,12L6,9.52V6h3.52L12,3.52L14.48,6H18v3.52L20.48,12L18,14.48z"
- />
-
- <path
- android:pathData=" M20,8.69 V4h-4.69L12,0.69L8.69,4H4v4.69L0.69,12L4,15.31V20h4.69L12,23.31L15.31,20H20v-4.69L23.31,12L20,8.69z M18,14.48V18h-3.52L12,20.48L9.52,18H6v-3.52L3.52,12L6,9.52V6h3.52L12,3.52L14.48,6H18v3.52L20.48,12L18,14.48z M12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5s5,-2.24 5,-5S14.76,7 12,7z"
- android:fillColor="#FFFFFF" />
-</vector>
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Levels in drawables go from 0 to 10000 -->
+ <!-- "One third" of the range per icon -->
+ <item android:maxLevel="3333" android:drawable="@drawable/ic_brightness_low" />
+ <item android:maxLevel="6666" android:drawable="@drawable/ic_brightness_medium" />
+ <item android:maxLevel="10000" android:drawable="@drawable/ic_brightness_full" />
+</level-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_brightness_thumb.xml b/packages/SystemUI/res/drawable/ic_brightness_full.xml
similarity index 86%
rename from packages/SystemUI/res/drawable/ic_brightness_thumb.xml
rename to packages/SystemUI/res/drawable/ic_brightness_full.xml
index d721988..f443332 100644
--- a/packages/SystemUI/res/drawable/ic_brightness_thumb.xml
+++ b/packages/SystemUI/res/drawable/ic_brightness_full.xml
@@ -1,5 +1,5 @@
<!--
-Copyright (C) 2017 The Android Open Source Project
+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.
@@ -21,9 +21,9 @@
<path
android:pathData="M18,14.48V18h-3.52L12,20.48L9.52,18H6v-3.52L3.52,12L6,9.52V6h3.52L12,3.52L14.48,6H18v3.52L20.48,12L18,14.48z"
- android:fillColor="?android:attr/colorBackgroundFloating" />
+ />
<path
android:pathData=" M20,8.69 V4h-4.69L12,0.69L8.69,4H4v4.69L0.69,12L4,15.31V20h4.69L12,23.31L15.31,20H20v-4.69L23.31,12L20,8.69z M18,14.48V18h-3.52L12,20.48L9.52,18H6v-3.52L3.52,12L6,9.52V6h3.52L12,3.52L14.48,6H18v3.52L20.48,12L18,14.48z M12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5s5,-2.24 5,-5S14.76,7 12,7z"
- android:fillColor="?android:attr/colorControlActivated" />
+ android:fillColor="#FFFFFF" />
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_brightness_low.xml b/packages/SystemUI/res/drawable/ic_brightness_low.xml
new file mode 100644
index 0000000..b463556
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_brightness_low.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM18,14.48L18,18h-3.52L12,20.48 9.52,18L6,18v-3.52L3.52,12 6,9.52L6,6h3.52L12,3.52 14.48,6L18,6v3.52L20.48,12 18,14.48zM12,9c1.65,0 3,1.35 3,3s-1.35,3 -3,3 -3,-1.35 -3,-3 1.35,-3 3,-3m0,-2c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_brightness_medium.xml b/packages/SystemUI/res/drawable/ic_brightness_medium.xml
new file mode 100644
index 0000000..80acc4d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_brightness_medium.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM18,14.48L18,18h-3.52L12,20.48 9.52,18L6,18v-3.52L3.52,12 6,9.52L6,6h3.52L12,3.52 14.48,6L18,6v3.52L20.48,12 18,14.48zM12,17c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5v10z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
index f35f5d1..6725a26 100644
--- a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
@@ -15,10 +15,12 @@
~ limitations under the License.
-->
+
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<solid
- android:color="@color/notif_pill_background"
+ android:color="?androidprv:attr/colorSurface"
/>
<corners android:radius="20dp" />
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml b/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml
index 6022206..77b9871 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml
@@ -23,13 +23,18 @@
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="@android:color/white"/>
+ <corners android:radius="@dimen/screenshot_button_corner_radius"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="?attr/underSurfaceColor"/>
<corners android:radius="@dimen/qs_footer_action_corner_radius"/>
</shape>
</item>
<item>
<shape android:shape="rectangle">
- <stroke android:width="1dp" android:color="@color/qs_footer_action_border"/>
- <solid android:color="@android:color/transparent"/>
+ <stroke android:width="1dp" android:color="@color/qs_footer_action_border"/>
<corners android:radius="@dimen/qs_footer_action_corner_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml b/packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml
similarity index 65%
copy from packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
copy to packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml
index b8ea622..4e9d380 100644
--- a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
+++ b/packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
+<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,10 +13,14 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
+
+<!-- dot drawable for ongoing system privacy events -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
<solid
- android:color="?android:attr/textColorSecondary" />
- <corners android:radius="2dp" />
-</shape>
+ android:color="@color/privacy_circle"/>
+ <size
+ android:width="6dp"
+ android:height="6dp"
+ />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index fb783a7..ceba4e3 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -19,6 +19,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="?android:colorBackgroundFloating"
+ android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -119,4 +120,14 @@
app:layout_constraintRight_toRightOf="parent"
/>
+ <ImageView
+ android:id="@+id/transition"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toBottomOf="@id/save"
+ app:layout_constraintStart_toStartOf="parent"
+ android:scaleType="matrix"
+ android:visibility="gone"
+ />
+
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index c98c3a0..b563633 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -28,7 +28,7 @@
<FrameLayout
android:layout_width="36dp"
android:layout_height="36dp"
- android:layout_gravity="center_vertical"
+ android:layout_gravity="center_vertical|start"
android:layout_marginStart="16dp">
<ImageView
android:id="@+id/title_icon"
@@ -41,7 +41,7 @@
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
+ android:layout_gravity="center_vertical|start"
android:layout_marginStart="68dp"
android:ellipsize="end"
android:maxLines="1"
diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml
index dc9d920..bb82f91 100644
--- a/packages/SystemUI/res/layout/notification_snooze.xml
+++ b/packages/SystemUI/res/layout/notification_snooze.xml
@@ -17,10 +17,11 @@
<com.android.systemui.statusbar.notification.row.NotificationSnooze
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:background="?android:attr/colorBackground"
+ android:background="?androidprv:attr/colorSurface"
android:theme="@style/Theme.SystemUI">
<RelativeLayout
@@ -34,7 +35,7 @@
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
- android:paddingStart="@*android:dimen/notification_content_margin_start"
+ android:paddingStart="@*android:dimen/notification_content_margin_end"
android:textColor="?android:attr/textColorPrimary"
android:paddingEnd="4dp"/>
diff --git a/packages/SystemUI/res/layout/notification_snooze_option.xml b/packages/SystemUI/res/layout/notification_snooze_option.xml
index f203839..d42cc02 100644
--- a/packages/SystemUI/res/layout/notification_snooze_option.xml
+++ b/packages/SystemUI/res/layout/notification_snooze_option.xml
@@ -17,8 +17,8 @@
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="48dp"
- android:layout_marginStart="@*android:dimen/notification_content_margin_start"
+ android:layout_height="@*android:dimen/notification_headerless_min_height"
+ android:layout_marginStart="@*android:dimen/notification_content_margin_end"
android:layout_marginEnd="@*android:dimen/notification_content_margin_end"
android:gravity="center_vertical"
android:textSize="14sp"
diff --git a/packages/SystemUI/res/layout/people_space_initial_layout.xml b/packages/SystemUI/res/layout/people_space_initial_layout.xml
new file mode 100644
index 0000000..ec29d18
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_space_initial_layout.xml
@@ -0,0 +1,69 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:background="@drawable/people_space_tile_view_card"
+ android:id="@+id/item"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:layout_gravity="top"
+ android:paddingVertical="16dp"
+ android:paddingHorizontal="16dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:paddingEnd="20dp"
+ android:gravity="bottom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:background="@drawable/ic_person"
+ android:layout_width="48dp"
+ android:layout_height="48dp" />
+
+ <TextView
+ android:id="@+id/name"
+ android:paddingTop="2dp"
+ android:text="@string/empty_user_name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="12sp"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <TextView
+ android:text="@string/status_before_loading"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="12sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="3"
+ android:ellipsize="end" />
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_placeholder_layout.xml b/packages/SystemUI/res/layout/people_space_placeholder_layout.xml
index b85d6b0..2402bd7 100644
--- a/packages/SystemUI/res/layout/people_space_placeholder_layout.xml
+++ b/packages/SystemUI/res/layout/people_space_placeholder_layout.xml
@@ -14,63 +14,56 @@
~ limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
+
<LinearLayout
android:background="@drawable/people_space_tile_view_card"
android:id="@+id/item"
- android:orientation="vertical"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:layout_gravity="top"
+ android:paddingVertical="16dp"
+ android:paddingHorizontal="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
- android:orientation="horizontal"
- android:gravity="center"
- android:paddingVertical="2dp"
- android:paddingHorizontal="8dp"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:orientation="vertical"
+ android:paddingEnd="20dp"
+ android:gravity="bottom"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
<ImageView
android:background="@drawable/ic_person"
- android:id="@+id/person_icon_only"
- android:layout_width="60dp"
- android:layout_height="60dp"/>
+ android:layout_width="48dp"
+ android:layout_height="48dp" />
- <LinearLayout
- android:orientation="vertical"
- android:paddingStart="8dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <ImageView
- android:id="@+id/availability"
- android:layout_width="10dp"
- android:layout_height="10dp"
- android:background="@drawable/circle_green_10dp"/>
- <TextView
- android:id="@+id/name"
- android:text="@string/empty_user_name"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp"
- android:maxLines="1"
- android:ellipsize="end"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <TextView
- android:id="@+id/last_interaction"
- android:text="@string/empty_status"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textSize="12sp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="3"
- android:ellipsize="end"/>
- </LinearLayout>
+ <TextView
+ android:id="@+id/name"
+ android:paddingTop="2dp"
+ android:text="@string/empty_user_name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="12sp"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
</LinearLayout>
+
+ <TextView
+ android:text="@string/empty_status"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="12sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="3"
+ android:ellipsize="end" />
</LinearLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_medium_empty.xml b/packages/SystemUI/res/layout/people_tile_medium_empty.xml
index 7d9cbb9..4236493 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_empty.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_empty.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -22,57 +22,49 @@
<LinearLayout
android:background="@drawable/people_space_tile_view_card"
android:id="@+id/item"
- android:orientation="vertical"
+ android:gravity="center"
+ android:paddingHorizontal="16dp"
+ android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
-
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_marginTop="-2dp"
+ android:layout_marginStart="-2dp"
+ android:layout_width="64dp"
+ android:layout_height="64dp" />
+ <ImageView
+ android:id="@+id/availability"
+ android:layout_marginStart="-2dp"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="@drawable/circle_green_10dp" />
<LinearLayout
- android:orientation="horizontal"
- android:gravity="center"
- android:layout_gravity="center"
- android:paddingVertical="2dp"
- android:paddingHorizontal="8dp"
+ android:orientation="vertical"
+ android:paddingStart="6dp"
+ android:gravity="top"
android:layout_width="match_parent"
- android:layout_height="match_parent">
- <ImageView
- android:id="@+id/person_icon"
- android:layout_width="64dp"
- android:layout_height="64dp"/>
- <ImageView
- android:id="@+id/availability"
- android:layout_marginStart="-2dp"
- android:layout_width="10dp"
- android:layout_height="10dp"
- android:background="@drawable/circle_green_10dp"/>
- <LinearLayout
- android:orientation="vertical"
- android:paddingStart="6dp"
- android:gravity="top"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:id="@+id/name"
- android:text="@string/empty_user_name"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp"
- android:maxLines="1"
- android:ellipsize="end"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <TextView
- android:id="@+id/last_interaction"
- android:text="@string/empty_status"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textSize="12sp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="3"
- android:ellipsize="end"/>
- </LinearLayout>
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/name"
+ android:text="@string/empty_user_name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/last_interaction"
+ android:text="@string/empty_status"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="12sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="3"
+ android:ellipsize="end" />
</LinearLayout>
</LinearLayout>
-</LinearLayout>
\ No newline at end of file
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
index c9e4945..7070660 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
@@ -97,7 +97,7 @@
android:gravity="bottom"
android:layout_gravity="center_vertical"
android:orientation="horizontal"
- android:paddingTop="4dp"
+ android:paddingTop="2dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToOutline="true">
diff --git a/packages/SystemUI/res/layout/people_tile_small.xml b/packages/SystemUI/res/layout/people_tile_small.xml
index 34aa8e4..7c28fc1 100644
--- a/packages/SystemUI/res/layout/people_tile_small.xml
+++ b/packages/SystemUI/res/layout/people_tile_small.xml
@@ -21,7 +21,7 @@
<LinearLayout
android:id="@+id/item"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@drawable/people_space_tile_view_card"
android:orientation="vertical"
@@ -42,12 +42,12 @@
android:tint="?android:attr/colorAccent"
android:layout_gravity="center"
android:layout_width="18dp"
- android:layout_height="22dp"
- android:layout_weight="1" />
+ android:layout_height="22dp" />
<TextView
android:id="@+id/messages_count"
android:layout_gravity="center"
+ android:gravity="center"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
@@ -59,7 +59,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
- android:layout_weight="1"
/>
<TextView
@@ -67,7 +66,6 @@
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
android:paddingHorizontal="4dp"
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 7cce1ba..6a1be81 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -45,8 +45,6 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- android:paddingLeft="@dimen/qs_tile_layout_margin_side"
- android:paddingRight="@dimen/qs_tile_layout_margin_side"
android:paddingBottom="28dp"
android:clipToPadding="false"
android:scrollIndicators="top"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 0217972..343b398 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-** Copyright 2012, The Android Open Source Project
+** 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.
@@ -16,8 +16,7 @@
-->
<!-- Extends FrameLayout -->
-<com.android.systemui.qs.QSFooterView
- xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.systemui.qs.QSFooterView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/qs_footer"
android:layout_width="match_parent"
android:layout_height="@dimen/qs_footer_height"
@@ -32,77 +31,70 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center_vertical"
- android:gravity="end" >
+ android:orientation="vertical">
- <com.android.keyguard.AlphaOptimizedLinearLayout
- android:id="@+id/qs_footer_actions_edit_container"
- android:layout_width="@integer/qs_footer_actions_width"
- android:layout_height="match_parent"
- android:layout_weight="@integer/qs_footer_actions_weight"
- android:gravity="center_vertical|start" >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/build"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:paddingStart="@dimen/qs_tile_margin_horizontal"
+ android:paddingEnd="4dp"
+ android:layout_weight="1"
+ android:clickable="true"
+ android:ellipsize="marquee"
+ android:focusable="true"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status"
+ android:visibility="gone" />
+
+ <com.android.systemui.qs.PageIndicator
+ android:id="@+id/footer_page_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:visibility="gone" />
+
+ <View
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/qs_footer_actions_container"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:gravity="center_vertical">
+
<com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@android:id/edit"
- android:layout_width="@dimen/qs_footer_action_button_size"
+ android:layout_width="0dp"
android:layout_height="@dimen/qs_footer_action_button_size"
- android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:layout_weight="1"
+ android:background="@drawable/qs_footer_action_chip_background"
android:clickable="true"
android:clipToPadding="false"
android:contentDescription="@string/accessibility_quick_settings_edit"
android:focusable="true"
android:padding="@dimen/qs_footer_icon_padding"
android:src="@*android:drawable/ic_mode_edit"
- android:tint="?android:attr/colorForeground"/>
-
- <TextView
- android:id="@+id/build"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:clickable="true"
- android:gravity="center_vertical"
- android:focusable="true"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="@style/TextAppearance.QS.Status"
- android:layout_marginEnd="4dp"
- android:visibility="gone"/>
- </com.android.keyguard.AlphaOptimizedLinearLayout>
-
- <com.android.systemui.qs.PageIndicator
- android:id="@+id/footer_page_indicator"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center_vertical"
- android:visibility="gone" />
-
- <com.android.keyguard.AlphaOptimizedLinearLayout
- android:id="@+id/qs_footer_actions_container"
- android:layout_width="@integer/qs_footer_actions_width"
- android:layout_height="match_parent"
- android:layout_weight="@integer/qs_footer_actions_weight"
- android:gravity="center_vertical|end" >
-
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@+id/pm_lite"
- android:layout_width="@dimen/qs_footer_action_button_size"
- android:layout_height="@dimen/qs_footer_action_button_size"
- android:background="?android:attr/selectableItemBackgroundBorderless"
- android:clickable="true"
- android:clipToPadding="false"
- android:contentDescription="@string/accessibility_quick_settings_power_menu"
- android:focusable="true"
- android:padding="@dimen/qs_footer_icon_padding"
- android:src="@*android:drawable/ic_lock_power_off"
- android:tint="?android:attr/colorForeground"
- android:visibility="gone"
- />
+ android:tint="?android:attr/colorForeground" />
<com.android.systemui.statusbar.phone.MultiUserSwitch
android:id="@+id/multi_user_switch"
- android:layout_width="@dimen/qs_footer_action_button_size"
+ android:layout_width="0dp"
android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_alignParentEnd="true"
- android:background="@drawable/ripple_drawable"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:layout_weight="1"
+ android:background="@drawable/qs_footer_action_chip_background"
android:focusable="true">
<ImageView
@@ -110,40 +102,58 @@
android:layout_width="@dimen/multi_user_avatar_expanded_size"
android:layout_height="@dimen/multi_user_avatar_expanded_size"
android:layout_gravity="center"
- android:scaleType="centerInside"/>
+ android:scaleType="centerInside" />
</com.android.systemui.statusbar.phone.MultiUserSwitch>
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/settings_button_container"
- android:layout_width="@dimen/qs_footer_action_button_size"
+ android:layout_width="0dp"
android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:layout_weight="1"
android:clipChildren="false"
android:clipToPadding="false">
<com.android.systemui.statusbar.phone.SettingsButton
android:id="@+id/settings_button"
- style="@android:style/Widget.Material.Button.Borderless"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="@dimen/qs_footer_action_button_size"
android:layout_gravity="center"
- android:padding="@dimen/qs_footer_icon_padding"
- android:background="@drawable/ripple_drawable"
android:contentDescription="@string/accessibility_quick_settings_settings"
- android:src="@drawable/ic_settings"
+ android:background="@drawable/qs_footer_action_chip_background_borderless"
+ android:padding="@dimen/qs_footer_icon_padding"
android:scaleType="centerInside"
- android:tint="?android:attr/colorForeground"/>
+ android:src="@drawable/ic_settings"
+ android:tint="?android:attr/colorForeground" />
<com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@+id/tuner_icon"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingStart="36dp"
- android:paddingEnd="4dp"
+ android:layout_width="8dp"
+ android:layout_height="8dp"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginBottom="@dimen/qs_footer_icon_padding"
android:src="@drawable/tuner"
android:tint="?android:attr/textColorTertiary"
- android:visibility="invisible"/>
+ android:visibility="invisible" />
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
- </com.android.keyguard.AlphaOptimizedLinearLayout>
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/pm_lite"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_weight="1"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:clickable="true"
+ android:clipToPadding="false"
+ android:focusable="true"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:src="@*android:drawable/ic_lock_power_off"
+ android:contentDescription="@string/accessibility_quick_settings_power_menu"
+ android:tint="?android:attr/colorForeground" />
+
+ </LinearLayout>
</LinearLayout>
+
</com.android.systemui.qs.QSFooterView>
diff --git a/packages/SystemUI/res/layout/qs_footer_impl_two_lines.xml b/packages/SystemUI/res/layout/qs_footer_impl_two_lines.xml
deleted file mode 100644
index 343b398..0000000
--- a/packages/SystemUI/res/layout/qs_footer_impl_two_lines.xml
+++ /dev/null
@@ -1,159 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-** 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.
--->
-
-<!-- Extends FrameLayout -->
-<com.android.systemui.qs.QSFooterView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/qs_footer"
- android:layout_width="match_parent"
- android:layout_height="@dimen/qs_footer_height"
- android:layout_marginStart="@dimen/qs_footer_margin"
- android:layout_marginEnd="@dimen/qs_footer_margin"
- android:background="@android:color/transparent"
- android:baselineAligned="false"
- android:clickable="false"
- android:clipChildren="false"
- android:clipToPadding="false">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="48dp"
- android:layout_gravity="center_vertical">
-
- <TextView
- android:id="@+id/build"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:paddingStart="@dimen/qs_tile_margin_horizontal"
- android:paddingEnd="4dp"
- android:layout_weight="1"
- android:clickable="true"
- android:ellipsize="marquee"
- android:focusable="true"
- android:gravity="center_vertical"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.QS.Status"
- android:visibility="gone" />
-
- <com.android.systemui.qs.PageIndicator
- android:id="@+id/footer_page_indicator"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center_vertical"
- android:visibility="gone" />
-
- <View
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1" />
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/qs_footer_actions_container"
- android:layout_width="match_parent"
- android:layout_height="48dp"
- android:gravity="center_vertical">
-
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@android:id/edit"
- android:layout_width="0dp"
- android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
- android:layout_weight="1"
- android:background="@drawable/qs_footer_action_chip_background"
- android:clickable="true"
- android:clipToPadding="false"
- android:contentDescription="@string/accessibility_quick_settings_edit"
- android:focusable="true"
- android:padding="@dimen/qs_footer_icon_padding"
- android:src="@*android:drawable/ic_mode_edit"
- android:tint="?android:attr/colorForeground" />
-
- <com.android.systemui.statusbar.phone.MultiUserSwitch
- android:id="@+id/multi_user_switch"
- android:layout_width="0dp"
- android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
- android:layout_weight="1"
- android:background="@drawable/qs_footer_action_chip_background"
- android:focusable="true">
-
- <ImageView
- android:id="@+id/multi_user_avatar"
- android:layout_width="@dimen/multi_user_avatar_expanded_size"
- android:layout_height="@dimen/multi_user_avatar_expanded_size"
- android:layout_gravity="center"
- android:scaleType="centerInside" />
- </com.android.systemui.statusbar.phone.MultiUserSwitch>
-
- <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
- android:id="@+id/settings_button_container"
- android:layout_width="0dp"
- android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
- android:background="@drawable/qs_footer_action_chip_background"
- android:layout_weight="1"
- android:clipChildren="false"
- android:clipToPadding="false">
-
- <com.android.systemui.statusbar.phone.SettingsButton
- android:id="@+id/settings_button"
- android:layout_width="match_parent"
- android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_gravity="center"
- android:contentDescription="@string/accessibility_quick_settings_settings"
- android:background="@drawable/qs_footer_action_chip_background_borderless"
- android:padding="@dimen/qs_footer_icon_padding"
- android:scaleType="centerInside"
- android:src="@drawable/ic_settings"
- android:tint="?android:attr/colorForeground" />
-
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@+id/tuner_icon"
- android:layout_width="8dp"
- android:layout_height="8dp"
- android:layout_gravity="center_horizontal|bottom"
- android:layout_marginBottom="@dimen/qs_footer_icon_padding"
- android:src="@drawable/tuner"
- android:tint="?android:attr/textColorTertiary"
- android:visibility="invisible" />
-
- </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@+id/pm_lite"
- android:layout_width="0dp"
- android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_weight="1"
- android:background="@drawable/qs_footer_action_chip_background"
- android:clickable="true"
- android:clipToPadding="false"
- android:focusable="true"
- android:padding="@dimen/qs_footer_icon_padding"
- android:src="@*android:drawable/ic_lock_power_off"
- android:contentDescription="@string/accessibility_quick_settings_power_menu"
- android:tint="?android:attr/colorForeground" />
-
- </LinearLayout>
- </LinearLayout>
-
-</com.android.systemui.qs.QSFooterView>
diff --git a/packages/SystemUI/res/layout/qs_paged_page.xml b/packages/SystemUI/res/layout/qs_paged_page.xml
index 5c8b2b0..c830773 100644
--- a/packages/SystemUI/res/layout/qs_paged_page.xml
+++ b/packages/SystemUI/res/layout/qs_paged_page.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2015 The Android Open Source Project
+ 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.
@@ -14,8 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<view
- class="com.android.systemui.qs.PagedTileLayout$TilePage"
+<com.android.systemui.qs.SideLabelTileLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tile_page"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml b/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml
deleted file mode 100644
index c830773..0000000
--- a/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.systemui.qs.SideLabelTileLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/tile_page"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false"
- android:clipToPadding="false" />
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index 46a7cf6..c3f1113 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2015 The Android Open Source Project
+ 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.
@@ -22,5 +22,4 @@
android:layout_height="0dp"
android:layout_weight="1"
android:clipChildren="true"
- android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom">
-</com.android.systemui.qs.PagedTileLayout>
+ android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom" />
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout_side_labels.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout_side_labels.xml
deleted file mode 100644
index efa2403..0000000
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout_side_labels.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<com.android.systemui.qs.PagedTileLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:id="@+id/qs_pager"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:clipChildren="true"
- android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom"
- systemui:sideLabels="true" />
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index dc595ee..3d2a621 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -43,10 +43,7 @@
android:background="@android:color/transparent"
android:focusable="true"
android:accessibilityTraversalBefore="@android:id/edit">
- <ViewStub
- android:id="@+id/qs_footer_stub"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
+ <include layout="@layout/qs_footer_impl" />
<include layout="@layout/qs_media_divider"
android:id="@+id/divider"/>
</com.android.systemui.qs.QSPanel>
@@ -59,18 +56,4 @@
<include android:id="@+id/qs_customize" layout="@layout/qs_customize_panel"
android:visibility="gone" />
- <FrameLayout
- android:id="@+id/qs_drag_handle_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:elevation="4dp"
- android:paddingBottom="5dp">
- <View
- android:layout_width="46dp"
- android:layout_height="3dp"
- android:background="@drawable/qs_footer_drag_handle" />
- </FrameLayout>
-
-
</com.android.systemui.qs.QSContainerImpl>
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index ae3adb8..4fcce24 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -31,8 +31,7 @@
android:paddingTop="2dp"
android:paddingStart="16dp"
android:paddingEnd="12dp"
- android:layout_marginRight="20dp"
- android:layout_marginLeft="20dp"
+ android:layout_marginLeft="16dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="16dp"
android:layout_gravity="start|center_vertical"
@@ -52,12 +51,10 @@
android:layout_gravity="center_vertical">
<ImageButton
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:paddingLeft="10dp"
- android:layout_marginBottom="12dp"
- android:layout_marginEnd="12dp"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginBottom="22dp"
android:id="@+id/remote_input_send"
android:src="@drawable/ic_send"
android:contentDescription="@*android:string/ime_action_send"
@@ -69,8 +66,8 @@
android:id="@+id/remote_input_progress"
android:layout_width="24dp"
android:layout_height="24dp"
- android:layout_marginBottom="12dp"
- android:layout_gravity="center_vertical"
+ android:layout_marginBottom="34dp"
+ android:layout_gravity="center_horizontal|bottom"
android:visibility="invisible"
android:indeterminate="true"
style="?android:attr/progressBarStyleSmall" />
diff --git a/packages/SystemUI/res/layout/rounded_corners.xml b/packages/SystemUI/res/layout/rounded_corners.xml
index db892d7..04fe918 100644
--- a/packages/SystemUI/res/layout/rounded_corners.xml
+++ b/packages/SystemUI/res/layout/rounded_corners.xml
@@ -14,6 +14,8 @@
** See the License for the specific language governing permissions and
** limitations under the License.
-->
+
+<!-- TODO: remove this in favor of requiring top and bottom layouts -->
<com.android.systemui.RegionInterceptingFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rounded_corners_default"
@@ -26,6 +28,25 @@
android:layout_gravity="left|top"
android:tint="#ff000000"
android:src="@drawable/rounded"/>
+
+ <FrameLayout
+ android:id="@+id/privacy_dot_left_container"
+ android:layout_height="@dimen/status_bar_height"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="@dimen/status_bar_padding_top"
+ android:layout_marginLeft="8dp"
+ android:layout_gravity="left|top"
+ android:visibility="invisible" >
+ <ImageView
+ android:id="@+id/privacy_dot_left"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center"
+ android:src="@drawable/system_animation_ongoing_dot"
+ android:visibility="visible" />
+ </FrameLayout>
+
+
<ImageView
android:id="@+id/right"
android:layout_width="12dp"
@@ -33,4 +54,22 @@
android:tint="#ff000000"
android:layout_gravity="right|bottom"
android:src="@drawable/rounded"/>
+ <FrameLayout
+ android:id="@+id/privacy_dot_right_container"
+ android:layout_height="@dimen/status_bar_height"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="@dimen/status_bar_padding_top"
+ android:layout_marginRight="8dp"
+ android:layout_gravity="right|top"
+ android:visibility="invisible" >
+ <ImageView
+ android:id="@+id/privacy_dot_right"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center"
+ android:src="@drawable/system_animation_ongoing_dot"
+ android:visibility="visible" />
+
+ </FrameLayout>
+
</com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/layout/rounded_corners_bottom.xml b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
index dde1248..720e47b 100644
--- a/packages/SystemUI/res/layout/rounded_corners_bottom.xml
+++ b/packages/SystemUI/res/layout/rounded_corners_bottom.xml
@@ -26,6 +26,24 @@
android:layout_gravity="left|bottom"
android:tint="#ff000000"
android:src="@drawable/rounded_corner_bottom"/>
+
+ <FrameLayout
+ android:id="@+id/privacy_dot_left_container"
+ android:layout_height="@dimen/status_bar_height"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="@dimen/status_bar_padding_top"
+ android:layout_marginLeft="0dp"
+ android:layout_gravity="left|bottom"
+ android:visibility="invisible" >
+ <ImageView
+ android:id="@+id/privacy_dot"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical|right"
+ android:src="@drawable/system_animation_ongoing_dot"
+ android:visibility="visible" />
+ </FrameLayout>
+
<ImageView
android:id="@+id/right"
android:layout_width="12dp"
@@ -33,4 +51,21 @@
android:tint="#ff000000"
android:layout_gravity="right|bottom"
android:src="@drawable/rounded_corner_bottom"/>
+ <FrameLayout
+ android:id="@+id/privacy_dot_right_container"
+ android:layout_height="@dimen/status_bar_height"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="@dimen/status_bar_padding_top"
+ android:layout_marginRight="0dp"
+ android:layout_gravity="right|bottom"
+ android:visibility="invisible" >
+ <ImageView
+ android:id="@+id/privacy_dot"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical|left"
+ android:src="@drawable/system_animation_ongoing_dot"
+ android:visibility="visible" />
+ </FrameLayout>
+
</com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/layout/rounded_corners_top.xml b/packages/SystemUI/res/layout/rounded_corners_top.xml
index 813c97d..6abe406 100644
--- a/packages/SystemUI/res/layout/rounded_corners_top.xml
+++ b/packages/SystemUI/res/layout/rounded_corners_top.xml
@@ -26,6 +26,24 @@
android:layout_gravity="left|top"
android:tint="#ff000000"
android:src="@drawable/rounded_corner_top"/>
+
+ <FrameLayout
+ android:id="@+id/privacy_dot_left_container"
+ android:layout_height="@*android:dimen/status_bar_height_portrait"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="@dimen/status_bar_padding_top"
+ android:layout_marginLeft="0dp"
+ android:layout_gravity="left|top"
+ android:visibility="invisible" >
+ <ImageView
+ android:id="@+id/privacy_dot"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical|right"
+ android:src="@drawable/system_animation_ongoing_dot"
+ android:visibility="visible" />
+ </FrameLayout>
+
<ImageView
android:id="@+id/right"
android:layout_width="12dp"
@@ -33,4 +51,24 @@
android:tint="#ff000000"
android:layout_gravity="right|top"
android:src="@drawable/rounded_corner_top"/>
+
+ <FrameLayout
+ android:id="@+id/privacy_dot_right_container"
+ android:layout_height="@*android:dimen/status_bar_height_portrait"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="@dimen/status_bar_padding_top"
+ android:layout_marginRight="0dp"
+ android:layout_gravity="right|top"
+ android:visibility="invisible" >
+ <ImageView
+ android:id="@+id/privacy_dot"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical|left"
+ android:src="@drawable/system_animation_ongoing_dot"
+ android:visibility="visible" />
+
+ </FrameLayout>
+
+
</com.android.systemui.RegionInterceptingFrameLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 5c77d16..91220e5 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -46,6 +46,7 @@
android:layout_gravity="end"
android:background="@drawable/notif_footer_btn_background"
android:focusable="true"
+ android:textColor="@color/notif_pill_text"
android:contentDescription="@string/accessibility_clear_all"
android:text="@string/clear_all_notifications_text"
/>
diff --git a/packages/SystemUI/res/layout/system_event_animation_window.xml b/packages/SystemUI/res/layout/system_event_animation_window.xml
new file mode 100644
index 0000000..c92dec9
--- /dev/null
+++ b/packages/SystemUI/res/layout/system_event_animation_window.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical|end"
+ android:paddingTop="@dimen/status_bar_padding_top"
+ android:paddingEnd="8dp"
+ >
+
+ <ImageView
+ android:id="@+id/dot_view"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:layout_gravity="center_vertical|end"
+ android:src="@drawable/system_animation_ongoing_dot"
+ android:visibility="invisible"
+ />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-h740dp-port/dimens.xml b/packages/SystemUI/res/values-h740dp-port/dimens.xml
deleted file mode 100644
index 966066f..0000000
--- a/packages/SystemUI/res/values-h740dp-port/dimens.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<resources>
- <dimen name="qs_tile_height">106dp</dimen>
- <dimen name="qs_tile_margin_vertical">24dp</dimen>
-
- <!-- The height of the qs customize header. Should be
- (qs_panel_padding_top (48dp) + brightness_mirror_height (48dp) + qs_tile_margin_top (18dp)) -
- (Toolbar_minWidth (56dp) + qs_tile_margin_top_bottom (12dp))
- -->
- <dimen name="qs_customize_header_min_height">46dp</dimen>
- <dimen name="qs_tile_margin_top">18dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index 46ec23c..ea456d8 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -20,12 +20,11 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
<resources>
- <!-- The maximum number of tiles in the QuickQSPanel -->
- <integer name="quick_qs_panel_max_columns">6</integer>
-
<!-- The maximum number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">2</integer>
+ <integer name="quick_settings_num_columns">4</integer>
+
<!-- The number of columns that the top level tiles span in the QuickSettings -->
<integer name="quick_settings_user_time_settings_tile_span">2</integer>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 51d7b8e..007f81b 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -23,12 +23,16 @@
<dimen name="docked_divider_handle_height">16dp</dimen>
<dimen name="qs_tile_margin_top">8dp</dimen>
- <dimen name="qs_tile_margin_vertical">0dp</dimen>
+
+ <!-- The height of the qs customize header. Should be
+ (qs_panel_padding_top (48dp) + brightness_mirror_height (48dp) + qs_tile_margin_top (8dp)) -
+ (Toolbar_minWidth (56dp) + qs_tile_margin_top_bottom (4dp))
+ -->
+ <dimen name="qs_customize_header_min_height">44dp</dimen>
<dimen name="battery_detail_graph_space_top">9dp</dimen>
<dimen name="battery_detail_graph_space_bottom">9dp</dimen>
- <integer name="quick_settings_num_columns">4</integer>
<dimen name="qs_detail_margin_top">0dp</dimen>
<dimen name="volume_tool_tip_right_margin">136dp</dimen>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index e6c5bd0..8f88950 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -33,7 +33,6 @@
<!-- The color of the text inside a notification -->
<color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
- <color name="notif_pill_background">@*android:color/surface_dark</color>
<color name="notif_pill_text">@android:color/system_neutral1_50</color>
<color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index cd2395e..fe52dee 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -31,6 +31,7 @@
<!-- Screenshots -->
<style name="LongScreenshotActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
+ <item name="android:windowNoTitle">true</item>
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowLightNavigationBar">false</item>
</style>
diff --git a/packages/SystemUI/res/values-pt-ldrtl/strings.xml b/packages/SystemUI/res/values-pt-ldrtl/strings.xml
index 6fd2bc1..53b2473 100644
--- a/packages/SystemUI/res/values-pt-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-pt-ldrtl/strings.xml
@@ -19,5 +19,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Arraste para a esquerda para alternar rapidamente entre os apps"</string>
+ <string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Arraste para a esquerda para mudar rapidamente de app"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR-ldrtl/strings.xml b/packages/SystemUI/res/values-pt-rBR-ldrtl/strings.xml
index 6fd2bc1..53b2473 100644
--- a/packages/SystemUI/res/values-pt-rBR-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR-ldrtl/strings.xml
@@ -19,5 +19,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Arraste para a esquerda para alternar rapidamente entre os apps"</string>
+ <string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Arraste para a esquerda para mudar rapidamente de app"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index a6321fe..e2b2e25 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -15,8 +15,6 @@
~ limitations under the License
-->
<resources>
- <integer name="quick_settings_num_columns">3</integer>
-
<!-- Max number of columns for quick controls area -->
<integer name="controls_max_columns">2</integer>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
deleted file mode 100644
index 302e5e4..0000000
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (c) 2012, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
--->
-<resources>
- <dimen name="keyguard_clock_notifications_margin">36dp</dimen>
-
- <dimen name="keyguard_indication_margin_bottom">80dp</dimen>
-
- <!-- Screen pinning request width (just a little bit bigger than the three buttons here -->
- <dimen name="screen_pinning_request_width">490dp</dimen>
- <!-- Screen pinning request bottom button circle widths -->
- <dimen name="screen_pinning_request_button_width">162dp</dimen>
- <!-- Screen pinning request, controls padding on bigger screens, bigger nav bar -->
- <dimen name="screen_pinning_request_frame_padding">39dp</dimen>
- <!-- Screen pinning request side views to match nav bar
- In sw600dp we want the buttons centered so this fills the space,
- (screen_pinning_request_width - 3 * screen_pinning_request_button_width) / 2 -->
- <dimen name="screen_pinning_request_side_width">2dp</dimen>
-
- <dimen name="navigation_key_width">162dp</dimen>
- <dimen name="navigation_key_padding">42dp</dimen>
-
- <dimen name="battery_detail_graph_space_top">27dp</dimen>
- <dimen name="battery_detail_graph_space_bottom">27dp</dimen>
-
- <dimen name="qs_tile_margin_top">32dp</dimen>
- <dimen name="qs_brightness_padding_top">6dp</dimen>
- <dimen name="qs_detail_margin_top">28dp</dimen>
-
- <!-- In split shade mode notifications should be aligned to QS header so the value should be
- adjusted to qs header height and height of centered content inside of it:
- (quick_qs_offset_height (48dp) - ongoing_appops_chip_height (24dp) ) / 2 -->
- <dimen name="notifications_top_padding_split_shade">12dp</dimen>
-</resources>
diff --git a/packages/SystemUI/res/values-w550dp-land/config.xml b/packages/SystemUI/res/values-w550dp-land/config.xml
deleted file mode 100644
index a33f131..0000000
--- a/packages/SystemUI/res/values-w550dp-land/config.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2016, 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.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
- for different hardware and product builds. -->
-<resources>
- <integer name="quick_settings_num_columns">6</integer>
-</resources>
diff --git a/packages/SystemUI/res/values-w650dp-land/dimens.xml b/packages/SystemUI/res/values-w650dp-land/dimens.xml
index 108d6cf..97b6da1 100644
--- a/packages/SystemUI/res/values-w650dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-w650dp-land/dimens.xml
@@ -16,6 +16,5 @@
-->
<resources>
<!-- Standard notification width + gravity -->
- <dimen name="notification_panel_width">644dp</dimen>
-
+ <dimen name="notification_panel_width">-1px</dimen> <!-- match_parent -->
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index e4bdbf3..f489fe8 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -176,10 +176,6 @@
<attr name="android:alpha" />
</declare-styleable>
- <declare-styleable name="PagedTileLayout">
- <attr name="sideLabels" format="boolean"/>
- </declare-styleable>
-
<declare-styleable name="CropView">
<attr name="handleThickness" />
<attr name="handleColor" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f699198..bf13c21 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -157,7 +157,6 @@
<color name="minimize_dock_shadow_end">#00000000</color>
<color name="default_remote_input_background">@*android:color/notification_default_color</color>
- <color name="notif_pill_background">@*android:color/surface_light</color>
<color name="notif_pill_text">@android:color/system_neutral1_900</color>
<color name="remote_input_accent">?android:attr/colorAccent</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 2355650..5feb957 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -86,13 +86,13 @@
<bool name="config_navigation_bar_enable_auto_dim_no_visible_wallpaper">true</bool>
<!-- The maximum number of tiles in the QuickQSPanel -->
- <integer name="quick_qs_panel_max_columns">6</integer>
+ <integer name="quick_qs_panel_max_columns">4</integer>
<!-- The number of columns in the QuickSettings -->
- <integer name="quick_settings_num_columns">3</integer>
+ <integer name="quick_settings_num_columns">2</integer>
<!-- The number of rows in the QuickSettings -->
- <integer name="quick_settings_max_rows">3</integer>
+ <integer name="quick_settings_max_rows">4</integer>
<!-- The number of columns that the top level tiles span in the QuickSettings -->
<integer name="quick_settings_user_time_settings_tile_span">1</integer>
@@ -656,4 +656,10 @@
<!-- Y -->
<!-- radius -->
</integer-array>
+
+ <!-- Overrides the behavior of the face unlock keyguard bypass setting:
+ 0 - Don't override the setting (default)
+ 1 - Override the setting to always bypass keyguard
+ 2 - Override the setting to never bypass keyguard -->
+ <integer name="config_face_unlock_bypass_override">0</integer>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ad00882..210efd8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -407,10 +407,10 @@
<!-- The height of the quick settings footer that holds the user switcher, settings icon,
etc. -->
- <dimen name="qs_footer_height">48dp</dimen>
+ <dimen name="qs_footer_height">96dp</dimen>
<!-- The size of each of the icon buttons in the QS footer -->
- <dimen name="qs_footer_action_button_size">@dimen/qs_footer_height</dimen>
+ <dimen name="qs_footer_action_button_size">48dp</dimen>
<dimen name="qs_footer_action_corner_radius">20dp</dimen>
@@ -529,25 +529,25 @@
<dimen name="pull_span_min">25dp</dimen>
<dimen name="qs_corner_radius">14dp</dimen>
- <dimen name="qs_tile_height">96dp</dimen>
+ <dimen name="qs_tile_height">88dp</dimen>
<!--notification_side_paddings + notification_content_margin_start - (qs_quick_tile_size - qs_tile_background_size) / 2 -->
<dimen name="qs_tile_layout_margin_side">18dp</dimen>
- <dimen name="qs_tile_margin_horizontal">18dp</dimen>
+ <dimen name="qs_tile_margin_horizontal">8dp</dimen>
<dimen name="qs_tile_margin_horizontal_two_line">2dp</dimen>
- <dimen name="qs_tile_margin_vertical">2dp</dimen>
- <dimen name="qs_tile_margin_top_bottom">12dp</dimen>
- <dimen name="qs_tile_margin_top_bottom_negative">-12dp</dimen>
+ <dimen name="qs_tile_margin_vertical">@dimen/qs_tile_margin_horizontal</dimen>
+ <dimen name="qs_tile_margin_top_bottom">4dp</dimen>
+ <dimen name="qs_tile_margin_top_bottom_negative">-4dp</dimen>
<!-- The height of the qs customize header. Should be
- (qs_panel_padding_top (48dp) + brightness_mirror_height (48dp) + qs_tile_margin_top (0dp)) -
- (Toolbar_minWidth (56dp) + qs_tile_margin_top_bottom (12dp))
+ (qs_panel_padding_top (48dp) + brightness_mirror_height (48dp) + qs_tile_margin_top (18dp)) -
+ (Toolbar_minWidth (56dp) + qs_tile_margin_top_bottom (4dp))
-->
- <dimen name="qs_customize_header_min_height">28dp</dimen>
- <dimen name="qs_tile_margin_top">0dp</dimen>
+ <dimen name="qs_customize_header_min_height">54dp</dimen>
+ <dimen name="qs_tile_margin_top">18dp</dimen>
<dimen name="qs_tile_icon_background_stroke_width">-1dp</dimen>
- <dimen name="qs_tile_background_size">44dp</dimen>
+ <dimen name="qs_tile_background_size">56dp</dimen>
<dimen name="qs_icon_size">20dp</dimen>
<dimen name="qs_label_container_margin">10dp</dimen>
- <dimen name="qs_quick_tile_size">48dp</dimen>
+ <dimen name="qs_quick_tile_size">60dp</dimen>
<dimen name="qs_quick_tile_padding">12dp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
<dimen name="qs_header_tile_margin_bottom">18dp</dimen>
@@ -557,9 +557,9 @@
Scaled @dimen/qs_page_indicator-width by .4f.
-->
<dimen name="qs_page_indicator_dot_width">6.4dp</dimen>
- <dimen name="qs_tile_side_label_padding">6dp</dimen>
+ <dimen name="qs_tile_side_label_padding">12dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
- <dimen name="qs_tile_text_size">12sp</dimen>
+ <dimen name="qs_tile_text_size">14sp</dimen>
<dimen name="qs_tile_divider_height">1dp</dimen>
<dimen name="qs_panel_padding">16dp</dimen>
<dimen name="qs_dual_tile_height">112dp</dimen>
@@ -570,7 +570,7 @@
<dimen name="qs_tile_padding_bottom">16dp</dimen>
<dimen name="qs_tile_spacing">4dp</dimen>
<dimen name="qs_panel_padding_bottom">0dp</dimen>
- <dimen name="qs_panel_padding_top">@dimen/qs_header_tooltip_height</dimen>
+ <dimen name="qs_panel_padding_top">48dp</dimen>
<dimen name="qs_detail_header_height">56dp</dimen>
<dimen name="qs_detail_header_padding">0dp</dimen>
<dimen name="qs_detail_image_width">56dp</dimen>
@@ -594,7 +594,6 @@
<dimen name="qs_detail_item_icon_width">32dp</dimen>
<dimen name="qs_detail_item_icon_marginStart">0dp</dimen>
<dimen name="qs_detail_item_icon_marginEnd">20dp</dimen>
- <dimen name="qs_header_tooltip_height">48dp</dimen>
<dimen name="qs_header_alarm_icon_size">@dimen/status_bar_icon_drawing_size</dimen>
<dimen name="qs_header_mobile_icon_size">@dimen/status_bar_icon_drawing_size</dimen>
<dimen name="qs_header_alarm_text_margin_start">6dp</dimen>
@@ -872,7 +871,9 @@
<dimen name="keyguard_affordance_width">48dp</dimen>
<dimen name="keyguard_affordance_horizontal_offset">32dp</dimen>
<dimen name="keyguard_affordance_vertical_offset">32dp</dimen>
-
+ <!-- Value should be at least sum of 'keyguard_affordance_width' +
+ 'keyguard_affordance_horizontal_offset' -->
+ <dimen name="keyguard_indication_area_padding">82dp</dimen>
<!-- The width/height of the unlock icon view on keyguard. -->
<dimen name="keyguard_lock_height">42dp</dimen>
@@ -1398,6 +1399,7 @@
<dimen name="max_people_avatar_size_for_large_content">64dp</dimen>
<dimen name="max_people_avatar_size">108dp</dimen>
<dimen name="name_text_size_for_small">14sp</dimen>
+ <dimen name="name_text_size_for_medium">14sp</dimen>
<dimen name="name_text_size_for_large">24sp</dimen>
<dimen name="content_text_size_for_medium">12sp</dimen>
<dimen name="content_text_size_for_large">14sp</dimen>
@@ -1415,17 +1417,17 @@
<dimen name="accessibility_floating_menu_large_single_radius">33dp</dimen>
<dimen name="accessibility_floating_menu_large_multiple_radius">35dp</dimen>
- <dimen name="rounded_slider_height">44dp</dimen>
+ <dimen name="rounded_slider_height">48dp</dimen>
<!-- rounded_slider_height / 2 -->
- <dimen name="rounded_slider_corner_radius">22dp</dimen>
+ <dimen name="rounded_slider_corner_radius">24dp</dimen>
<dimen name="rounded_slider_icon_size">20dp</dimen>
<!-- (rounded_slider_height - rounded_slider_icon_size) / 2 -->
- <dimen name="rounded_slider_icon_inset">12dp</dimen>
+ <dimen name="rounded_slider_icon_inset">14dp</dimen>
<!-- rounded_slider_corner_radius - rounded_slider_track_corner_radius -->
- <dimen name="rounded_slider_track_inset">18dp</dimen>
- <dimen name="rounded_slider_track_width">8dp</dimen>
+ <dimen name="rounded_slider_track_inset">22dp</dimen>
+ <dimen name="rounded_slider_track_width">4dp</dimen>
<!-- rounded_slider_track_width / 2 -->
- <dimen name="rounded_slider_track_corner_radius">4dp</dimen>
+ <dimen name="rounded_slider_track_corner_radius">2dp</dimen>
<!-- inset for ic_lock_open within a DisabledUdfpsView -->
<dimen name="udfps_unlock_icon_inset">16dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index bbf2048..5827f4e 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -28,8 +28,6 @@
<!-- b/171917882 -->
<bool name="flag_notification_twocolumn">false</bool>
- <bool name="flag_qs_labels">false</bool>
-
<!-- AOD/Lockscreen alternate layout -->
<bool name="flag_keyguard_layout">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4ae1c936..e55142b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2012,7 +2012,7 @@
<string name="notification_menu_settings_action">Settings</string>
<!-- Notification: Snooze panel: Snooze undo button label. [CHAR LIMIT=50]-->
- <string name="snooze_undo">UNDO</string>
+ <string name="snooze_undo">Undo</string>
<!-- Notification: Snooze panel: message indicating how long the notification was snoozed for. [CHAR LIMIT=100]-->
<string name="snoozed_for_time">Snoozed for <xliff:g id="time_amount" example="15 minutes">%1$s</xliff:g></string>
@@ -2953,10 +2953,7 @@
[CHAR LIMIT=NONE] -->
<string name="battery_state_unknown_notification_text">Tap for more information</string>
- <!-- No translation [CHAR LIMIT=0] -->
- <string name="qs_remove_labels" translatable="false"></string>
-
- <string name="qs_tile_label_fontFamily" translatable="false">@*android:string/config_headlineFontFamily</string>
+ <string name="qs_tile_label_fontFamily" translatable="false">@*android:string/config_headlineFontFamilyMedium</string>
<!-- Secondary label for alarm tile when there is no next alarm information [CHAR LIMIT=20] -->
<string name="qs_alarm_tile_no_alarm">No alarm set</string>
diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml
index e386147..68e6ca8 100644
--- a/packages/SystemUI/res/xml/people_space_widget_info.xml
+++ b/packages/SystemUI/res/xml/people_space_widget_info.xml
@@ -26,5 +26,5 @@
android:previewLayout="@layout/people_space_placeholder_layout"
android:resizeMode="horizontal|vertical"
android:configure="com.android.systemui.people.PeopleSpaceActivity"
- android:initialLayout="@layout/people_space_placeholder_layout">
+ android:initialLayout="@layout/people_space_initial_layout">
</appwidget-provider>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index f89e365..e7418e6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -23,8 +23,6 @@
import android.app.smartspace.SmartspaceConfig;
import android.app.smartspace.SmartspaceManager;
import android.app.smartspace.SmartspaceSession;
-import android.content.ContentResolver;
-import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.text.TextUtils;
@@ -39,14 +37,13 @@
import com.android.keyguard.clock.ClockManager;
import com.android.settingslib.Utils;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.plugins.ClockPlugin;
-import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -69,14 +66,16 @@
public class KeyguardClockSwitchController extends ViewController<KeyguardClockSwitch> {
private static final boolean CUSTOM_CLOCKS_ENABLED = true;
- private final Resources mResources;
private final StatusBarStateController mStatusBarStateController;
private final SysuiColorExtractor mColorExtractor;
private final ClockManager mClockManager;
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 SystemUIFactory mSystemUIFactory;
/**
* Clock for both small and large sizes
@@ -86,10 +85,6 @@
private AnimatableClockController mLargeClockViewController;
private FrameLayout mLargeClockFrame;
- private PluginManager mPluginManager;
- private boolean mIsSmartspaceEnabled;
- PluginListener mPluginListener;
- private Executor mUiExecutor;
private SmartspaceSession mSmartspaceSession;
private SmartspaceSession.Callback mSmartspaceCallback;
private float mDozeAmount;
@@ -137,31 +132,28 @@
@Inject
public KeyguardClockSwitchController(
KeyguardClockSwitch keyguardClockSwitch,
- @Main Resources resources,
StatusBarStateController statusBarStateController,
SysuiColorExtractor colorExtractor, ClockManager clockManager,
KeyguardSliceViewController keyguardSliceViewController,
NotificationIconAreaController notificationIconAreaController,
- ContentResolver contentResolver,
BroadcastDispatcher broadcastDispatcher,
- PluginManager pluginManager,
FeatureFlags featureFlags,
@Main Executor uiExecutor,
BatteryController batteryController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ SystemUIFactory systemUIFactory) {
super(keyguardClockSwitch);
- mResources = resources;
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
mClockManager = clockManager;
mKeyguardSliceViewController = keyguardSliceViewController;
mNotificationIconAreaController = notificationIconAreaController;
mBroadcastDispatcher = broadcastDispatcher;
- mPluginManager = pluginManager;
- mIsSmartspaceEnabled = featureFlags.isSmartspaceEnabled();
+ mFeatureFlags = featureFlags;
mUiExecutor = uiExecutor;
mBatteryController = batteryController;
mConfigurationController = configurationController;
+ mSystemUIFactory = systemUIFactory;
}
/**
@@ -206,75 +198,45 @@
mStatusBarStateController.addCallback(mStatusBarStateListener);
mConfigurationController.addCallback(mConfigurationListener);
- // If a smartspace plugin is detected, replace the existing smartspace
- // (keyguard_status_area), and initialize a new session
- mPluginListener = new PluginListener<BcSmartspaceDataPlugin>() {
+ BcSmartspaceDataPlugin smartspaceDataPlugin = mSystemUIFactory.getSmartspaceDataProvider();
+ if (mFeatureFlags.isSmartspaceEnabled() && smartspaceDataPlugin != null) {
+ View ksa = mView.findViewById(R.id.keyguard_status_area);
+ int ksaIndex = mView.indexOfChild(ksa);
+ ksa.setVisibility(View.GONE);
- @Override
- public void onPluginConnected(BcSmartspaceDataPlugin plugin, Context pluginContext) {
- if (!mIsSmartspaceEnabled) return;
+ mSmartspaceView = smartspaceDataPlugin.getView(mView);
+ mSmartspaceView.registerDataProvider(smartspaceDataPlugin);
+ updateSmartspaceColor();
+ View asView = (View) mSmartspaceView;
- View ksa = mView.findViewById(R.id.keyguard_status_area);
- int ksaIndex = mView.indexOfChild(ksa);
- ksa.setVisibility(View.GONE);
+ // 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);
- mSmartspaceView = plugin.getView(mView);
- mSmartspaceView.registerDataProvider(plugin);
- updateSmartspaceColor();
- View asView = (View) mSmartspaceView;
+ mView.addView(asView, ksaIndex, lp);
+ int padding = getContext().getResources()
+ .getDimensionPixelSize(R.dimen.below_clock_padding_start);
+ asView.setPadding(padding, 0, padding, 0);
- // Place plugin view below normal clock...
- RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
- MATCH_PARENT, WRAP_CONTENT);
- lp.addRule(RelativeLayout.BELOW, R.id.lockscreen_clock_view);
+ // ... but above the large clock
+ lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
+ lp.addRule(RelativeLayout.BELOW, asView.getId());
+ mLargeClockFrame.setLayoutParams(lp);
- mView.addView(asView, ksaIndex, lp);
- int padding = getContext().getResources()
- .getDimensionPixelSize(R.dimen.below_clock_padding_start);
- asView.setPadding(padding, 0, padding, 0);
+ View nic = mView.findViewById(
+ R.id.left_aligned_notification_icon_container);
+ lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
+ lp.addRule(RelativeLayout.BELOW, asView.getId());
+ nic.setLayoutParams(lp);
- // ... but above the large clock
- lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
- lp.addRule(RelativeLayout.BELOW, asView.getId());
- mLargeClockFrame.setLayoutParams(lp);
-
- View nic = mView.findViewById(
- com.android.systemui.R.id.left_aligned_notification_icon_container);
- lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
- lp.addRule(RelativeLayout.BELOW, asView.getId());
- nic.setLayoutParams(lp);
-
- createSmartspaceSession(plugin);
- }
-
- @Override
- public void onPluginDisconnected(BcSmartspaceDataPlugin plugin) {
- if (!mIsSmartspaceEnabled) return;
-
- mView.removeView((View) mSmartspaceView);
- mView.findViewById(R.id.keyguard_status_area).setVisibility(View.VISIBLE);
-
- View nic = mView.findViewById(
- com.android.systemui.R.id.left_aligned_notification_icon_container);
- RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)
- nic.getLayoutParams();
- lp.addRule(RelativeLayout.BELOW, R.id.keyguard_status_area);
- nic.setLayoutParams(lp);
- mLargeClockFrame.setLayoutParams(lp);
-
- mSmartspaceView = null;
- }
-
- private void createSmartspaceSession(BcSmartspaceDataPlugin plugin) {
- mSmartspaceSession = getContext().getSystemService(SmartspaceManager.class)
- .createSmartspaceSession(
- new SmartspaceConfig.Builder(getContext(), "lockscreen").build());
- mSmartspaceCallback = targets -> plugin.onTargetsAvailable(targets);
- mSmartspaceSession.registerSmartspaceUpdates(mUiExecutor, mSmartspaceCallback);
- mSmartspaceSession.requestSmartspaceUpdate();
- }
- };
- mPluginManager.addPluginListener(mPluginListener, BcSmartspaceDataPlugin.class, false);
+ mSmartspaceSession = getContext().getSystemService(SmartspaceManager.class)
+ .createSmartspaceSession(
+ new SmartspaceConfig.Builder(getContext(), "lockscreen").build());
+ mSmartspaceCallback = targets -> smartspaceDataPlugin.onTargetsAvailable(targets);
+ mSmartspaceSession.registerSmartspaceUpdates(mUiExecutor, mSmartspaceCallback);
+ mSmartspaceSession.requestSmartspaceUpdate();
+ }
}
private void updateWallpaperColor() {
@@ -303,7 +265,6 @@
mSmartspaceSession.destroy();
mSmartspaceSession = null;
}
- mPluginManager.removePluginListener(mPluginListener);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mConfigurationController.removeCallback(mConfigurationListener);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
index 8cd68ef..ab15630 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -18,7 +18,7 @@
val isFaceDisabled: Boolean,
val isBecauseCannotSkipBouncer: Boolean,
val isKeyguardGoingAway: Boolean,
- val isFaceSettingEnabledForUser: Boolean,
+ val isBiometricSettingEnabledForUser: Boolean,
val isLockIconPressed: Boolean,
val isScanningAllowedByStrongAuth: Boolean,
val isPrimaryUser: Boolean,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 67ee1f4..138dd15 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -353,18 +353,15 @@
}
};
- private SparseBooleanArray mFaceSettingEnabledForUser = new SparseBooleanArray();
+ private SparseBooleanArray mBiometricEnabledForUser = new SparseBooleanArray();
private BiometricManager mBiometricManager;
private IBiometricEnabledOnKeyguardCallback mBiometricEnabledCallback =
new IBiometricEnabledOnKeyguardCallback.Stub() {
@Override
- public void onChanged(BiometricSourceType type, boolean enabled, int userId)
- throws RemoteException {
+ public void onChanged(boolean enabled, int userId) throws RemoteException {
mHandler.post(() -> {
- if (type == BiometricSourceType.FACE) {
- mFaceSettingEnabledForUser.put(userId, enabled);
- updateFaceListeningState();
- }
+ mBiometricEnabledForUser.put(userId, enabled);
+ updateBiometricListeningState();
});
}
};
@@ -1119,6 +1116,7 @@
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
final boolean isEncrypted = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
+
return isEncrypted || isLockDown;
}
@@ -1359,7 +1357,7 @@
mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
"com.android.systemui:AOD_INTERRUPT_END");
}
- mAuthController.onCancelAodInterrupt();
+ mAuthController.onCancelUdfps();
mIsUdfpsRunningWhileDozing = false;
}
};
@@ -1631,6 +1629,12 @@
mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker);
}
+ @VisibleForTesting
+ void resetBiometricListeningState() {
+ mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+ mFaceRunningState = BIOMETRIC_STATE_STOPPED;
+ }
+
private void registerRingerTracker() {
mRingerModeTracker.getRingerMode().observeForever(mRingerModeObserver);
}
@@ -1951,7 +1955,7 @@
mIsFaceEnrolled = whitelistIpcs(
() -> mFaceManager != null && mFaceManager.isHardwareDetected()
&& mFaceManager.hasEnrolledTemplates(userId)
- && mFaceSettingEnabledForUser.get(userId));
+ && mBiometricEnabledForUser.get(userId));
}
/**
@@ -2099,7 +2103,7 @@
shouldListenForFingerprintAssistant() || (mKeyguardOccluded && mIsDreaming))
&& !mSwitchingUser && !isFingerprintDisabled(getCurrentUser())
&& (!mKeyguardGoingAway || !mDeviceInteractive) && mIsPrimaryUser
- && allowedOnBouncer;
+ && allowedOnBouncer && mBiometricEnabledForUser.get(getCurrentUser());
return shouldListen;
}
@@ -2158,7 +2162,7 @@
(mBouncer || mAuthInterruptActive || awakeKeyguard
|| shouldListenForFaceAssistant())
&& !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer
- && !mKeyguardGoingAway && mFaceSettingEnabledForUser.get(user) && !mLockIconPressed
+ && !mKeyguardGoingAway && mBiometricEnabledForUser.get(user) && !mLockIconPressed
&& strongAuthAllowsScanning && mIsPrimaryUser
&& !mSecureCameraLaunched;
@@ -2176,7 +2180,7 @@
isFaceDisabled(user),
becauseCannotSkipBouncer,
mKeyguardGoingAway,
- mFaceSettingEnabledForUser.get(user),
+ mBiometricEnabledForUser.get(user),
mLockIconPressed,
strongAuthAllowsScanning,
mIsPrimaryUser,
@@ -3235,6 +3239,7 @@
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
+ pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId));
if (isUdfpsEnrolled()) {
pw.println(" shouldListenForUdfps=" + shouldListenForUdfps());
pw.println(" bouncerVisible=" + mBouncer);
@@ -3257,7 +3262,7 @@
pw.println(" possible=" + isUnlockWithFacePossible(userId));
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
- pw.println(" enabledByUser=" + mFaceSettingEnabledForUser.get(userId));
+ pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId));
pw.println(" mSecureCameraLaunched=" + mSecureCameraLaunched);
}
if (mFaceListenModels != null && !mFaceListenModels.isEmpty()) {
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index 570854e..abdd770 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -29,6 +29,7 @@
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.util.Utils;
/**
* Provides background color and radius animations for key pad buttons.
@@ -100,7 +101,8 @@
ContextThemeWrapper ctw = new ContextThemeWrapper(context, mStyle);
TypedArray a = ctw.obtainStyledAttributes(customAttrs);
- mNormalColor = a.getColor(0, 0);
+ mNormalColor = Utils.getPrivateAttrColorIfUnset(ctw, a, 0, 0,
+ com.android.internal.R.attr.colorSurface);
mHighlightColor = a.getColor(1, 0);
a.recycle();
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 983b1bb..62d5a45 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -91,6 +91,13 @@
}
@Override
+ public void startActivity(Intent intent, boolean dismissShade,
+ @Nullable ActivityLaunchAnimator.Controller animationController) {
+ mActualStarter.ifPresent(
+ starter -> starter.get().startActivity(intent, dismissShade, animationController));
+ }
+
+ @Override
public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) {
mActualStarter.ifPresent(
starter -> starter.get().startActivity(intent, onlyProvisioned, dismissShade));
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index a686fc0..cbfdce5 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -79,6 +79,8 @@
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.NotificationFilter;
@@ -352,6 +354,8 @@
@Inject Lazy<DeviceConfigProxy> mDeviceConfigProxy;
@Inject Lazy<NavigationBarOverlayController> mNavbarButtonsControllerLazy;
@Inject Lazy<TelephonyListenerManager> mTelephonyListenerManager;
+ @Inject Lazy<SystemStatusAnimationScheduler> mSystemStatusAnimationSchedulerLazy;
+ @Inject Lazy<PrivacyDotViewController> mPrivacyDotViewControllerLazy;
@Inject
public Dependency() {
@@ -561,6 +565,10 @@
mProviders.put(NavigationBarOverlayController.class, mNavbarButtonsControllerLazy::get);
+ mProviders.put(SystemStatusAnimationScheduler.class,
+ mSystemStatusAnimationSchedulerLazy::get);
+ mProviders.put(PrivacyDotViewController.class, mPrivacyDotViewControllerLazy::get);
+
Dependency.setInstance(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 5fa98bc..d07723e 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -88,6 +88,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.qs.SecureSetting;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.settings.SecureSettings;
@@ -126,6 +127,7 @@
private DisplayManager.DisplayListener mDisplayListener;
private CameraAvailabilityListener mCameraListener;
private final UserTracker mUserTracker;
+ private final PrivacyDotViewController mDotViewController;
//TODO: These are piecemeal being updated to Points for now to support non-square rounded
// corners. for now it is only supposed when reading the intrinsic size from the drawables with
@@ -140,6 +142,11 @@
protected View[] mOverlays;
@Nullable
private DisplayCutoutView[] mCutoutViews;
+ //TODO:
+ View mTopLeftDot;
+ View mTopRightDot;
+ View mBottomLeftDot;
+ View mBottomRightDot;
private float mDensity;
private WindowManager mWindowManager;
private int mRotation;
@@ -147,6 +154,8 @@
private Handler mHandler;
private boolean mPendingRotationChange;
private boolean mIsRoundedCornerMultipleRadius;
+ private int mStatusBarHeightPortrait;
+ private int mStatusBarHeightLandscape;
private CameraAvailabilityListener.CameraTransitionCallback mCameraTransitionCallback =
new CameraAvailabilityListener.CameraTransitionCallback() {
@@ -205,13 +214,15 @@
SecureSettings secureSettings,
BroadcastDispatcher broadcastDispatcher,
TunerService tunerService,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ PrivacyDotViewController dotViewController) {
super(context);
mMainHandler = handler;
mSecureSettings = secureSettings;
mBroadcastDispatcher = broadcastDispatcher;
mTunerService = tunerService;
mUserTracker = userTracker;
+ mDotViewController = dotViewController;
}
@Override
@@ -222,6 +233,7 @@
}
mHandler = startHandlerThread();
mHandler.post(this::startOnScreenDecorationsThread);
+ mDotViewController.setUiExecutor(mHandler::post);
}
@VisibleForTesting
@@ -286,6 +298,7 @@
private void setupDecorations() {
if (hasRoundedCorners() || shouldDrawCutout()) {
+ updateStatusBarHeight();
final DisplayCutout cutout = getCutout();
final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
int rotatedPos;
@@ -298,6 +311,10 @@
removeOverlay(i);
}
}
+ // Overlays have been created, send the dots to the controller
+ //TODO: need a better way to do this
+ mDotViewController.initialize(
+ mTopLeftDot, mTopRightDot, mBottomLeftDot, mBottomRightDot);
} else {
removeAllOverlays();
}
@@ -431,14 +448,21 @@
private View overlayForPosition(@BoundsPosition int pos) {
switch (pos) {
case BOUNDS_POSITION_TOP:
- return LayoutInflater.from(mContext)
+ case BOUNDS_POSITION_LEFT:
+ View top = LayoutInflater.from(mContext)
.inflate(R.layout.rounded_corners_top, null);
+ mTopLeftDot = top.findViewById(R.id.privacy_dot_left_container);
+ mTopRightDot = top.findViewById(R.id.privacy_dot_right_container);
+ return top;
case BOUNDS_POSITION_BOTTOM:
- return LayoutInflater.from(mContext)
+ case BOUNDS_POSITION_RIGHT:
+ View bottom = LayoutInflater.from(mContext)
.inflate(R.layout.rounded_corners_bottom, null);
+ mBottomLeftDot = bottom.findViewById(R.id.privacy_dot_left_container);
+ mBottomRightDot = bottom.findViewById(R.id.privacy_dot_right_container);
+ return bottom;
default:
- return LayoutInflater.from(mContext)
- .inflate(R.layout.rounded_corners, null);
+ throw new IllegalArgumentException("Unknown bounds position");
}
}
@@ -575,6 +599,11 @@
View child;
for (int j = 0; j < size; j++) {
child = ((ViewGroup) mOverlays[i]).getChildAt(j);
+ if (child.getId() == R.id.privacy_dot_left_container
+ || child.getId() == R.id.privacy_dot_right_container) {
+ // Exclude privacy dot from color inversion (for now?)
+ continue;
+ }
if (child instanceof ImageView) {
((ImageView) child).setImageTintList(tintList);
} else if (child instanceof DisplayCutoutView) {
@@ -611,10 +640,15 @@
Preconditions.checkState(mHandler.getLooper().getThread() == Thread.currentThread(),
"must call on " + mHandler.getLooper().getThread()
+ ", but was " + Thread.currentThread());
+
+ int newRotation = mContext.getDisplay().getRotation();
+ if (mRotation != newRotation) {
+ mDotViewController.updateRotation(newRotation);
+ }
+
if (mPendingRotationChange) {
return;
}
- int newRotation = mContext.getDisplay().getRotation();
if (newRotation != mRotation) {
mRotation = newRotation;
@@ -630,6 +664,14 @@
}
}
+ private void updateStatusBarHeight() {
+ mStatusBarHeightLandscape = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_landscape);
+ mStatusBarHeightPortrait = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_portrait);
+ mDotViewController.setStatusBarHeights(mStatusBarHeightPortrait, mStatusBarHeightLandscape);
+ }
+
private void updateRoundedCornerRadii() {
// We should eventually move to just using the intrinsic size of the drawables since
// they should be sized to the exact pixels they want to cover. Therefore I'm purposely not
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index cc167b9..af064e1 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -29,6 +29,7 @@
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dagger.WMComponent;
import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider;
+import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
import com.android.wm.shell.transition.Transitions;
@@ -209,4 +210,8 @@
AssetManager am, String modelName) {
return new BackGestureTfClassifierProvider();
}
+
+ public BcSmartspaceDataPlugin getSmartspaceDataProvider() {
+ return null;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java
index 2661d89..b544599 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsController.java
@@ -60,17 +60,31 @@
/**
* Returns a copy of the list containing all the active AppOps that the controller tracks.
*
- * @return List of active AppOps information
+ * @return List of active AppOps information, without paused elements.
*/
List<AppOpItem> getActiveAppOps();
/**
+ * Returns a copy of the list containing all the active AppOps that the controller tracks.
+ *
+ * @param showPaused {@code true} to also obtain paused items. {@code false} otherwise.
+ * @return List of active AppOps information
+ */
+ List<AppOpItem> getActiveAppOps(boolean showPaused);
+
+ /**
* Returns a copy of the list containing all the active AppOps that the controller tracks, for
* a given user id.
*
* @param userId User id to track
+ * @param showPaused {@code true} to also obtain paused items. {@code false} otherwise.
*
* @return List of active AppOps information for that user id
*/
- List<AppOpItem> getActiveAppOpsForUser(int userId);
+ List<AppOpItem> getActiveAppOpsForUser(int userId, boolean showPaused);
+
+ /**
+ * @return whether this controller is considering the microphone as muted.
+ */
+ boolean isMicMuted();
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 994401d..534f93e 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -241,9 +241,9 @@
AppOpItem item = getAppOpItemLocked(mActiveItems, code, uid, packageName);
if (item == null && active) {
item = new AppOpItem(code, uid, packageName, mClock.elapsedRealtime());
- if (code == AppOpsManager.OP_RECORD_AUDIO) {
+ if (isOpMicrophone(code)) {
item.setDisabled(isAnyRecordingPausedLocked(uid));
- } else if (code == AppOpsManager.OP_CAMERA) {
+ } else if (isOpCamera(code)) {
item.setDisabled(mCameraDisabled);
}
mActiveItems.add(item);
@@ -298,6 +298,11 @@
return PermissionManager.shouldShowPackageForIndicatorCached(mContext, packageName);
}
+ @WorkerThread
+ public List<AppOpItem> getActiveAppOps() {
+ return getActiveAppOps(false);
+ }
+
/**
* Returns a copy of the list containing all the active AppOps that the controller tracks.
*
@@ -306,8 +311,8 @@
* @return List of active AppOps information
*/
@WorkerThread
- public List<AppOpItem> getActiveAppOps() {
- return getActiveAppOpsForUser(UserHandle.USER_ALL);
+ public List<AppOpItem> getActiveAppOps(boolean showPaused) {
+ return getActiveAppOpsForUser(UserHandle.USER_ALL, showPaused);
}
/**
@@ -321,7 +326,7 @@
* @return List of active AppOps information for that user id
*/
@WorkerThread
- public List<AppOpItem> getActiveAppOpsForUser(int userId) {
+ public List<AppOpItem> getActiveAppOpsForUser(int userId, boolean showPaused) {
Assert.isNotMainThread();
List<AppOpItem> list = new ArrayList<>();
synchronized (mActiveItems) {
@@ -330,7 +335,8 @@
AppOpItem item = mActiveItems.get(i);
if ((userId == UserHandle.USER_ALL
|| UserHandle.getUserId(item.getUid()) == userId)
- && isUserVisible(item.getPackageName()) && !item.isDisabled()) {
+ && isUserVisible(item.getPackageName())
+ && (showPaused || !item.isDisabled())) {
list.add(item);
}
}
@@ -441,9 +447,9 @@
AppOpItem item = mActiveItems.get(i);
boolean paused = false;
- if (item.getCode() == AppOpsManager.OP_RECORD_AUDIO) {
+ if (isOpMicrophone(item.getCode())) {
paused = isAnyRecordingPausedLocked(item.getUid());
- } else if (item.getCode() == AppOpsManager.OP_CAMERA) {
+ } else if (isOpCamera(item.getCode())) {
paused = mCameraDisabled;
}
@@ -502,6 +508,19 @@
});
}
+ @Override
+ public boolean isMicMuted() {
+ return mMicMuted;
+ }
+
+ private boolean isOpCamera(int op) {
+ return op == AppOpsManager.OP_CAMERA || op == AppOpsManager.OP_PHONE_CALL_CAMERA;
+ }
+
+ private boolean isOpMicrophone(int op) {
+ return op == AppOpsManager.OP_RECORD_AUDIO || op == AppOpsManager.OP_PHONE_CALL_MICROPHONE;
+ }
+
protected class H extends Handler {
H(Looper looper) {
super(looper);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index ed8f32f..0c7b55d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -309,18 +309,14 @@
}
/**
- * Cancel a fingerprint scan.
- *
- * The sensor that triggers an AOD interrupt for fingerprint doesn't give
- * ACTION_UP/ACTION_CANCEL events, so the scan needs to be cancelled manually. This should be
- * called when authentication either succeeds or fails. Failing to cancel the scan will leave
- * the screen in high brightness mode.
+ * Cancel a fingerprint scan manually. This will get rid of the white circle on the udfps
+ * sensor area even if the user hasn't explicitly lifted their finger yet.
*/
- public void onCancelAodInterrupt() {
+ public void onCancelUdfps() {
if (mUdfpsController == null) {
return;
}
- mUdfpsController.onCancelAodInterrupt();
+ mUdfpsController.onCancelUdfps();
}
private void sendResultAndCleanUp(@DismissedReason int reason,
@@ -499,6 +495,8 @@
if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage);
mCurrentDialog.onError(errorMessage);
}
+
+ onCancelUdfps();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index aa818bf..9239a8a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -24,6 +24,7 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -35,15 +36,23 @@
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
+import android.media.AudioAttributes;
+import android.os.Handler;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.VelocityTracker;
+import android.view.View;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
@@ -94,7 +103,9 @@
@NonNull private final DumpManager mDumpManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final KeyguardViewMediator mKeyguardViewMediator;
- @NonNull private FalsingManager mFalsingManager;
+ @NonNull private final Vibrator mVibrator;
+ @NonNull private final Handler mMainHandler;
+ @NonNull private final FalsingManager mFalsingManager;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -118,6 +129,27 @@
private boolean mIsAodInterruptActive;
@Nullable private Runnable mCancelAodTimeoutAction;
+ private static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
+ new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ .build();
+
+ private final VibrationEffect mEffectTick = VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+ private final VibrationEffect mEffectTextureTick =
+ VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
+ private final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ private final VibrationEffect mEffectHeavy =
+ VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
+ private final Runnable mAcquiredVibration = new Runnable() {
+ @Override
+ public void run() {
+ String effect = Settings.Global.getString(mContext.getContentResolver(),
+ "udfps_acquired_type");
+ mVibrator.vibrate(getVibration(effect, mEffectTick), VIBRATION_SONIFICATION_ATTRIBUTES);
+ }
+ };
+
/**
* Keeps track of state within a single FingerprintService request. Note that this state
* persists across configuration changes, etc, since it is considered a single request.
@@ -227,7 +259,9 @@
};
@SuppressLint("ClickableViewAccessibility")
- private final UdfpsView.OnTouchListener mOnTouchListener = (view, event) -> {
+ private final UdfpsView.OnTouchListener mOnTouchListener = this::onTouch;
+
+ private boolean onTouch(View view, MotionEvent event) {
UdfpsView udfpsView = (UdfpsView) view;
final boolean isFingerDown = udfpsView.isIlluminationRequested();
boolean handled = false;
@@ -251,6 +285,27 @@
// data for many other pointers because of multi-touch support.
mActivePointerId = event.getPointerId(0);
mVelocityTracker.addMovement(event);
+
+ // TODO: (b/185124905) these settings are for ux testing purposes and should
+ // be removed (or cached) before going into production
+ final ContentResolver contentResolver = mContext.getContentResolver();
+ int startEnabled = Settings.Global.getInt(contentResolver,
+ "udfps_start", 0);
+ if (startEnabled > 0) {
+ String startEffectSetting = Settings.Global.getString(contentResolver,
+ "udfps_start_type");
+ mVibrator.vibrate(getVibration(startEffectSetting, mEffectClick),
+ VIBRATION_SONIFICATION_ATTRIBUTES);
+ }
+
+ int acquiredEnabled = Settings.Global.getInt(contentResolver,
+ "udfps_acquired", 0);
+ if (acquiredEnabled > 0) {
+ int delay = Settings.Global.getInt(contentResolver,
+ "udfps_acquired_delay", 500);
+ mMainHandler.removeCallbacks(mAcquiredVibration);
+ mMainHandler.postDelayed(mAcquiredVibration, delay);
+ }
handled = true;
}
break;
@@ -307,7 +362,7 @@
// Do nothing.
}
return handled;
- };
+ }
@Inject
public UdfpsController(@NonNull Context context,
@@ -324,6 +379,9 @@
@NonNull KeyguardViewMediator keyguardViewMediator,
@NonNull FalsingManager falsingManager) {
mContext = context;
+ // TODO (b/185124905): inject main handler and vibrator once done prototyping
+ mMainHandler = new Handler(Looper.getMainLooper());
+ mVibrator = context.getSystemService(Vibrator.class);
mInflater = inflater;
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
// fingerprint manager should never be null.
@@ -559,19 +617,25 @@
// Since the sensor that triggers the AOD interrupt doesn't provide ACTION_UP/ACTION_CANCEL,
// we need to be careful about not letting the screen accidentally remain in high brightness
// mode. As a mitigation, queue a call to cancel the fingerprint scan.
- mCancelAodTimeoutAction = mFgExecutor.executeDelayed(this::onCancelAodInterrupt,
+ mCancelAodTimeoutAction = mFgExecutor.executeDelayed(this::onCancelUdfps,
AOD_INTERRUPT_TIMEOUT_MILLIS);
// using a hard-coded value for major and minor until it is available from the sensor
onFingerDown(screenX, screenY, minor, major);
}
/**
- * Cancel fingerprint scan.
+ * Cancel updfs scan affordances - ability to hide the HbmSurfaceView (white circle) before
+ * user explicitly lifts their finger. Generally, this should be called whenever udfps fails
+ * or errors.
*
- * This is intended to be called after the fingerprint scan triggered by the AOD interrupt
- * either succeeds or fails.
+ * The sensor that triggers an AOD fingerprint interrupt (see onAodInterrupt) doesn't give
+ * ACTION_UP/ACTION_CANCEL events, so and AOD interrupt scan needs to be cancelled manually.
+ * This should be called when authentication either succeeds or fails. Failing to cancel the
+ * scan will leave the screen in high brightness mode and will show the HbmSurfaceView until
+ * the user lifts their finger.
*/
- void onCancelAodInterrupt() {
+ void onCancelUdfps() {
+ onFingerUp();
if (!mIsAodInterruptActive) {
return;
}
@@ -580,7 +644,6 @@
mCancelAodTimeoutAction = null;
}
mIsAodInterruptActive = false;
- onFingerUp();
}
// This method can be called from the UI thread.
@@ -598,6 +661,7 @@
// This method can be called from the UI thread.
private void onFingerUp() {
+ mMainHandler.removeCallbacks(mAcquiredVibration);
if (mView == null) {
Log.w(TAG, "Null view in onFingerUp");
return;
@@ -617,4 +681,23 @@
// Do nothing. This method can be implemented for devices that require the high-brightness
// mode for fingerprint illumination.
}
+
+ private VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) {
+ if (TextUtils.isEmpty(effect)) {
+ return defaultEffect;
+ }
+
+ switch (effect.toLowerCase()) {
+ case "click":
+ return mEffectClick;
+ case "heavy":
+ return mEffectHeavy;
+ case "texture_tick":
+ return mEffectTextureTick;
+ case "tick":
+ return mEffectTick;
+ default:
+ return defaultEffect;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index 6a012eb..6f70672 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -42,6 +42,7 @@
public static final int QS_COLLAPSE = 12;
public static final int UDFPS_AUTHENTICATION = 13;
public static final int DISABLED_UDFPS_AFFORDANCE = 14;
+ public static final int QS_SWIPE = 15;
@IntDef({
QUICK_SETTINGS,
@@ -59,7 +60,8 @@
QS_COLLAPSE,
BRIGHTNESS_SLIDER,
UDFPS_AUTHENTICATION,
- DISABLED_UDFPS_AFFORDANCE
+ DISABLED_UDFPS_AFFORDANCE,
+ QS_SWIPE
})
@Retention(RetentionPolicy.SOURCE)
public @interface InteractionType {}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index a4e1637..2298010 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -155,7 +155,8 @@
|| interactionType == SHADE_DRAG
|| interactionType == QS_COLLAPSE
|| interactionType == Classifier.UDFPS_AUTHENTICATION
- || interactionType == Classifier.DISABLED_UDFPS_AFFORDANCE) {
+ || interactionType == Classifier.DISABLED_UDFPS_AFFORDANCE
+ || interactionType == Classifier.QS_SWIPE) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
index 3bc24c7..72d4303 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -19,6 +19,7 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import android.provider.DeviceConfig;
@@ -118,7 +119,7 @@
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER
- || interactionType == QS_COLLAPSE) {
+ || interactionType == QS_COLLAPSE || interactionType == QS_SWIPE) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index 1042516..c2ad7e6 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -24,6 +24,7 @@
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
import static com.android.systemui.classifier.Classifier.PULSE_EXPAND;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
@@ -85,6 +86,9 @@
case QS_COLLAPSE:
wrongDirection = !vertical || !up;
break;
+ case QS_SWIPE:
+ wrongDirection = vertical;
+ break;
default:
wrongDirection = true;
break;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 365a102..126724c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -26,6 +26,7 @@
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.BootCompleteCache;
import com.android.systemui.BootCompleteCacheImpl;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.classifier.FalsingModule;
@@ -159,6 +160,11 @@
@Binds
abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
+ @Provides
+ static SystemUIFactory getSystemUIFactory() {
+ return SystemUIFactory.getInstance();
+ }
+
// TODO: This should provided by the WM component
/** Provides Optional of BubbleManager */
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index de00d50..2e03d9a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -17,10 +17,20 @@
package com.android.systemui.keyguard;
import android.annotation.IntDef;
+import android.app.IWallpaperManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.os.Bundle;
import android.os.PowerManager;
+import android.os.RemoteException;
import android.os.Trace;
+import android.util.DisplayMetrics;
+
+import androidx.annotation.Nullable;
import com.android.systemui.Dumpable;
+import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import java.io.FileDescriptor;
@@ -31,7 +41,7 @@
import javax.inject.Inject;
/**
- * Tracks the wakefulness lifecycle.
+ * Tracks the wakefulness lifecycle, including why we're waking or sleeping.
*/
@SysUISingleton
public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observer> implements
@@ -51,13 +61,32 @@
public static final int WAKEFULNESS_AWAKE = 2;
public static final int WAKEFULNESS_GOING_TO_SLEEP = 3;
+ private final Context mContext;
+ private final DisplayMetrics mDisplayMetrics;
+
+ @Nullable
+ private final IWallpaperManager mWallpaperManagerService;
+
private int mWakefulness = WAKEFULNESS_ASLEEP;
+
private @PowerManager.WakeReason int mLastWakeReason = PowerManager.WAKE_REASON_UNKNOWN;
+
+ @Nullable
+ private Point mLastWakeOriginLocation = null;
+
private @PowerManager.GoToSleepReason int mLastSleepReason =
PowerManager.GO_TO_SLEEP_REASON_MIN;
+ @Nullable
+ private Point mLastSleepOriginLocation = null;
+
@Inject
- public WakefulnessLifecycle() {
+ public WakefulnessLifecycle(
+ Context context,
+ @Nullable IWallpaperManager wallpaperManagerService) {
+ mContext = context;
+ mDisplayMetrics = context.getResources().getDisplayMetrics();
+ mWallpaperManagerService = wallpaperManagerService;
}
public @Wakefulness int getWakefulness() {
@@ -85,6 +114,17 @@
}
setWakefulness(WAKEFULNESS_WAKING);
mLastWakeReason = pmWakeReason;
+ updateLastWakeOriginLocation();
+
+ if (mWallpaperManagerService != null) {
+ try {
+ mWallpaperManagerService.notifyWakingUp(
+ mLastWakeOriginLocation.x, mLastWakeOriginLocation.y, new Bundle());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
dispatch(Observer::onStartedWakingUp);
}
@@ -102,6 +142,17 @@
}
setWakefulness(WAKEFULNESS_GOING_TO_SLEEP);
mLastSleepReason = pmSleepReason;
+ updateLastSleepOriginLocation();
+
+ if (mWallpaperManagerService != null) {
+ try {
+ mWallpaperManagerService.notifyGoingToSleep(
+ mLastSleepOriginLocation.x, mLastSleepOriginLocation.y, new Bundle());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
dispatch(Observer::onStartedGoingToSleep);
}
@@ -124,6 +175,60 @@
Trace.traceCounter(Trace.TRACE_TAG_APP, "wakefulness", wakefulness);
}
+ private void updateLastWakeOriginLocation() {
+ mLastWakeOriginLocation = null;
+
+ switch (mLastWakeReason) {
+ case PowerManager.WAKE_REASON_POWER_BUTTON:
+ mLastWakeOriginLocation = getPowerButtonOrigin();
+ break;
+ default:
+ mLastWakeOriginLocation = getDefaultWakeSleepOrigin();
+ break;
+ }
+ }
+
+ private void updateLastSleepOriginLocation() {
+ mLastSleepOriginLocation = null;
+
+ switch (mLastSleepReason) {
+ case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:
+ mLastSleepOriginLocation = getPowerButtonOrigin();
+ break;
+ default:
+ mLastSleepOriginLocation = getDefaultWakeSleepOrigin();
+ break;
+ }
+ }
+
+ /**
+ * Returns the point on the screen closest to the physical power button.
+ */
+ private Point getPowerButtonOrigin() {
+ final boolean isPortrait = mContext.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_PORTRAIT;
+
+ if (isPortrait) {
+ return new Point(
+ mDisplayMetrics.widthPixels,
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.physical_power_button_center_screen_location_y));
+ } else {
+ return new Point(
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.physical_power_button_center_screen_location_y),
+ mDisplayMetrics.heightPixels);
+ }
+ }
+
+ /**
+ * Returns the point on the screen used as the default origin for wake/sleep events. This is the
+ * middle-bottom of the screen.
+ */
+ private Point getDefaultWakeSleepOrigin() {
+ return new Point(mDisplayMetrics.widthPixels / 2, mDisplayMetrics.heightPixels);
+ }
+
public interface Observer {
default void onStartedWakingUp() {}
default void onFinishedWakingUp() {}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 6d1109e..3544f60 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -17,14 +17,18 @@
package com.android.systemui.navigationbar;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
@@ -80,6 +84,8 @@
ConfigurationController.ConfigurationListener,
NavigationModeController.ModeChangedListener, Dumpable {
+ private static final float TABLET_MIN_DPS = 600;
+
private static final String TAG = NavigationBarController.class.getSimpleName();
private final Context mContext;
@@ -107,6 +113,8 @@
private final Handler mHandler;
private final DisplayManager mDisplayManager;
private final NavigationBarOverlayController mNavBarOverlayController;
+ private int mNavMode;
+ private boolean mIsTablet;
/** A displayId - nav bar maps. */
@VisibleForTesting
@@ -172,11 +180,30 @@
configurationController.addCallback(this);
mConfigChanges.applyNewConfig(mContext.getResources());
mNavBarOverlayController = navBarOverlayController;
+ mNavMode = mNavigationModeController.addListener(this);
mNavigationModeController.addListener(this);
}
@Override
public void onConfigChanged(Configuration newConfig) {
+ boolean isOldConfigTablet = mIsTablet;
+ mIsTablet = isTablet(newConfig);
+ boolean largeScreenChanged = mIsTablet != isOldConfigTablet;
+ // If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
+ if (isThreeButtonTaskbarFlagEnabled() &&
+ largeScreenChanged && mNavMode == NAV_BAR_MODE_3BUTTON) {
+ if (!mIsTablet) {
+ // Folded state, show 3 button nav bar
+ createNavigationBar(mContext.getDisplay(), null, null);
+ } else {
+ // Unfolded state, hide 3 button nav bars
+ for (int i = 0; i < mNavigationBars.size(); i++) {
+ removeNavigationBar(mNavigationBars.keyAt(i));
+ }
+ }
+ return;
+ }
+
if (mConfigChanges.applyNewConfig(mContext.getResources())) {
for (int i = 0; i < mNavigationBars.size(); i++) {
recreateNavigationBar(mNavigationBars.keyAt(i));
@@ -190,7 +217,19 @@
@Override
public void onNavigationModeChanged(int mode) {
+ final int oldMode = mNavMode;
+ mNavMode = mode;
mHandler.post(() -> {
+ // create/destroy nav bar based on nav mode only in unfolded state
+ if (isThreeButtonTaskbarFlagEnabled() && oldMode != mNavMode && mIsTablet) {
+ if (oldMode == NAV_BAR_MODE_3BUTTON &&
+ mNavigationBars.get(mContext.getDisplayId()) == null) {
+ // We remove navbar for 3 button unfolded, add it back in
+ createNavigationBar(mContext.getDisplay(), null, null);
+ } else if (mNavMode == NAV_BAR_MODE_3BUTTON) {
+ removeNavigationBar(mContext.getDisplayId());
+ }
+ }
for (int i = 0; i < mNavigationBars.size(); i++) {
NavigationBar navBar = mNavigationBars.valueAt(i);
if (navBar == null) {
@@ -212,6 +251,7 @@
@Override
public void onDisplayReady(int displayId) {
Display display = mDisplayManager.getDisplay(displayId);
+ mIsTablet = isTablet(mContext.getResources().getConfiguration());
createNavigationBar(display, null /* savedState */, null /* result */);
}
@@ -267,6 +307,10 @@
return;
}
+ if (isThreeButtonTaskbarEnabled()) {
+ return;
+ }
+
final int displayId = display.getDisplayId();
final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY;
final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
@@ -398,6 +442,24 @@
return mNavigationBars.get(DEFAULT_DISPLAY);
}
+ private boolean isThreeButtonTaskbarEnabled() {
+ return mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON &&
+ isThreeButtonTaskbarFlagEnabled();
+ }
+
+ private boolean isThreeButtonTaskbarFlagEnabled() {
+ return SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
+ }
+
+ private boolean isTablet(Configuration newConfig) {
+ float density = Resources.getSystem().getDisplayMetrics().density;
+ int size = Math.min((int) (density * newConfig.screenWidthDp),
+ (int) (density* newConfig.screenHeightDp));
+ DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
+ float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+ return (size / densityRatio) >= TABLET_MIN_DPS;
+ }
+
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
for (int i = 0; i < mNavigationBars.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
index 59329d1..41957bc 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
@@ -93,7 +93,7 @@
: Dependency.get(NotificationEntryManager.class);
RemoteViews view = PeopleSpaceUtils.getPreview(getContext(), mPeopleManager, mLauncherApps,
- mNotificationEntryManager, shortcutId, userHandle, packageName);
+ mNotificationEntryManager, shortcutId, userHandle, packageName, extras);
if (view == null) {
if (DEBUG) Log.d(TAG, "No preview available for shortcutId: " + shortcutId);
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index a0b5521..38a6186 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -23,18 +23,14 @@
import static com.android.systemui.people.PeopleTileViewHelper.getSizeInDp;
import android.app.Activity;
-import android.app.INotificationManager;
-import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.LauncherApps;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
-import android.os.ServiceManager;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -43,7 +39,6 @@
import com.android.systemui.R;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import java.util.ArrayList;
import java.util.List;
@@ -56,19 +51,13 @@
private static final String TAG = "PeopleSpaceActivity";
private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
- private IPeopleManager mPeopleManager;
private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
- private INotificationManager mNotificationManager;
- private LauncherApps mLauncherApps;
private Context mContext;
- private NotificationEntryManager mNotificationEntryManager;
private int mAppWidgetId;
@Inject
- public PeopleSpaceActivity(NotificationEntryManager notificationEntryManager,
- PeopleSpaceWidgetManager peopleSpaceWidgetManager) {
+ public PeopleSpaceActivity(PeopleSpaceWidgetManager peopleSpaceWidgetManager) {
super();
- mNotificationEntryManager = notificationEntryManager;
mPeopleSpaceWidgetManager = peopleSpaceWidgetManager;
}
@@ -77,11 +66,6 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getApplicationContext();
- mNotificationManager = INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE));
- mPeopleManager = IPeopleManager.Stub.asInterface(
- ServiceManager.getService(Context.PEOPLE_SERVICE));
- mLauncherApps = mContext.getSystemService(LauncherApps.class);
mAppWidgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID,
INVALID_APPWIDGET_ID);
setResult(RESULT_CANCELED);
@@ -92,10 +76,8 @@
List<PeopleSpaceTile> priorityTiles = new ArrayList<>();
List<PeopleSpaceTile> recentTiles = new ArrayList<>();
try {
- priorityTiles = PeopleSpaceUtils.getPriorityTiles(mContext, mNotificationManager,
- mPeopleManager, mLauncherApps, mNotificationEntryManager);
- recentTiles = PeopleSpaceUtils.getRecentTiles(mContext, mNotificationManager,
- mPeopleManager, mLauncherApps, mNotificationEntryManager);
+ priorityTiles = mPeopleSpaceWidgetManager.getPriorityTiles();
+ recentTiles = mPeopleSpaceWidgetManager.getRecentTiles();
} catch (Exception e) {
Log.e(TAG, "Couldn't retrieve conversations", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 0fbe1f7..c0a16e1 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -19,8 +19,6 @@
import static android.app.Notification.CATEGORY_MISSED_CALL;
import static android.app.Notification.EXTRA_MESSAGES;
-import android.annotation.NonNull;
-import android.app.INotificationManager;
import android.app.Notification;
import android.app.people.ConversationChannel;
import android.app.people.IPeopleManager;
@@ -36,16 +34,13 @@
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.icu.text.MeasureFormat;
-import android.icu.util.Measure;
-import android.icu.util.MeasureUnit;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.ContactsContract;
-import android.service.notification.ConversationChannelWrapper;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
@@ -65,7 +60,6 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.text.SimpleDateFormat;
-import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -73,7 +67,6 @@
import java.util.Date;
import java.util.HashSet;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -85,9 +78,6 @@
/** Turns on debugging information about People Space. */
public static final boolean DEBUG = true;
private static final String TAG = "PeopleSpaceUtils";
- private static final int DAYS_IN_A_WEEK = 7;
- private static final int MIN_HOUR = 1;
- private static final int ONE_DAY = 1;
public static final String PACKAGE_NAME = "package_name";
public static final String USER_ID = "user_id";
public static final String SHORTCUT_ID = "shortcut_id";
@@ -127,58 +117,6 @@
}
}
- /** Returns a list of map entries corresponding to user's priority conversations. */
- @NonNull
- public static List<PeopleSpaceTile> getPriorityTiles(
- Context context, INotificationManager notificationManager, IPeopleManager peopleManager,
- LauncherApps launcherApps, NotificationEntryManager notificationEntryManager)
- throws Exception {
- List<ConversationChannelWrapper> conversations =
- notificationManager.getConversations(
- false).getList();
- // Add priority conversations to tiles list.
- Stream<ShortcutInfo> priorityConversations = conversations.stream()
- .filter(c -> c.getNotificationChannel() != null
- && c.getNotificationChannel().isImportantConversation())
- .map(c -> c.getShortcutInfo());
- List<PeopleSpaceTile> priorityTiles = getSortedTiles(peopleManager, launcherApps,
- priorityConversations);
- priorityTiles = augmentTilesFromVisibleNotifications(
- context, priorityTiles, notificationEntryManager);
- return priorityTiles;
- }
-
- /** Returns a list of map entries corresponding to user's recent conversations. */
- @NonNull
- public static List<PeopleSpaceTile> getRecentTiles(
- Context context, INotificationManager notificationManager, IPeopleManager peopleManager,
- LauncherApps launcherApps, NotificationEntryManager notificationEntryManager)
- throws Exception {
- if (DEBUG) Log.d(TAG, "Add recent conversations");
- List<ConversationChannelWrapper> conversations =
- notificationManager.getConversations(
- false).getList();
- Stream<ShortcutInfo> nonPriorityConversations = conversations.stream()
- .filter(c -> c.getNotificationChannel() == null
- || !c.getNotificationChannel().isImportantConversation())
- .map(c -> c.getShortcutInfo());
-
- List<ConversationChannel> recentConversationsList =
- peopleManager.getRecentConversations().getList();
- Stream<ShortcutInfo> recentConversations = recentConversationsList
- .stream()
- .map(c -> c.getShortcutInfo());
-
- Stream<ShortcutInfo> mergedStream = Stream.concat(nonPriorityConversations,
- recentConversations);
- List<PeopleSpaceTile> recentTiles =
- getSortedTiles(peopleManager, launcherApps, mergedStream);
-
- recentTiles = augmentTilesFromVisibleNotifications(
- context, recentTiles, notificationEntryManager);
- return recentTiles;
- }
-
/** Returns stored widgets for the conversation specified. */
public static Set<String> getStoredWidgetIds(SharedPreferences sp, PeopleTileKey key) {
if (!key.isValid()) {
@@ -255,7 +193,8 @@
return augmentedTile.get(0);
}
- static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context,
+ /** Adds to {@code tiles} any visible notifications. */
+ public static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context,
List<PeopleSpaceTile> tiles, NotificationEntryManager notificationEntryManager) {
if (notificationEntryManager == null) {
Log.w(TAG, "NotificationEntryManager is null");
@@ -356,11 +295,12 @@
}
/** Returns a list sorted by ascending last interaction time from {@code stream}. */
- private static List<PeopleSpaceTile> getSortedTiles(IPeopleManager peopleManager,
- LauncherApps launcherApps,
+ public static List<PeopleSpaceTile> getSortedTiles(IPeopleManager peopleManager,
+ LauncherApps launcherApps, UserManager userManager,
Stream<ShortcutInfo> stream) {
return stream
.filter(Objects::nonNull)
+ .filter(c -> !userManager.isQuietModeEnabled(c.getUserHandle()))
.map(c -> new PeopleSpaceTile.Builder(c, launcherApps).build())
.filter(c -> shouldKeepConversation(c))
.map(c -> c.toBuilder().setLastInteractionTimestamp(
@@ -426,36 +366,6 @@
return bitmap;
}
- /** Returns a readable status describing the {@code lastInteraction}. */
- public static String getLastInteractionString(Context context, long lastInteraction) {
- if (lastInteraction == 0L) {
- Log.e(TAG, "Could not get valid last interaction");
- return context.getString(R.string.basic_status);
- }
- long now = System.currentTimeMillis();
- Duration durationSinceLastInteraction = Duration.ofMillis(now - lastInteraction);
- MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(),
- MeasureFormat.FormatWidth.WIDE);
- if (durationSinceLastInteraction.toHours() < MIN_HOUR) {
- return context.getString(R.string.timestamp, formatter.formatMeasures(
- new Measure(durationSinceLastInteraction.toMinutes(), MeasureUnit.MINUTE)));
- } else if (durationSinceLastInteraction.toDays() < ONE_DAY) {
- return context.getString(R.string.timestamp, formatter.formatMeasures(
- new Measure(durationSinceLastInteraction.toHours(),
- MeasureUnit.HOUR)));
- } else if (durationSinceLastInteraction.toDays() < DAYS_IN_A_WEEK) {
- return context.getString(R.string.timestamp, formatter.formatMeasures(
- new Measure(durationSinceLastInteraction.toHours(),
- MeasureUnit.DAY)));
- } else {
- return context.getString(durationSinceLastInteraction.toDays() == DAYS_IN_A_WEEK
- ? R.string.timestamp : R.string.over_timestamp,
- formatter.formatMeasures(
- new Measure(durationSinceLastInteraction.toDays() / DAYS_IN_A_WEEK,
- MeasureUnit.WEEK)));
- }
- }
-
/**
* Returns whether the {@code conversation} should be kept for display in the People Space.
*
@@ -619,7 +529,7 @@
*/
public static RemoteViews getPreview(Context context, IPeopleManager peopleManager,
LauncherApps launcherApps, NotificationEntryManager notificationEntryManager,
- String shortcutId, UserHandle userHandle, String packageName) {
+ String shortcutId, UserHandle userHandle, String packageName, Bundle options) {
peopleManager = (peopleManager != null) ? peopleManager : IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE));
launcherApps = (launcherApps != null) ? launcherApps
@@ -646,8 +556,7 @@
context, tile, notificationEntryManager);
if (DEBUG) Log.i(TAG, "Returning tile preview for shortcutId: " + shortcutId);
- Bundle bundle = new Bundle();
- return new PeopleTileViewHelper(context, augmentedTile, 0, bundle).getViews();
+ return new PeopleTileViewHelper(context, augmentedTile, 0, options).getViews();
}
/** Returns the userId associated with a {@link PeopleSpaceTile} */
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 51af47d..6b917c5 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -33,7 +33,6 @@
import static com.android.systemui.people.PeopleSpaceUtils.convertDrawableToBitmap;
import static com.android.systemui.people.PeopleSpaceUtils.getUserId;
-import android.annotation.ColorInt;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.app.people.ConversationStatus;
@@ -41,27 +40,28 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
-import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.icu.text.MeasureFormat;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.IconDrawableFactory;
import android.util.Log;
-import android.view.ContextThemeWrapper;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import java.text.NumberFormat;
+import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
@@ -76,6 +76,10 @@
public static final boolean DEBUG = true;
private static final String TAG = "PeopleTileView";
+ private static final int DAYS_IN_A_WEEK = 7;
+ private static final int ONE_DAY = 1;
+ private static final int MAX_WEEKS = 2;
+
public static final int LAYOUT_SMALL = 0;
public static final int LAYOUT_MEDIUM = 1;
public static final int LAYOUT_LARGE = 2;
@@ -83,7 +87,9 @@
private static final int MIN_CONTENT_MAX_LINES = 2;
private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_CONTENT = 14 + 12 + 4 + 16;
- private static final int FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT = 8 + 4 + 4 + 8;
+ private static final int MIN_MEDIUM_VERTICAL_PADDING = 4;
+ private static final int MAX_MEDIUM_PADDING = 16;
+ private static final int FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT_BEFORE_PADDING = 4 + 4;
private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL = 6 + 4 + 8;
private static final int FIXED_WIDTH_DIMENS_FOR_SMALL = 4 + 4;
@@ -96,6 +102,8 @@
public static final String EMPTY_STRING = "";
+ private int mMediumVerticalPadding;
+
private Context mContext;
private PeopleSpaceTile mTile;
private float mDensity;
@@ -205,7 +213,8 @@
private int getContentHeightForLayout(int lineHeight) {
switch (mLayoutSize) {
case LAYOUT_MEDIUM:
- return mHeight - (lineHeight + FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT);
+ return mHeight - (lineHeight + FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT_BEFORE_PADDING
+ + mMediumVerticalPadding * 2);
case LAYOUT_LARGE:
return mHeight - (getSizeInDp(
R.dimen.max_people_avatar_size_for_large_content) + lineHeight
@@ -224,7 +233,16 @@
}
// Small layout used below a certain minimum mWidth with any mHeight.
if (mWidth >= getSizeInDp(R.dimen.required_width_for_medium)) {
- if (DEBUG) Log.d(TAG, "Medium view for mWidth: " + mWidth + " mHeight: " + mHeight);
+ int spaceAvailableForPadding =
+ mHeight - (getSizeInDp(R.dimen.avatar_size_for_medium) + 4 + getLineHeight(
+ getSizeInDp(R.dimen.name_text_size_for_medium)));
+ if (DEBUG) {
+ Log.d(TAG, "Medium view for mWidth: " + mWidth + " mHeight: " + mHeight
+ + " with padding space: " + spaceAvailableForPadding);
+ }
+ int maxVerticalPadding = Math.min(Math.floorDiv(spaceAvailableForPadding, 2),
+ MAX_MEDIUM_PADDING);
+ mMediumVerticalPadding = Math.max(MIN_MEDIUM_VERTICAL_PADDING, maxVerticalPadding);
return LAYOUT_MEDIUM;
}
// Small layout can always handle our minimum mWidth and mHeight for our widget.
@@ -347,11 +365,7 @@
setMaxLines(views);
CharSequence content = mTile.getNotificationContent();
views = setPunctuationRemoteViewsFields(views, content);
- // TODO(b/184931139): Update to RemoteViews wrapper to set via attribute once available
- @ColorInt int color = Utils.getColorAttr(mContext,
- android.R.attr.textColorPrimary).getDefaultColor();
- views.setInt(R.id.text_content, "setTextColor", color);
-
+ views.setColorAttr(R.id.text_content, "setTextColor", android.R.attr.textColorPrimary);
views.setTextViewText(R.id.text_content, mTile.getNotificationContent());
views.setViewVisibility(R.id.image, View.GONE);
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_message);
@@ -398,9 +412,7 @@
views.setViewVisibility(R.id.messages_count, View.GONE);
setMaxLines(views);
// Secondary text color for statuses.
- @ColorInt int secondaryColor = Utils.getColorAttr(mContext,
- android.R.attr.textColorSecondary).getDefaultColor();
- views.setInt(R.id.text_content, "setTextColor", secondaryColor);
+ views.setColorAttr(R.id.text_content, "setTextColor", android.R.attr.textColorSecondary);
views.setTextViewText(R.id.text_content, statusText);
Icon statusIcon = status.getIcon();
@@ -541,6 +553,14 @@
views.setViewVisibility(R.id.text_content, View.VISIBLE);
views.setViewVisibility(R.id.subtext, View.GONE);
}
+
+ if (mLayoutSize == LAYOUT_MEDIUM) {
+ if (DEBUG) Log.d(TAG, "Set vertical padding: " + mMediumVerticalPadding);
+ int horizontalPadding = (int) Math.floor(MAX_MEDIUM_PADDING * mDensity);
+ int verticalPadding = (int) Math.floor(mMediumVerticalPadding * mDensity);
+ views.setViewPadding(R.id.item, horizontalPadding, verticalPadding, horizontalPadding,
+ verticalPadding);
+ }
return views;
}
@@ -551,9 +571,16 @@
views.setViewVisibility(R.id.predefined_icon, View.GONE);
views.setViewVisibility(R.id.messages_count, View.GONE);
}
- String status = PeopleSpaceUtils.getLastInteractionString(mContext,
+ String status = getLastInteractionString(mContext,
mTile.getLastInteractionTimestamp());
- views.setTextViewText(R.id.last_interaction, status);
+ if (status != null) {
+ if (DEBUG) Log.d(TAG, "Show last interaction");
+ views.setViewVisibility(R.id.last_interaction, View.VISIBLE);
+ views.setTextViewText(R.id.last_interaction, status);
+ } else {
+ if (DEBUG) Log.d(TAG, "Hide last interaction");
+ views.setViewVisibility(R.id.last_interaction, View.GONE);
+ }
return views;
}
@@ -599,4 +626,34 @@
hasNewStory);
return convertDrawableToBitmap(personDrawable);
}
+
+ /** Returns a readable status describing the {@code lastInteraction}. */
+ @Nullable
+ public static String getLastInteractionString(Context context, long lastInteraction) {
+ if (lastInteraction == 0L) {
+ Log.e(TAG, "Could not get valid last interaction");
+ return null;
+ }
+ long now = System.currentTimeMillis();
+ Duration durationSinceLastInteraction = Duration.ofMillis(now - lastInteraction);
+ MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(),
+ MeasureFormat.FormatWidth.WIDE);
+ if (durationSinceLastInteraction.toDays() <= ONE_DAY) {
+ return null;
+ } else if (durationSinceLastInteraction.toDays() < DAYS_IN_A_WEEK) {
+ return context.getString(R.string.timestamp, formatter.formatMeasures(
+ new Measure(durationSinceLastInteraction.toHours(),
+ MeasureUnit.DAY)));
+ } else if (durationSinceLastInteraction.toDays() <= DAYS_IN_A_WEEK * 2) {
+ return context.getString(durationSinceLastInteraction.toDays() == DAYS_IN_A_WEEK
+ ? R.string.timestamp : R.string.over_timestamp,
+ formatter.formatMeasures(
+ new Measure(durationSinceLastInteraction.toDays() / DAYS_IN_A_WEEK,
+ MeasureUnit.WEEK)));
+ } else {
+ // Over 2 weeks ago
+ return context.getString(R.string.over_timestamp,
+ formatter.formatMeasures(new Measure(MAX_WEEKS, MeasureUnit.WEEK)));
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
index c01a52d..c416b5e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
@@ -23,11 +23,13 @@
import android.os.Bundle;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.service.notification.NotificationStats;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.UnlaunchableAppActivity;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.statusbar.IStatusBarService;
@@ -48,15 +50,17 @@
private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
private NotificationEntryManager mNotificationEntryManager;
private final Optional<BubblesManager> mBubblesManagerOptional;
+ private final UserManager mUserManager;
private boolean mIsForTesting;
private IStatusBarService mIStatusBarService;
@Inject
public LaunchConversationActivity(NotificationEntryManager notificationEntryManager,
- Optional<BubblesManager> bubblesManagerOptional) {
+ Optional<BubblesManager> bubblesManagerOptional, UserManager userManager) {
super();
mNotificationEntryManager = notificationEntryManager;
mBubblesManagerOptional = bubblesManagerOptional;
+ mUserManager = userManager;
}
@Override
@@ -80,12 +84,24 @@
}
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_CLICKED);
try {
+
+ if (mUserManager.isQuietModeEnabled(userHandle)) {
+ if (DEBUG) Log.d(TAG, "Cannot launch app when quieted");
+ final Intent dialogIntent =
+ UnlaunchableAppActivity.createInQuietModeDialogIntent(
+ userHandle.getIdentifier());
+ this.getApplicationContext().startActivity(dialogIntent);
+ finish();
+ return;
+ }
+
NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
notificationKey);
if (entry != null && entry.canBubble() && mBubblesManagerOptional.isPresent()) {
if (DEBUG) Log.d(TAG, "Open bubble for conversation");
mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
// Just opt-out and don't cancel the notification for bubbles.
+ finish();
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index d63dc4a..fb0dcc2 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -31,7 +31,9 @@
import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetOptionsAndView;
import static com.android.systemui.people.PeopleSpaceUtils.updateAppWidgetViews;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.PendingIntent;
@@ -51,7 +53,9 @@
import android.os.Bundle;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.preference.PreferenceManager;
+import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
@@ -76,6 +80,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Stream;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -96,6 +101,8 @@
private NotificationEntryManager mNotificationEntryManager;
private PackageManager mPackageManager;
private PeopleSpaceWidgetProvider mPeopleSpaceWidgetProvider;
+ private INotificationManager mINotificationManager;
+ private UserManager mUserManager;
public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
@GuardedBy("mLock")
public static Map<PeopleTileKey, PeopleSpaceWidgetProvider.TileConversationListener>
@@ -121,17 +128,21 @@
mNotificationEntryManager = Dependency.get(NotificationEntryManager.class);
mPackageManager = mContext.getPackageManager();
mPeopleSpaceWidgetProvider = new PeopleSpaceWidgetProvider();
+ mINotificationManager = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mUserManager = context.getSystemService(UserManager.class);
}
/**
* AppWidgetManager setter used for testing.
*/
@VisibleForTesting
- protected void setAppWidgetManager(
+ public void setAppWidgetManager(
AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager,
PeopleManager peopleManager, LauncherApps launcherApps,
NotificationEntryManager notificationEntryManager, PackageManager packageManager,
- boolean isForTesting, PeopleSpaceWidgetProvider peopleSpaceWidgetProvider) {
+ boolean isForTesting, PeopleSpaceWidgetProvider peopleSpaceWidgetProvider,
+ UserManager userManager, INotificationManager notificationManager) {
mAppWidgetManager = appWidgetManager;
mIPeopleManager = iPeopleManager;
mPeopleManager = peopleManager;
@@ -140,6 +151,8 @@
mPackageManager = packageManager;
mIsForTesting = isForTesting;
mPeopleSpaceWidgetProvider = peopleSpaceWidgetProvider;
+ mUserManager = userManager;
+ mINotificationManager = notificationManager;
}
/**
@@ -764,12 +777,12 @@
* Builds a request to pin a People Tile app widget, with a preview and storing necessary
* information as the callback.
*/
- public boolean requestPinAppWidget(ShortcutInfo shortcutInfo) {
+ public boolean requestPinAppWidget(ShortcutInfo shortcutInfo, Bundle options) {
if (DEBUG) Log.d(TAG, "Requesting pin widget, shortcutId: " + shortcutInfo.getId());
RemoteViews widgetPreview = PeopleSpaceUtils.getPreview(mContext, mIPeopleManager,
mLauncherApps, mNotificationEntryManager, shortcutInfo.getId(),
- shortcutInfo.getUserHandle(), shortcutInfo.getPackage());
+ shortcutInfo.getUserHandle(), shortcutInfo.getPackage(), options);
if (widgetPreview == null) {
Log.w(TAG, "Skipping pinning widget: no tile for shortcutId: " + shortcutInfo.getId());
return false;
@@ -783,4 +796,52 @@
ComponentName componentName = new ComponentName(mContext, PeopleSpaceWidgetProvider.class);
return mAppWidgetManager.requestPinAppWidget(componentName, extras, successCallback);
}
+
+ /** Returns a list of map entries corresponding to user's priority conversations. */
+ @NonNull
+ public List<PeopleSpaceTile> getPriorityTiles()
+ throws Exception {
+ List<ConversationChannelWrapper> conversations =
+ mINotificationManager.getConversations(true).getList();
+ // Add priority conversations to tiles list.
+ Stream<ShortcutInfo> priorityConversations = conversations.stream()
+ .filter(c -> c.getNotificationChannel() != null
+ && c.getNotificationChannel().isImportantConversation())
+ .map(c -> c.getShortcutInfo());
+ List<PeopleSpaceTile> priorityTiles = PeopleSpaceUtils.getSortedTiles(mIPeopleManager,
+ mLauncherApps, mUserManager,
+ priorityConversations);
+ priorityTiles = PeopleSpaceUtils.augmentTilesFromVisibleNotifications(
+ mContext, priorityTiles, mNotificationEntryManager);
+ return priorityTiles;
+ }
+
+ /** Returns a list of map entries corresponding to user's recent conversations. */
+ @NonNull
+ public List<PeopleSpaceTile> getRecentTiles()
+ throws Exception {
+ if (DEBUG) Log.d(TAG, "Add recent conversations");
+ List<ConversationChannelWrapper> conversations =
+ mINotificationManager.getConversations(false).getList();
+ Stream<ShortcutInfo> nonPriorityConversations = conversations.stream()
+ .filter(c -> c.getNotificationChannel() == null
+ || !c.getNotificationChannel().isImportantConversation())
+ .map(c -> c.getShortcutInfo());
+
+ List<ConversationChannel> recentConversationsList =
+ mIPeopleManager.getRecentConversations().getList();
+ Stream<ShortcutInfo> recentConversations = recentConversationsList
+ .stream()
+ .map(c -> c.getShortcutInfo());
+
+ Stream<ShortcutInfo> mergedStream = Stream.concat(nonPriorityConversations,
+ recentConversations);
+ List<PeopleSpaceTile> recentTiles =
+ PeopleSpaceUtils.getSortedTiles(mIPeopleManager, mLauncherApps, mUserManager,
+ mergedStream);
+
+ recentTiles = PeopleSpaceUtils.augmentTilesFromVisibleNotifications(
+ mContext, recentTiles, mNotificationEntryManager);
+ return recentTiles;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
index f87ea7c..feb27d80 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
@@ -29,6 +29,7 @@
import androidx.annotation.MainThread
import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread
+import com.android.systemui.appops.AppOpsController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -48,7 +49,6 @@
return PrivacyDialog(context, list, starter)
}
}
-
/**
* Controller for [PrivacyDialog].
*
@@ -66,6 +66,7 @@
private val uiExecutor: Executor,
private val privacyLogger: PrivacyLogger,
private val keyguardStateController: KeyguardStateController,
+ private val appOpsController: AppOpsController,
@VisibleForTesting private val dialogProvider: DialogProvider
) {
@@ -79,7 +80,8 @@
@Background backgroundExecutor: Executor,
@Main uiExecutor: Executor,
privacyLogger: PrivacyLogger,
- keyguardStateController: KeyguardStateController
+ keyguardStateController: KeyguardStateController,
+ appOpsController: AppOpsController
) : this(
permissionManager,
packageManager,
@@ -90,6 +92,7 @@
uiExecutor,
privacyLogger,
keyguardStateController,
+ appOpsController,
defaultDialogProvider
)
@@ -127,7 +130,9 @@
}
@WorkerThread
- private fun permGroupUsage(): List<PermGroupUsage> = permissionManager.indicatorAppOpUsageData
+ private fun permGroupUsage(): List<PermGroupUsage> {
+ return permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted)
+ }
/**
* Show the [PrivacyDialog]
@@ -261,4 +266,4 @@
starter: (String, Int) -> Unit
): PrivacyDialog
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
index 4c617ed..63ec6e5 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
@@ -54,7 +54,8 @@
data class PrivacyItem(
val privacyType: PrivacyType,
val application: PrivacyApplication,
- val timeStampElapsed: Long = UNKNOWN_TIMESTAMP
+ val timeStampElapsed: Long = UNKNOWN_TIMESTAMP,
+ val paused: Boolean = false
) {
val log = "(${privacyType.logName}, ${application.packageName}(${application.uid}), " +
"$timeStampElapsed)"
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index f7e2a31..8b27b6e 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -257,7 +257,7 @@
privacyList = emptyList()
return
}
- val list = appOpsController.getActiveAppOpsForUser(UserHandle.USER_ALL).filter {
+ val list = appOpsController.getActiveAppOps(true).filter {
UserHandle.getUserId(it.uid) in currentUserIds ||
it.code == AppOpsManager.OP_PHONE_CALL_MICROPHONE ||
it.code == AppOpsManager.OP_PHONE_CALL_CAMERA
@@ -279,7 +279,9 @@
// Anything earlier than this timestamp can be removed
val removeBeforeTime = systemClock.elapsedRealtime() - TIME_TO_HOLD_INDICATORS
- val mustKeep = privacyList.filter { it.timeStampElapsed > removeBeforeTime && it !in list }
+ val mustKeep = privacyList.filter {
+ it.timeStampElapsed > removeBeforeTime && !(it isIn list)
+ }
// There are items we must keep because they haven't been around for enough time.
if (mustKeep.isNotEmpty()) {
@@ -291,7 +293,18 @@
logger.logPrivacyItemsUpdateScheduled(delay)
holdingRunnableCanceler = bgExecutor.executeDelayed(updateListAndNotifyChanges, delay)
}
- return list + mustKeep
+ return list.filter { !it.paused } + mustKeep
+ }
+
+ /**
+ * Ignores the paused status to determine if the element is in the list
+ */
+ private infix fun PrivacyItem.isIn(list: List<PrivacyItem>): Boolean {
+ return list.any {
+ it.privacyType == privacyType &&
+ it.application == application &&
+ it.timeStampElapsed == timeStampElapsed
+ }
}
private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
@@ -308,7 +321,7 @@
return null
}
val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
- return PrivacyItem(type, app, appOpItem.timeStartedElapsed)
+ return PrivacyItem(type, app, appOpItem.timeStartedElapsed, appOpItem.isDisabled)
}
interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 32723b4..f7fa5bf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -8,7 +8,6 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
@@ -71,8 +70,6 @@
private int mMinRows = 1;
private int mMaxColumns = TileLayout.NO_MAX_COLUMNS;
- private final boolean mSideLabels;
-
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context, SCROLL_CUBIC);
@@ -83,14 +80,9 @@
mLayoutDirection = getLayoutDirection();
mClippingRect = new Rect();
- TypedArray t = context.getTheme().obtainStyledAttributes(
- attrs, R.styleable.PagedTileLayout, 0, 0);
- mSideLabels = t.getBoolean(R.styleable.PagedTileLayout_sideLabels, false);
- t.recycle();
- if (mSideLabels) {
- setPageMargin(context.getResources().getDimensionPixelOffset(
+ // Make sure there's a space between pages when scroling
+ setPageMargin(context.getResources().getDimensionPixelOffset(
R.dimen.qs_tile_margin_horizontal));
- }
}
private int mLastMaxHeight = -1;
@@ -228,8 +220,7 @@
private TileLayout createTileLayout() {
TileLayout page = (TileLayout) LayoutInflater.from(getContext())
- .inflate(mSideLabels ? R.layout.qs_paged_page_side_labels
- : R.layout.qs_paged_page, this, false);
+ .inflate(R.layout.qs_paged_page, this, false);
page.setMinRows(mMinRows);
page.setMaxColumns(mMaxColumns);
return page;
@@ -345,9 +336,8 @@
// Update bottom padding, useful for removing extra space once the panel page indicator is
// hidden.
Resources res = getContext().getResources();
- if (mSideLabels) {
- setPageMargin(res.getDimensionPixelOffset(R.dimen.qs_tile_margin_horizontal));
- }
+ setPageMargin(res.getDimensionPixelOffset(R.dimen.qs_tile_margin_horizontal));
+
setPadding(0, 0, 0,
getContext().getResources().getDimensionPixelSize(
R.dimen.qs_paged_tile_layout_padding_bottom));
@@ -550,18 +540,6 @@
}
};
- public static class TilePage extends TileLayout {
-
- public TilePage(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public boolean isFull() {
- return mRecords.size() >= maxTiles();
- }
-
- }
-
private final PagerAdapter mAdapter = new PagerAdapter() {
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index ea471b9..cefcd4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -33,7 +33,6 @@
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.tileimpl.HeightOverrideable;
import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -102,14 +101,13 @@
private final Executor mExecutor;
private final TunerService mTunerService;
private boolean mShowCollapsedOnKeyguard;
- private final FeatureFlags mFeatureFlags;
@Inject
public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader,
QSPanelController qsPanelController,
QuickQSPanelController quickQSPanelController, QSTileHost qsTileHost,
QSSecurityFooter securityFooter, @Main Executor executor, TunerService tunerService,
- FeatureFlags featureFlags, QSExpansionPathInterpolator qsExpansionPathInterpolator) {
+ QSExpansionPathInterpolator qsExpansionPathInterpolator) {
mQs = qs;
mQuickQsPanel = quickPanel;
mQsPanelController = qsPanelController;
@@ -119,7 +117,6 @@
mHost = qsTileHost;
mExecutor = executor;
mTunerService = tunerService;
- mFeatureFlags = featureFlags;
mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mHost.addCallback(this);
mQsPanelController.addOnAttachStateChangeListener(this);
@@ -247,7 +244,6 @@
+ mQs.getHeader().getPaddingBottom();
firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0);
- boolean qsSideLabelsEnabled = mFeatureFlags.isQSLabelsEnabled();
int qqsTileHeight = 0;
if (mQsPanelController.areThereTiles()) {
@@ -275,22 +271,19 @@
if (count < tileLayout.getNumVisibleTiles()) {
getRelativePosition(loc1, quickTileView, view);
getRelativePosition(loc2, tileView, view);
- int yOffset = qsSideLabelsEnabled
- ? loc2[1] - loc1[1]
- : mQuickStatusBarHeader.getOffsetTranslation();
+ int yOffset = loc2[1] - loc1[1];
// Move the quick tile right from its location to the new one.
- View v = qsSideLabelsEnabled ? quickTileView.getIcon() : quickTileView;
+ View v = quickTileView.getIcon();
translationXBuilder.addFloat(v, "translationX", 0, xDiff);
translationYBuilder.addFloat(v, "translationY", 0, yDiff - yOffset);
mAllViews.add(v);
// Move the real tile from the quick tile position to its final
// location.
- v = qsSideLabelsEnabled ? tileIcon : tileView;
+ v = tileIcon;
translationXBuilder.addFloat(v, "translationX", -xDiff, 0);
translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0);
- if (qsSideLabelsEnabled) {
// Offset the translation animation on the views
// (that goes from 0 to getOffsetTranslation)
int offsetWithQSBHTranslation =
@@ -300,28 +293,24 @@
translationYBuilder.addFloat(tileView, "translationY",
-offsetWithQSBHTranslation, 0);
- if (mQQSTileHeightAnimator == null) {
- mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
- quickTileView.getHeight(), tileView.getHeight());
- qqsTileHeight = quickTileView.getHeight();
- }
-
- mQQSTileHeightAnimator.addView(quickTileView);
- View qqsLabelContainer = quickTileView.getLabelContainer();
- View qsLabelContainer = tileView.getLabelContainer();
-
- getRelativePosition(loc1, qqsLabelContainer, view);
- getRelativePosition(loc2, qsLabelContainer, view);
- yDiff = loc2[1] - loc1[1] - yOffset;
-
- translationYBuilder.addFloat(qqsLabelContainer, "translationY", 0,
- yDiff);
- translationYBuilder.addFloat(qsLabelContainer, "translationY", -yDiff,
- 0);
- mAllViews.add(qqsLabelContainer);
- mAllViews.add(qsLabelContainer);
+ if (mQQSTileHeightAnimator == null) {
+ mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
+ quickTileView.getHeight(), tileView.getHeight());
+ qqsTileHeight = quickTileView.getHeight();
}
+ mQQSTileHeightAnimator.addView(quickTileView);
+ View qqsLabelContainer = quickTileView.getLabelContainer();
+ View qsLabelContainer = tileView.getLabelContainer();
+
+ getRelativePosition(loc1, qqsLabelContainer, view);
+ getRelativePosition(loc2, qsLabelContainer, view);
+ yDiff = loc2[1] - loc1[1] - yOffset;
+
+ translationYBuilder.addFloat(qqsLabelContainer, "translationY", 0, yDiff);
+ translationYBuilder.addFloat(qsLabelContainer, "translationY", -yDiff, 0);
+ mAllViews.add(qqsLabelContainer);
+ mAllViews.add(qsLabelContainer);
} else { // These tiles disappear when expanding
firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0);
translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
@@ -333,11 +322,7 @@
translationX);
}
- if (qsSideLabelsEnabled) {
- mQuickQsViews.add(tileView);
- } else {
- mQuickQsViews.add(tileView.getIconWithBackground());
- }
+ mQuickQsViews.add(tileView);
mAllViews.add(tileView.getIcon());
mAllViews.add(quickTileView);
} else if (mFullRows && isIconInAnimatedRow(count)) {
@@ -346,27 +331,22 @@
mAllViews.add(tileIcon);
} else {
- if (!qsSideLabelsEnabled) {
- firstPageBuilder.addFloat(tileView, "alpha", 0, 1);
- firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
- } else {
- // Pretend there's a corresponding QQS tile (for the position) that we are
- // expanding from.
- SideLabelTileLayout qqsLayout =
- (SideLabelTileLayout) mQuickQsPanel.getTileLayout();
- getRelativePosition(loc1, qqsLayout, view);
- getRelativePosition(loc2, tileView, view);
- int diff = loc2[1] - (loc1[1] + qqsLayout.getPhantomTopPosition(count));
- translationYBuilder.addFloat(tileView, "translationY", -diff, 0);
- if (mOtherTilesExpandAnimator == null) {
- mOtherTilesExpandAnimator =
- new HeightExpansionAnimator(
- this, qqsTileHeight, tileView.getHeight());
- }
- mOtherTilesExpandAnimator.addView(tileView);
- tileView.setClipChildren(true);
- tileView.setClipToPadding(true);
+ // Pretend there's a corresponding QQS tile (for the position) that we are
+ // expanding from.
+ SideLabelTileLayout qqsLayout =
+ (SideLabelTileLayout) mQuickQsPanel.getTileLayout();
+ getRelativePosition(loc1, qqsLayout, view);
+ getRelativePosition(loc2, tileView, view);
+ int diff = loc2[1] - (loc1[1] + qqsLayout.getPhantomTopPosition(count));
+ translationYBuilder.addFloat(tileView, "translationY", -diff, 0);
+ if (mOtherTilesExpandAnimator == null) {
+ mOtherTilesExpandAnimator =
+ new HeightExpansionAnimator(
+ this, qqsTileHeight, tileView.getHeight());
}
+ mOtherTilesExpandAnimator.addView(tileView);
+ tileView.setClipChildren(true);
+ tileView.setClipToPadding(true);
}
mAllViews.add(tileView);
@@ -392,7 +372,6 @@
.build();
// Fade in the tiles/labels as we reach the final position.
Builder builder = new Builder()
- .setStartDelay(qsSideLabelsEnabled ? 0 : EXPANDED_TILE_DELAY)
.addFloat(tileLayout, "alpha", 0, 1);
mFirstPageDelayedAnimator = builder.build();
@@ -470,12 +449,7 @@
// Returns true if the view is a possible page in PagedTileLayout
private boolean isAPage(View view) {
- if (view instanceof PagedTileLayout.TilePage) {
- return true;
- } else if (view instanceof SideLabelTileLayout) {
- return !(view instanceof QuickQSPanel.QQSSideLabelTileLayout);
- }
- return false;
+ return view.getClass().equals(SideLabelTileLayout.class);
}
public void setPosition(float position) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 586176f..bf9acc2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -22,7 +22,6 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.util.AttributeSet;
-import android.util.Pair;
import android.view.View;
import android.view.WindowInsets;
import android.widget.FrameLayout;
@@ -61,7 +60,6 @@
private QuickStatusBarHeader mHeader;
private float mQsExpansion;
private QSCustomizer mQSCustomizer;
- private View mDragHandle;
private NonInterceptingScrollView mQSPanelContainer;
private View mBackground;
@@ -84,7 +82,6 @@
mQSDetail = findViewById(R.id.qs_detail);
mHeader = findViewById(R.id.header);
mQSCustomizer = findViewById(R.id.qs_customize);
- mDragHandle = findViewById(R.id.qs_drag_handle_view);
mBackground = findViewById(R.id.quick_settings_background);
mHeader.getHeaderQsPanel().setMediaVisibilityChangedListener((visible) -> {
if (mHeader.getHeaderQsPanel().isShown()) {
@@ -240,8 +237,6 @@
int scrollBottom = calculateContainerBottom();
setBottom(getTop() + height);
mQSDetail.setBottom(getTop() + scrollBottom);
- // Pin the drag handle to the bottom of the panel.
- mDragHandle.setTranslationY(scrollBottom - mDragHandle.getHeight());
mBackground.setTop(mQSPanelContainer.getTop());
updateBackgroundBottom(scrollBottom, animate);
}
@@ -278,7 +273,6 @@
public void setExpansion(float expansion) {
mQsExpansion = expansion;
- mDragHandle.setAlpha(1.0f - expansion);
updateExpansion();
}
@@ -296,9 +290,6 @@
if (view == mQSPanelContainer) {
// QS panel lays out some of its content full width
qsPanelController.setContentMargins(mContentPadding, mContentPadding);
- Pair<Integer, Integer> margins = qsPanelController.getVisualSideMargins();
- // Apply paddings based on QSPanel
- mQSCustomizer.setContentPaddings(margins.first, margins.second);
} else if (view == mHeader) {
// The header contains the QQS panel which needs to have special padding, to
// visually align them.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index eb7b115..c4986cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -71,7 +71,6 @@
private float mExpansionAmount;
protected View mEdit;
- protected View mEditContainer;
private TouchAnimator mSettingsCogAnimator;
private View mActionsContainer;
@@ -107,7 +106,6 @@
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
mActionsContainer = requireViewById(R.id.qs_footer_actions_container);
- mEditContainer = findViewById(R.id.qs_footer_actions_edit_container);
mBuildText = findViewById(R.id.build);
mTunerIcon = requireViewById(R.id.tuner_icon);
@@ -185,9 +183,6 @@
.addFloat(mPageIndicator, "alpha", 0, 1)
.addFloat(mBuildText, "alpha", 0, 1)
.setStartDelay(0.9f);
- if (mEditContainer != null) {
- builder.addFloat(mEditContainer, "alpha", 0, 1);
- }
return builder.build();
}
@@ -283,12 +278,9 @@
mTunerIcon.setVisibility(isTunerEnabled ? View.VISIBLE : View.INVISIBLE);
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
mMultiUserSwitch.setVisibility(showUserSwitcher() ? View.VISIBLE : View.GONE);
- if (mEditContainer != null) {
- mEditContainer.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
- }
mSettingsButton.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
- mBuildText.setVisibility(mExpanded && mShouldShowBuildText ? View.VISIBLE : View.GONE);
+ mBuildText.setVisibility(mExpanded && mShouldShowBuildText ? View.VISIBLE : View.INVISIBLE);
}
private boolean showUserSwitcher() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 3467838..74ae3a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -32,6 +32,7 @@
import com.android.internal.logging.nano.MetricsProto;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.globalactions.GlobalActionsDialogLite;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
@@ -65,6 +66,7 @@
private final MetricsLogger mMetricsLogger;
private final FalsingManager mFalsingManager;
private final SettingsButton mSettingsButton;
+ private final View mSettingsButtonContainer;
private final TextView mBuildText;
private final View mEdit;
private final MultiUserSwitch mMultiUserSwitch;
@@ -152,6 +154,7 @@
mFalsingManager = falsingManager;
mSettingsButton = mView.findViewById(R.id.settings_button);
+ mSettingsButtonContainer = mView.findViewById(R.id.settings_button_container);
mBuildText = mView.findViewById(R.id.build);
mEdit = mView.findViewById(android.R.id.edit);
mMultiUserSwitch = mView.findViewById(R.id.multi_user_switch);
@@ -258,10 +261,12 @@
mView.disable(state2, isTunerEnabled());
}
-
private void startSettingsActivity() {
+ ActivityLaunchAnimator.Controller animationController =
+ mSettingsButtonContainer != null ? ActivityLaunchAnimator.Controller.fromView(
+ mSettingsButtonContainer) : null;
mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
- true /* dismissShade */);
+ true /* dismissShade */, animationController);
}
private boolean isTunerEnabled() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 27cc268..f89e70a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -27,12 +27,10 @@
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
-import android.util.Pair;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewStub;
import android.widget.LinearLayout;
import com.android.internal.logging.UiEventLogger;
@@ -72,8 +70,6 @@
private final H mHandler = new H();
/** Whether or not the QS media player feature is enabled. */
protected boolean mUsingMediaPlayer;
- private int mVisualMarginStart;
- private int mVisualMarginEnd;
protected boolean mExpanded;
protected boolean mListening;
@@ -96,7 +92,6 @@
private PageIndicator mFooterPageIndicator;
private int mContentMarginStart;
private int mContentMarginEnd;
- private int mVisualTilePadding;
private boolean mUsingHorizontalLayout;
private Record mDetailRecord;
@@ -111,9 +106,7 @@
protected QSTileLayout mTileLayout;
private int mLastOrientation = -1;
private int mMediaTotalBottomMargin;
- private int mFooterMarginStartHorizontal;
private Consumer<Boolean> mMediaVisibilityChangedListener;
- protected boolean mSideLabels;
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -128,21 +121,7 @@
}
- protected void inflateQSFooter(boolean newFooter) {
- ViewStub stub = findViewById(R.id.qs_footer_stub);
- if (stub != null) {
- stub.setLayoutResource(
- newFooter ? R.layout.qs_footer_impl_two_lines : R.layout.qs_footer_impl);
- stub.inflate();
- mFooter = findViewById(R.id.qs_footer);
- }
- }
-
- void initialize(boolean sideLabels) {
- mSideLabels = sideLabels;
-
- inflateQSFooter(sideLabels);
-
+ void initialize() {
mRegularTileLayout = createRegularTileLayout();
mTileLayout = mRegularTileLayout;
@@ -195,8 +174,7 @@
public QSTileLayout createRegularTileLayout() {
if (mRegularTileLayout == null) {
mRegularTileLayout = (QSTileLayout) LayoutInflater.from(mContext)
- .inflate(mSideLabels ? R.layout.qs_paged_tile_layout_side_labels
- : R.layout.qs_paged_tile_layout, this, false);
+ .inflate(R.layout.qs_paged_tile_layout, this, false);
}
return mRegularTileLayout;
}
@@ -311,11 +289,6 @@
}
public void updateResources() {
- int tileSize = getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
- int tileBg = getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
- mFooterMarginStartHorizontal = getResources().getDimensionPixelSize(
- R.dimen.qs_footer_horizontal_margin);
- mVisualTilePadding = mSideLabels ? 0 : (int) ((tileSize - tileBg) / 2.0f);
updatePadding();
updatePageIndicator();
@@ -358,6 +331,7 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mFooter = findViewById(R.id.qs_footer);
mDivider = findViewById(R.id.divider);
}
@@ -638,60 +612,10 @@
// to the edge like the brightness slider
mContentMarginStart = startMargin;
mContentMarginEnd = endMargin;
- updateTileLayoutMargins(mContentMarginStart - mVisualTilePadding,
- mContentMarginEnd - mVisualTilePadding);
updateMediaHostContentMargins(mediaHostView);
- updateFooterMargin();
updateDividerMargin();
}
- private void updateFooterMargin() {
- if (mFooter != null) {
- int footerMargin = 0;
- int indicatorMargin = 0;
- if (mUsingHorizontalLayout && !mSideLabels) {
- footerMargin = mFooterMarginStartHorizontal;
- indicatorMargin = footerMargin - mVisualMarginEnd;
- }
- updateMargins(mFooter, footerMargin, 0);
- // The page indicator isn't centered anymore because of the visual positioning.
- // Let's fix it by adding some margin
- if (mFooterPageIndicator != null) {
- updateMargins(mFooterPageIndicator, 0, indicatorMargin);
- }
- }
- }
-
- /**
- * Update the margins of all tile Layouts.
- *
- * @param visualMarginStart the visual start margin of the tile, adjusted for local insets
- * to the tile. This can be set on a tileLayout
- * @param visualMarginEnd the visual end margin of the tile, adjusted for local insets
- * to the tile. This can be set on a tileLayout
- */
- private void updateTileLayoutMargins(int visualMarginStart, int visualMarginEnd) {
- mVisualMarginStart = visualMarginStart;
- mVisualMarginEnd = visualMarginEnd;
- updateTileLayoutMargins();
- }
-
- public Pair<Integer, Integer> getVisualSideMargins() {
- if (mSideLabels) {
- return new Pair(0, 0);
- } else {
- return new Pair(mVisualMarginStart, mUsingHorizontalLayout ? 0 : mVisualMarginEnd);
- }
- }
-
- private void updateTileLayoutMargins() {
- int marginEnd = mVisualMarginEnd;
- if (mUsingHorizontalLayout || mSideLabels) {
- marginEnd = 0;
- }
- updateMargins((View) mTileLayout, mSideLabels ? 0 : mVisualMarginStart, marginEnd);
- }
-
private void updateDividerMargin() {
if (mDivider == null) return;
updateMargins(mDivider, mContentMarginStart, mContentMarginEnd);
@@ -769,22 +693,13 @@
newLayout.setListening(mListening, uiEventLogger);
if (needsDynamicRowsAndColumns()) {
newLayout.setMinRows(horizontal ? 2 : 1);
- // Let's use 3 columns to match the current layout
- int columns;
- if (mSideLabels) {
- columns = horizontal ? 2 : 4;
- } else {
- columns = horizontal ? 3 : TileLayout.NO_MAX_COLUMNS;
- }
- newLayout.setMaxColumns(columns);
+ newLayout.setMaxColumns(horizontal ? 2 : 4);
}
updateMargins(mediaHostView);
}
}
private void updateMargins(ViewGroup mediaHostView) {
- updateTileLayoutMargins();
- updateFooterMargin();
updateDividerMargin();
updateMediaHostContentMargins(mediaHostView);
updateHorizontalLinearLayoutMargins();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index eda1abb..93ccfc72 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -16,13 +16,14 @@
package com.android.systemui.qs;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE;
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
import android.annotation.NonNull;
import android.content.res.Configuration;
-import android.util.Pair;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -32,6 +33,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.customize.QSCustomizerController;
@@ -59,6 +61,7 @@
private final TunerService mTunerService;
private final QSCustomizerController mQsCustomizerController;
private final QSTileRevealController.Factory mQsTileRevealControllerFactory;
+ private final FalsingManager mFalsingManager;
private final BrightnessController mBrightnessController;
private final BrightnessSlider.Factory mBrightnessSliderFactory;
private final BrightnessSlider mBrightnessSlider;
@@ -82,6 +85,16 @@
private final BrightnessMirrorController.BrightnessMirrorListener mBrightnessMirrorListener =
mirror -> updateBrightnessMirror();
+ private View.OnTouchListener mTileLayoutTouchListener = new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mFalsingManager.isFalseTouch(QS_SWIPE);
+ }
+ return false;
+ }
+ };
+
@Inject
QSPanelController(QSPanel view, QSSecurityFooter qsSecurityFooter, TunerService tunerService,
QSTileHost qstileHost, QSCustomizerController qsCustomizerController,
@@ -90,7 +103,7 @@
QSTileRevealController.Factory qsTileRevealControllerFactory,
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
- BrightnessSlider.Factory brightnessSliderFactory,
+ BrightnessSlider.Factory brightnessSliderFactory, FalsingManager falsingManager,
FeatureFlags featureFlags) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager, featureFlags);
@@ -98,6 +111,7 @@
mTunerService = tunerService;
mQsCustomizerController = qsCustomizerController;
mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
+ mFalsingManager = falsingManager;
mQsSecurityFooter.setHostEnvironment(qstileHost);
mBrightnessSliderFactory = brightnessSliderFactory;
@@ -134,6 +148,9 @@
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.addCallback(mBrightnessMirrorListener);
}
+
+ ((PagedTileLayout) mView.createRegularTileLayout())
+ .setOnTouchListener(mTileLayoutTouchListener);
}
@Override
@@ -291,11 +308,6 @@
}
/** */
- public Pair<Integer, Integer> getVisualSideMargins() {
- return mView.getVisualSideMargins();
- }
-
- /** */
public void showDetailAdapter(DetailAdapter detailAdapter, int x, int y) {
mView.showDetailAdapter(true, detailAdapter, new int[]{x, y});
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index e41a038..925c9eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -75,8 +75,6 @@
private float mRevealExpansion;
private final QSHost.Callback mQSHostCallback = this::setTiles;
- protected boolean mShowLabels = true;
- protected boolean mQSLabelFlag;
private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
new QSPanel.OnConfigurationChangedListener() {
@@ -121,14 +119,13 @@
mQSLogger = qsLogger;
mDumpManager = dumpManager;
mFeatureFlags = featureFlags;
- mQSLabelFlag = featureFlags.isQSLabelsEnabled();
mShouldUseSplitNotificationShade =
Utils.shouldUseSplitNotificationShade(mFeatureFlags, getResources());
}
@Override
protected void onInit() {
- mView.initialize(mQSLabelFlag);
+ mView.initialize();
mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), "");
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index e7828c3..63733b3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -50,16 +50,11 @@
}
@Override
- void initialize(boolean sideLabels) {
- super.initialize(sideLabels);
+ void initialize() {
+ super.initialize();
applyBottomMargin((View) mRegularTileLayout);
}
- @Override
- protected void inflateQSFooter(boolean newFooter) {
- // No footer
- }
-
private void applyBottomMargin(View view) {
int margin = getResources().getDimensionPixelSize(R.dimen.qs_header_tile_margin_bottom);
MarginLayoutParams layoutParams = (MarginLayoutParams) view.getLayoutParams();
@@ -74,22 +69,14 @@
@Override
public TileLayout createRegularTileLayout() {
- if (mSideLabels) {
- return new QQSSideLabelTileLayout(mContext);
- } else {
- return new QuickQSPanel.HeaderTileLayout(mContext);
- }
+ return new QQSSideLabelTileLayout(mContext);
}
@Override
protected QSTileLayout createHorizontalTileLayout() {
- if (mSideLabels) {
- TileLayout t = createRegularTileLayout();
- t.setMaxColumns(2);
- return t;
- } else {
- return new DoubleLineTileLayout(mContext);
- }
+ TileLayout t = createRegularTileLayout();
+ t.setMaxColumns(2);
+ return t;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 3f06312..7123e49 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -235,6 +235,12 @@
public void onAnimationStarted() {
mIconContainer.removeIgnoredSlot(mMobileSlotName);
}
+
+ @Override
+ public void onAnimationAtStart() {
+ super.onAnimationAtStart();
+ mIconContainer.removeIgnoredSlot(mMobileSlotName);
+ }
});
}
mAlphaAnimator = builder.build();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 30a08c6..7518b20 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -141,19 +141,6 @@
}
}
- /**
- * Sets the padding for the RecyclerView. Also, updates the margin between the tiles in the
- * {@link TileAdapter}.
- */
- public void setContentPaddings(int paddingStart, int paddingEnd) {
- mRecyclerView.setPaddingRelative(
- paddingStart,
- mRecyclerView.getPaddingTop(),
- paddingEnd,
- mRecyclerView.getPaddingBottom()
- );
- }
-
/** Hide the customizer. */
public void hide(boolean animate) {
if (isShown) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 006b230..5080533 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -14,8 +14,6 @@
package com.android.systemui.qs.customize;
-import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
-
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
@@ -61,7 +59,6 @@
import java.util.List;
import javax.inject.Inject;
-import javax.inject.Named;
/** */
@QSScope
@@ -110,25 +107,21 @@
private final AccessibilityDelegateCompat mAccessibilityDelegate;
private RecyclerView mRecyclerView;
private int mNumColumns;
- private final boolean mUseHorizontalTiles;
@Inject
public TileAdapter(
@QSThemedContext Context context,
QSTileHost qsHost,
- UiEventLogger uiEventLogger,
- @Named(QS_LABELS_FLAG) boolean useHorizontalTiles
- ) {
+ UiEventLogger uiEventLogger) {
mContext = context;
mHost = qsHost;
mUiEventLogger = uiEventLogger;
mItemTouchHelper = new ItemTouchHelper(mCallbacks);
mDecoration = new TileItemDecoration(context);
- mMarginDecoration = new MarginTileDecoration(!useHorizontalTiles);
+ mMarginDecoration = new MarginTileDecoration();
mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles);
mNumColumns = context.getResources().getInteger(NUM_COLUMNS_ID);
mAccessibilityDelegate = new TileAdapterDelegate();
- mUseHorizontalTiles = useHorizontalTiles;
mSizeLookup.setSpanIndexCacheEnabled(true);
}
@@ -287,9 +280,7 @@
}
FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent,
false);
- View view = mUseHorizontalTiles
- ? new CustomizeTileViewHorizontal(context, new QSIconViewImpl(context))
- : new CustomizeTileView(context, new QSIconViewImpl(context));
+ View view = new CustomizeTileViewHorizontal(context, new QSIconViewImpl(context));
frame.addView(view);
return new Holder(frame);
}
@@ -715,11 +706,6 @@
private static class MarginTileDecoration extends ItemDecoration {
private int mHalfMargin;
- private final boolean mUseOutsideMargins;
-
- private MarginTileDecoration(boolean useOutsideMargins) {
- mUseOutsideMargins = useOutsideMargins;
- }
public void setHalfMargin(int halfMargin) {
mHalfMargin = halfMargin;
@@ -738,9 +724,9 @@
if (view instanceof TextView) {
super.getItemOffsets(outRect, view, parent, state);
} else {
- if (mUseOutsideMargins || (column != 0 && column != lm.getSpanCount() - 1)) {
- // Using outside margins or in a column that's not leftmost or rightmost
- // (half of the margin between columns).
+ if (column != 0 && column != lm.getSpanCount() - 1) {
+ // In a column that's not leftmost or rightmost (half of the margin between
+ // columns).
outRect.left = mHalfMargin;
outRect.right = mHalfMargin;
} else if (column == 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
index 10192bc..a1e1d64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
@@ -30,18 +30,11 @@
@Module
public interface QSFlagsModule {
- String QS_LABELS_FLAG = "qs_labels_flag";
+
String RBC_AVAILABLE = "rbc_available";
String PM_LITE_ENABLED = "pm_lite";
String PM_LITE_SETTING = "sysui_pm_lite";
- @Provides
- @SysUISingleton
- @Named(QS_LABELS_FLAG)
- static boolean provideQSFlag(FeatureFlags featureFlags) {
- return featureFlags.isQSLabelsEnabled();
- }
-
/** */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 9b0536c..3437dd5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -14,8 +14,6 @@
package com.android.systemui.qs.tileimpl;
-import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
-
import android.content.Context;
import android.os.Build;
import android.util.Log;
@@ -56,7 +54,6 @@
import com.android.systemui.util.leak.GarbageMonitor;
import javax.inject.Inject;
-import javax.inject.Named;
import javax.inject.Provider;
import dagger.Lazy;
@@ -97,12 +94,9 @@
private final Lazy<QSHost> mQsHostLazy;
private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
- private final boolean mSideLabels;
-
@Inject
public QSFactoryImpl(
Lazy<QSHost> qsHostLazy,
- @Named(QS_LABELS_FLAG) boolean useSideLabels,
Provider<CustomTile.Builder> customTileBuilderProvider,
Provider<WifiTile> wifiTileProvider,
Provider<InternetTile> internetTileProvider,
@@ -134,8 +128,6 @@
mQsHostLazy = qsHostLazy;
mCustomTileBuilderProvider = customTileBuilderProvider;
- mSideLabels = useSideLabels;
-
mWifiTileProvider = wifiTileProvider;
mInternetTileProvider = internetTileProvider;
mBluetoothTileProvider = bluetoothTileProvider;
@@ -251,12 +243,6 @@
@Override
public QSTileView createTileView(Context context, QSTile tile, boolean collapsedView) {
QSIconView icon = tile.createTileView(context);
- if (mSideLabels) {
- return new QSTileViewHorizontal(context, icon, collapsedView);
- } else if (collapsedView) {
- return new QSTileBaseView(context, icon, collapsedView);
- } else {
- return new com.android.systemui.qs.tileimpl.QSTileView(context, icon);
- }
+ return new QSTileViewHorizontal(context, icon, collapsedView);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index aa8ce85..ba69dd5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -66,9 +66,9 @@
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.PagedTileLayout.TilePage;
import com.android.systemui.qs.QSEvent;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.SideLabelTileLayout;
import com.android.systemui.qs.logging.QSLogger;
import java.io.FileDescriptor;
@@ -497,7 +497,7 @@
private void updateIsFullQs() {
for (Object listener : mListeners) {
- if (TilePage.class.equals(listener.getClass())) {
+ if (SideLabelTileLayout.class.equals(listener.getClass())) {
mIsFullQs = 1;
return;
}
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 b728b43..6f19276 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -184,8 +184,7 @@
@Override
public CharSequence getTileLabel() {
- CharSequence qawLabel = mQuickAccessWalletClient.getServiceLabel();
- return qawLabel == null ? mLabel : qawLabel;
+ return mLabel;
}
private void queryWalletCards() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 33041d2..fa28754 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -17,11 +17,12 @@
package com.android.systemui.screenshot;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.HardwareRenderer;
+import android.graphics.Matrix;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
@@ -32,11 +33,14 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
-import android.view.IWindowManager;
import android.view.ScrollCaptureResponse;
import android.view.View;
+import android.view.Window;
import android.widget.ImageView;
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.internal.app.ChooserActivity;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
@@ -64,17 +68,16 @@
public static final String EXTRA_CAPTURE_RESPONSE = "capture-response";
private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path";
+ private static final boolean USE_SHARED_ELEMENT = false;
+
private final UiEventLogger mUiEventLogger;
- private final ScrollCaptureController mScrollCaptureController;
private final Executor mUiExecutor;
private final Executor mBackgroundExecutor;
private final ImageExporter mImageExporter;
-
- // If true, the activity is re-loading an image from storage, which should either succeed and
- // populate the UI or fail and finish the activity.
- private boolean mRestoringInstance;
+ private final LongScreenshotHolder mLongScreenshotHolder;
private ImageView mPreview;
+ private ImageView mTransitionView;
private View mSave;
private View mEdit;
private View mShare;
@@ -86,8 +89,8 @@
private ListenableFuture<File> mCacheSaveFuture;
private ListenableFuture<ImageLoader.Result> mCacheLoadFuture;
- private ListenableFuture<LongScreenshot> mLongScreenshotFuture;
private LongScreenshot mLongScreenshot;
+ private boolean mTransitionStarted;
private enum PendingAction {
SHARE,
@@ -97,21 +100,21 @@
@Inject
public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter,
- @Main Executor mainExecutor, @Background Executor bgExecutor, IWindowManager wms,
- Context context, ScrollCaptureController scrollCaptureController) {
+ @Main Executor mainExecutor, @Background Executor bgExecutor,
+ LongScreenshotHolder longScreenshotHolder) {
mUiEventLogger = uiEventLogger;
mUiExecutor = mainExecutor;
mBackgroundExecutor = bgExecutor;
mImageExporter = imageExporter;
- mScrollCaptureController = scrollCaptureController;
+ mLongScreenshotHolder = longScreenshotHolder;
}
@Override
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")");
-
super.onCreate(savedInstanceState);
+ getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
setContentView(R.layout.long_screenshot);
mPreview = requireViewById(R.id.preview);
@@ -121,6 +124,7 @@
mCropView = requireViewById(R.id.crop_view);
mMagnifierView = requireViewById(R.id.magnifier);
mCropView.setCropInteractionListener(mMagnifierView);
+ mTransitionView = requireViewById(R.id.transition);
mSave.setOnClickListener(this::onClicked);
mEdit.setOnClickListener(this::onClicked);
@@ -152,7 +156,7 @@
super.onStart();
if (mCacheLoadFuture != null) {
- Log.d(TAG, "mRestoringInstance = true");
+ Log.d(TAG, "mCacheLoadFuture != null");
final ListenableFuture<ImageLoader.Result> future = mCacheLoadFuture;
mCacheLoadFuture.addListener(() -> {
Log.d(TAG, "cached bitmap load complete");
@@ -170,42 +174,22 @@
}, mUiExecutor);
mCacheLoadFuture = null;
return;
- }
-
- if (mLongScreenshotFuture == null) {
- Log.d(TAG, "mLongScreenshotFuture == null");
- // First run through, ensure we have a connection to use (see #onCreate)
- if (mScrollCaptureResponse == null || !mScrollCaptureResponse.isConnected()) {
- Log.e(TAG, "Did not receive a live scroll capture connection, bailing out!");
- finishAndRemoveTask();
- return;
- }
- mLongScreenshotFuture = mScrollCaptureController.run(mScrollCaptureResponse);
- mLongScreenshotFuture.addListener(() -> {
- LongScreenshot longScreenshot;
- try {
- longScreenshot = mLongScreenshotFuture.get();
- } catch (CancellationException | InterruptedException | ExecutionException e) {
- Log.e(TAG, "Error capturing long screenshot!", e);
- finishAndRemoveTask();
- return;
- }
- if (longScreenshot.getHeight() == 0) {
- Log.e(TAG, "Got a zero height result");
- finishAndRemoveTask();
- return;
- }
- onCaptureCompleted(longScreenshot);
- }, mUiExecutor);
} else {
- Log.d(TAG, "mLongScreenshotFuture != null");
+ LongScreenshot longScreenshot = mLongScreenshotHolder.takeLongScreenshot();
+ if (longScreenshot != null) {
+ onLongScreenshotReceived(longScreenshot);
+ } else {
+ Log.e(TAG, "No long screenshot available!");
+ finishAndRemoveTask();
+ }
}
}
- private void onCaptureCompleted(LongScreenshot longScreenshot) {
- Log.d(TAG, "onCaptureCompleted(longScreenshot=" + longScreenshot + ")");
+ private void onLongScreenshotReceived(LongScreenshot longScreenshot) {
+ Log.d(TAG, "onLongScreenshotReceived(longScreenshot=" + longScreenshot + ")");
mLongScreenshot = longScreenshot;
mPreview.setImageDrawable(mLongScreenshot.getDrawable());
+ mTransitionView.setImageDrawable(mLongScreenshot.getDrawable());
updateImageDimensions();
mCropView.setVisibility(View.VISIBLE);
mMagnifierView.setDrawable(mLongScreenshot.getDrawable(),
@@ -277,15 +261,15 @@
protected void onStop() {
Log.d(TAG, "onStop finishing=" + isFinishing());
super.onStop();
+ if (mTransitionStarted) {
+ finish();
+ }
if (isFinishing()) {
if (mScrollCaptureResponse != null) {
mScrollCaptureResponse.close();
}
cleanupCache();
- if (mLongScreenshotFuture != null) {
- mLongScreenshotFuture.cancel(true);
- }
if (mLongScreenshot != null) {
mLongScreenshot.release();
}
@@ -323,11 +307,22 @@
intent.setComponent(ComponentName.unflattenFromString(editorPackage));
}
intent.setDataAndType(uri, "image/png");
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- startActivityAsUser(intent, UserHandle.CURRENT);
- finishAndRemoveTask();
+ if (USE_SHARED_ELEMENT) {
+ updateImageDimensions();
+ mTransitionView.setVisibility(View.VISIBLE);
+ // TODO: listen for transition completing instead of finishing onStop
+ mTransitionStarted = true;
+ startActivity(intent,
+ ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
+ } else {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivityAsUser(intent, UserHandle.CURRENT);
+ finishAndRemoveTask();
+ }
}
private void doShare(Uri uri) {
@@ -417,19 +412,47 @@
- mPreview.getPaddingBottom();
float viewRatio = previewWidth / (float) previewHeight;
+ // Top and left offsets of the image relative to mPreview.
+ int imageLeft = mPreview.getPaddingLeft();
+ int imageTop = mPreview.getPaddingTop();
+
+ // The image width and height on screen
+ int imageHeight = previewHeight;
+ int imageWidth = previewWidth;
+ float scale;
if (imageRatio > viewRatio) {
// Image is full width and height is constrained, compute extra padding to inform
// CropView
- float imageHeight = previewHeight * viewRatio / imageRatio;
- int extraPadding = (int) (previewHeight - imageHeight) / 2;
+ imageHeight = (int) (previewHeight * viewRatio / imageRatio);
+ int extraPadding = (previewHeight - imageHeight) / 2;
mCropView.setExtraPadding(extraPadding + mPreview.getPaddingTop(),
extraPadding + mPreview.getPaddingBottom());
+ imageTop += (previewHeight - imageHeight) / 2;
+ scale = imageHeight / bounds.height();
+ mCropView.setExtraPadding(extraPadding, extraPadding);
mCropView.setImageWidth(previewWidth);
} else {
+ imageWidth = (int) (previewWidth * imageRatio / viewRatio);
+ imageLeft += (previewWidth - imageWidth) / 2;
+ scale = imageWidth / (float) bounds.width();
// Image is full height
mCropView.setExtraPadding(mPreview.getPaddingTop(), mPreview.getPaddingBottom());
mCropView.setImageWidth((int) (previewHeight * imageRatio));
}
+ // Update transition view's position and scale.
+ Rect boundaries = mCropView.getCropBoundaries(imageWidth, imageHeight);
+ mTransitionView.setTranslationX(imageLeft + boundaries.left);
+ mTransitionView.setTranslationY(imageTop + boundaries.top);
+ ConstraintLayout.LayoutParams params =
+ (ConstraintLayout.LayoutParams) mTransitionView.getLayoutParams();
+ params.width = boundaries.width();
+ params.height = boundaries.height();
+ mTransitionView.setLayoutParams(params);
+
+ Matrix matrix = new Matrix();
+ matrix.postScale(scale, scale, 0, 0);
+ matrix.postTranslate(-boundaries.left, -boundaries.top);
+ mTransitionView.setImageMatrix(matrix);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotHolder.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotHolder.java
new file mode 100644
index 0000000..39c6f07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotHolder.java
@@ -0,0 +1,59 @@
+/*
+ * 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.screenshot;
+
+import com.android.systemui.dagger.SysUISingleton;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.inject.Inject;
+
+/**
+ * LongScreenshotHolder holds on to 1 LongScreenshot reference to facilitate indirect in-process
+ * passing.
+ */
+@SysUISingleton
+public class LongScreenshotHolder {
+ private final AtomicReference<ScrollCaptureController.LongScreenshot> mLongScreenshot;
+
+ @Inject
+ public LongScreenshotHolder() {
+ mLongScreenshot = new AtomicReference<>();
+ }
+
+ /**
+ * Set the holder's stored LongScreenshot.
+ */
+ public void setLongScreenshot(ScrollCaptureController.LongScreenshot longScreenshot) {
+ mLongScreenshot.set(longScreenshot);
+ }
+
+ /**
+ * @return true if the holder has a non-null LongScreenshot.
+ */
+ public boolean hasLongScreenshot() {
+ return mLongScreenshot.get() != null;
+ }
+
+ /**
+ * Return the current stored LongScreenshot, clear the holder's storage.
+ */
+ public ScrollCaptureController.LongScreenshot takeLongScreenshot() {
+ return mLongScreenshot.getAndSet(null);
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index badffce..9d01986 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -191,6 +191,8 @@
private final ScrollCaptureClient mScrollCaptureClient;
private final PhoneWindow mWindow;
private final DisplayManager mDisplayManager;
+ private final ScrollCaptureController mScrollCaptureController;
+ private final LongScreenshotHolder mLongScreenshotHolder;
private ScreenshotView mScreenshotView;
private Bitmap mScreenBitmap;
@@ -233,13 +235,17 @@
ScrollCaptureClient scrollCaptureClient,
UiEventLogger uiEventLogger,
ImageExporter imageExporter,
- @Main Executor mainExecutor) {
+ @Main Executor mainExecutor,
+ ScrollCaptureController scrollCaptureController,
+ LongScreenshotHolder longScreenshotHolder) {
mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
mScrollCaptureClient = scrollCaptureClient;
mUiEventLogger = uiEventLogger;
mImageExporter = imageExporter;
mMainExecutor = mainExecutor;
+ mScrollCaptureController = scrollCaptureController;
+ mLongScreenshotHolder = longScreenshotHolder;
mBgExecutor = Executors.newSingleThreadExecutor();
mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
@@ -569,15 +575,30 @@
}
Log.d(TAG, "ScrollCapture: connected to window ["
+ mLastScrollCaptureResponse.getWindowTitle() + "]");
- final Intent intent = new Intent(mContext, LongScreenshotActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.putExtra(LongScreenshotActivity.EXTRA_CAPTURE_RESPONSE,
- mLastScrollCaptureResponse);
+
+ final ScrollCaptureResponse response = mLastScrollCaptureResponse;
mScreenshotView.showScrollChip(/* onClick */ () -> {
// Clear the reference to prevent close() in dismissScreenshot
mLastScrollCaptureResponse = null;
- mContext.startActivity(intent);
- dismissScreenshot(false);
+ final ListenableFuture<ScrollCaptureController.LongScreenshot> future =
+ mScrollCaptureController.run(response);
+ future.addListener(() -> {
+ ScrollCaptureController.LongScreenshot longScreenshot;
+ try {
+ longScreenshot = future.get();
+ } catch (CancellationException | InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Exception", e);
+ return;
+ }
+
+ mLongScreenshotHolder.setLongScreenshot(longScreenshot);
+
+ final Intent intent = new Intent(mContext, LongScreenshotActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mContext.startActivity(intent);
+
+ dismissScreenshot(false);
+ }, mMainExecutor);
});
} catch (CancellationException e) {
// Ignore
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
index 29f67f3..6ebab8a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
@@ -57,6 +57,13 @@
ERROR,
TIMEOUT
}
+
+ /* Enum to define screenshot smart action types. */
+ public enum ScreenshotSmartActionType {
+ REGULAR_SMART_ACTIONS,
+ QUICK_SHARE_ACTION
+ }
+
/**
* Default implementation that returns an empty list.
* This method is overridden in vendor-specific Sys UI implementation.
@@ -68,7 +75,8 @@
* @param userHandle user handle of the foreground task owner
*/
public CompletableFuture<List<Notification.Action>> getActions(String screenshotId,
- Uri screenshotUri, Bitmap bitmap, ComponentName componentName, UserHandle userHandle) {
+ Uri screenshotUri, Bitmap bitmap, ComponentName componentName,
+ ScreenshotSmartActionType actionType, UserHandle userHandle) {
if (DEBUG_ACTIONS) {
Log.d(TAG, "Returning empty smart action list.");
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
index 1184dc7..9bd7923 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
@@ -20,6 +20,7 @@
import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS;
import static com.android.systemui.screenshot.LogConfig.logTag;
+import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType;
import android.app.ActivityManager;
import android.app.Notification;
@@ -87,8 +88,8 @@
(runningTask != null && runningTask.topActivity != null)
? runningTask.topActivity
: new ComponentName("", "");
- smartActionsFuture = smartActionsProvider.getActions(
- screenshotId, screenshotUri, image, componentName, userHandle);
+ smartActionsFuture = smartActionsProvider.getActions(screenshotId, screenshotUri, image,
+ componentName, ScreenshotSmartActionType.REGULAR_SMART_ACTIONS, userHandle);
} catch (Throwable e) {
long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList());
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index dbd6758..b60fd13 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -48,7 +48,7 @@
super(context, attrs);
}
- // Inflated from quick_settings_brightness_dialog or quick_settings_brightness_dialog_thick
+ // Inflated from quick_settings_brightness_dialog
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index ec3a857..17b489c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -53,11 +53,6 @@
return mFlagReader.isEnabled(R.bool.flag_notification_twocolumn);
}
- // Does not support runtime changes
- public boolean isQSLabelsEnabled() {
- return mFlagReader.isEnabled(R.bool.flag_qs_labels);
- }
-
public boolean isKeyguardLayoutEnabled() {
return mFlagReader.isEnabled(R.bool.flag_keyguard_layout);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 23e6a9f..0e56ab7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -169,8 +169,6 @@
return mCallback.handleRemoteViewClick(view, pendingIntent,
action == null ? false : action.isAuthenticationRequired(), () -> {
Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
- options.second.setLaunchWindowingMode(
- WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent);
return RemoteViews.startPendingIntent(view, pendingIntent, options);
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index f57fd21..f4266a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -41,6 +41,7 @@
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
@@ -82,6 +83,9 @@
private Rect mClipRect = new Rect();
private int mCutoutHeight;
private int mGapHeight;
+ private int mIndexOfFirstViewInShelf = -1;
+ private int mIndexOfFirstViewInOverflowingSection = -1;
+
private NotificationShelfController mController;
public NotificationShelf(Context context, AttributeSet attrs) {
@@ -159,30 +163,49 @@
}
/** Update the state of the shelf. */
- public void updateState(AmbientState ambientState) {
+ public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
ExpandableView lastView = ambientState.getLastVisibleBackgroundChild();
ShelfState viewState = (ShelfState) getViewState();
if (mShowNotificationShelf && lastView != null) {
- float maxShelfEnd = ambientState.getInnerHeight() + ambientState.getTopPadding()
- + ambientState.getStackTranslation();
ExpandableViewState lastViewState = lastView.getViewState();
- float viewEnd = lastViewState.yTranslation + lastViewState.height;
viewState.copyFrom(lastViewState);
+
viewState.height = getIntrinsicHeight();
- viewState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - viewState.height,
- getFullyClosedTranslation());
viewState.zTranslation = ambientState.getBaseZHeight();
viewState.clipTopAmount = 0;
viewState.alpha = 1f - ambientState.getHideAmount();
viewState.belowSpeedBump = mHostLayoutController.getSpeedBumpIndex() == 0;
viewState.hideSensitive = false;
viewState.xTranslation = getTranslationX();
+ viewState.hasItemsInStableShelf = lastViewState.inShelf;
+ viewState.firstViewInShelf = algorithmState.firstViewInShelf;
+ viewState.firstViewInOverflowSection = algorithmState.firstViewInOverflowSection;
if (mNotGoneIndex != -1) {
viewState.notGoneIndex = Math.min(viewState.notGoneIndex, mNotGoneIndex);
}
- viewState.hasItemsInStableShelf = lastViewState.inShelf;
+
viewState.hidden = !mAmbientState.isShadeExpanded()
- || mAmbientState.isQsCustomizerShowing();
+ || mAmbientState.isQsCustomizerShowing()
+ || algorithmState.firstViewInShelf == null;
+
+ final int indexOfFirstViewInShelf = algorithmState.visibleChildren.indexOf(
+ algorithmState.firstViewInShelf);
+
+ if (mAmbientState.isExpansionChanging()
+ && algorithmState.firstViewInShelf != null
+ && indexOfFirstViewInShelf > 0) {
+
+ // Show shelf if section before it is showing.
+ final ExpandableView viewBeforeShelf = algorithmState.visibleChildren.get(
+ indexOfFirstViewInShelf - 1);
+ if (viewBeforeShelf.getViewState().hidden) {
+ viewState.hidden = true;
+ }
+ }
+
+ final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
+ viewState.yTranslation = stackEnd - viewState.height;
} else {
viewState.hidden = true;
viewState.location = ExpandableViewState.LOCATION_GONE;
@@ -199,13 +222,11 @@
if (!mShowNotificationShelf) {
return;
}
-
mShelfIcons.resetViewStates();
float shelfStart = getTranslationY();
float numViewsInShelf = 0.0f;
View lastChild = mAmbientState.getLastVisibleBackgroundChild();
mNotGoneIndex = -1;
- float interpolationStart = mMaxLayoutHeight - getIntrinsicHeight() * 2;
// find the first view that doesn't overlap with the shelf
int notGoneIndex = 0;
int colorOfViewBeforeLast = NO_COLOR;
@@ -219,7 +240,7 @@
float currentScrollVelocity = mAmbientState.getCurrentScrollVelocity();
boolean scrollingFast = currentScrollVelocity > mScrollFastThreshold
|| (mAmbientState.isExpansionChanging()
- && Math.abs(mAmbientState.getExpandingVelocity()) > mScrollFastThreshold);
+ && Math.abs(mAmbientState.getExpandingVelocity()) > mScrollFastThreshold);
boolean expandingAnimated = mAmbientState.isExpansionChanging()
&& !mAmbientState.isPanelTracking();
int baseZHeight = mAmbientState.getBaseZHeight();
@@ -233,22 +254,37 @@
if (!child.needsClippingToShelf() || child.getVisibility() == GONE) {
continue;
}
-
float notificationClipEnd;
boolean aboveShelf = ViewState.getFinalTranslationZ(child) > baseZHeight
|| child.isPinned();
boolean isLastChild = child == lastChild;
float rowTranslationY = child.getTranslationY();
+
+ final float inShelfAmount = updateShelfTransformation(i, child, scrollingFast,
+ expandingAnimated, isLastChild);
+
+ final float stackEnd = mAmbientState.getStackY()
+ + mAmbientState.getStackHeight();
+ // TODO(b/172289889) scale mPaddingBetweenElements with expansion amount
if ((isLastChild && !child.isInShelf()) || aboveShelf || backgroundForceHidden) {
- notificationClipEnd = shelfStart + getIntrinsicHeight();
+ notificationClipEnd = stackEnd;
+ } else if (mAmbientState.isExpansionChanging()) {
+ if (mIndexOfFirstViewInOverflowingSection != -1
+ && i >= mIndexOfFirstViewInOverflowingSection) {
+ // Clip notifications in (section overflowing into shelf) to shelf start.
+ notificationClipEnd = shelfStart - mPaddingBetweenElements;
+ } else {
+ // Clip notifications before the section overflowing into shelf
+ // to stackEnd because we do not show the shelf if the section right before the
+ // shelf is still hidden.
+ notificationClipEnd = stackEnd;
+ }
} else {
notificationClipEnd = shelfStart - mPaddingBetweenElements;
}
int clipTop = updateNotificationClipHeight(child, notificationClipEnd, notGoneIndex);
clipTopAmount = Math.max(clipTop, clipTopAmount);
- final float inShelfAmount = updateShelfTransformation(child, scrollingFast,
- expandingAnimated, isLastChild);
// If the current row is an ExpandableNotificationRow, update its color, roundedness,
// and icon state.
if (child instanceof ExpandableNotificationRow) {
@@ -314,19 +350,23 @@
distanceToGapTop / mGapHeight);
previousAnv.setBottomRoundness(firstElementRoundness,
false /* don't animate */);
- backgroundTop = (int) distanceToGapBottom;
}
}
previousAnv = anv;
}
}
+
clipTransientViews();
setClipTopAmount(clipTopAmount);
- boolean isHidden = getViewState().hidden || clipTopAmount >= getIntrinsicHeight();
- if (mShowNotificationShelf) {
- setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE);
- }
+
+ boolean isHidden = getViewState().hidden
+ || clipTopAmount >= getIntrinsicHeight()
+ || !mShowNotificationShelf
+ || numViewsInShelf < 1f;
+
+ // TODO(b/172289889) transition last icon in shelf to notification icon and vice versa.
+ setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE);
setBackgroundTop(backgroundTop);
setFirstElementRoundness(firstElementRoundness);
mShelfIcons.setSpeedBumpIndex(mHostLayoutController.getSpeedBumpIndex());
@@ -339,11 +379,10 @@
continue;
}
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- updateIconClipAmount(row);
updateContinuousClipping(row);
}
- boolean hideBackground = numViewsInShelf < 1.0f;
- setHideBackground(hideBackground || backgroundForceHidden);
+ boolean hideBackground = isHidden;
+ setHideBackground(hideBackground);
if (mNotGoneIndex == -1) {
mNotGoneIndex = notGoneIndex;
}
@@ -476,10 +515,10 @@
/**
* @return the amount how much this notification is in the shelf
*/
- private float updateShelfTransformation(ExpandableView view, boolean scrollingFast,
+ private float updateShelfTransformation(int i, ExpandableView view, boolean scrollingFast,
boolean expandingAnimated, boolean isLastChild) {
- // Let calculate how much the view is in the shelf
+ // Let's calculate how much the view is in the shelf
float viewStart = view.getTranslationY();
int fullHeight = view.getActualHeight() + mPaddingBetweenElements;
float iconTransformStart = calculateIconTransformationStart(view);
@@ -496,15 +535,21 @@
transformDistance,
view.getMinHeight() - getIntrinsicHeight());
}
+
float viewEnd = viewStart + fullHeight;
float fullTransitionAmount = 0.0f;
float iconTransitionAmount = 0.0f;
float shelfStart = getTranslationY();
-
- if (viewEnd >= shelfStart
+ if (mAmbientState.isExpansionChanging() && !mAmbientState.isOnKeyguard()) {
+ // TODO(b/172289889) handle icon placement for notification that is clipped by the shelf
+ if (mIndexOfFirstViewInShelf != -1 && i >= mIndexOfFirstViewInShelf) {
+ fullTransitionAmount = 1f;
+ iconTransitionAmount = 1f;
+ }
+ } else if (viewEnd >= shelfStart
&& (!mAmbientState.isUnlockHintRunning() || view.isInShelf())
&& (mAmbientState.isShadeExpanded()
- || (!view.isPinned() && !view.isHeadsUpAnimatingAway()))) {
+ || (!view.isPinned() && !view.isHeadsUpAnimatingAway()))) {
if (viewStart < shelfStart) {
float fullAmount = (shelfStart - viewStart) / fullHeight;
@@ -572,7 +617,7 @@
&& !mNoAnimationsInThisFrame;
}
iconState.clampedAppearAmount = clampedAmount;
- setIconTransformationAmount(view, transitionAmount, isLastChild);
+ setIconTransformationAmount(view, transitionAmount);
}
private boolean isTargetClipped(ExpandableView view) {
@@ -585,12 +630,10 @@
+ view.getContentTranslation()
+ view.getRelativeTopPadding(target)
+ target.getHeight();
-
return endOfTarget >= getTranslationY() - mPaddingBetweenElements;
}
- private void setIconTransformationAmount(ExpandableView view, float transitionAmount,
- boolean isLastChild) {
+ private void setIconTransformationAmount(ExpandableView view, float transitionAmount) {
if (!(view instanceof ExpandableNotificationRow)) {
return;
}
@@ -601,7 +644,6 @@
return;
}
iconState.alpha = transitionAmount;
-
boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
iconState.hidden = isAppearing
|| (view instanceof ExpandableNotificationRow
@@ -610,8 +652,8 @@
|| (transitionAmount == 0.0f && !iconState.isAnimating(icon))
|| row.isAboveShelf()
|| row.showingPulsing()
- || (!row.isInShelf() && isLastChild)
|| row.getTranslationZ() > mAmbientState.getBaseZHeight();
+
iconState.iconAppearAmount = iconState.hidden? 0f : transitionAmount;
// Fade in icons at shelf start
@@ -790,8 +832,19 @@
mController = notificationShelfController;
}
+ public void setIndexOfFirstViewInShelf(ExpandableView firstViewInShelf) {
+ mIndexOfFirstViewInShelf = mHostLayoutController.indexOfChild(firstViewInShelf);
+ }
+
+ public void setFirstViewInOverflowingSection(ExpandableView firstViewInOverflowingSection) {
+ mIndexOfFirstViewInOverflowingSection =
+ mHostLayoutController.indexOfChild(firstViewInOverflowingSection);
+ }
+
private class ShelfState extends ExpandableViewState {
private boolean hasItemsInStableShelf;
+ private ExpandableView firstViewInShelf;
+ private ExpandableView firstViewInOverflowSection;
@Override
public void applyToView(View view) {
@@ -800,6 +853,8 @@
}
super.applyToView(view);
+ setIndexOfFirstViewInShelf(firstViewInShelf);
+ setFirstViewInOverflowingSection(firstViewInOverflowSection);
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
@@ -812,6 +867,8 @@
}
super.animateTo(child, properties);
+ setIndexOfFirstViewInShelf(firstViewInShelf);
+ setFirstViewInOverflowingSection(firstViewInOverflowSection);
updateAppearance();
setHasItemsInStableShelf(hasItemsInStableShelf);
mShelfIcons.setAnimationsEnabled(mAnimationsEnabled);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
index 1e935c1..4f70fdb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
@@ -22,6 +22,7 @@
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.phone.StatusBarNotificationPresenter;
@@ -103,9 +104,10 @@
return mView.getHeight();
}
- public void updateState(AmbientState ambientState) {
+ public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
mAmbientState = ambientState;
- mView.updateState(ambientState);
+ mView.updateState(algorithmState, ambientState);
}
public int getIntrinsicHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index c811fdd..a537299 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -25,12 +25,14 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.DimenRes;
+import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import com.android.internal.annotations.GuardedBy;
@@ -66,6 +68,8 @@
private Executor mChangeRunnableExecutor;
private Executor mExecutor;
private Looper mExecutorLooper;
+ @Nullable
+ private Rect mDrawableBounds;
public ScrimView(Context context) {
this(context, null);
@@ -125,7 +129,9 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- if (changed) {
+ if (mDrawableBounds != null) {
+ mDrawable.setBounds(mDrawableBounds);
+ } else if (changed) {
mDrawable.setBounds(left, top, right, bottom);
invalidate();
}
@@ -288,4 +294,15 @@
((ScrimDrawable) mDrawable).setRoundedCorners(radius);
}
}
+
+ /**
+ * Set bounds for the view, all coordinates are absolute
+ */
+ public void setDrawableBounds(float left, float top, float right, float bottom) {
+ if (mDrawableBounds == null) {
+ mDrawableBounds = new Rect();
+ }
+ mDrawableBounds.set((int) left, (int) top, (int) right, (int) bottom);
+ mDrawable.setBounds(mDrawableBounds);
+ }
}
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 20383fe..fad7480 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -24,6 +24,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
@@ -237,9 +238,11 @@
static OngoingCallController provideOngoingCallController(
CommonNotifCollection notifCollection,
FeatureFlags featureFlags,
- SystemClock systemClock) {
+ SystemClock systemClock,
+ ActivityStarter activityStarter) {
OngoingCallController ongoingCallController =
- new OngoingCallController(notifCollection, featureFlags, systemClock);
+ new OngoingCallController(
+ notifCollection, featureFlags, systemClock, activityStarter);
ongoingCallController.init();
return ongoingCallController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
new file mode 100644
index 0000000..5ab71bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -0,0 +1,352 @@
+/*
+ * 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.events
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ObjectAnimator
+import android.annotation.UiThread
+import android.util.Log
+import android.view.Gravity
+import android.view.View
+import android.widget.FrameLayout
+
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+
+import java.lang.IllegalStateException
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Understands how to keep the persistent privacy dot in the corner of the screen in
+ * ScreenDecorations, which does not rotate with the device.
+ *
+ * The basic principle here is that each dot will sit in a box that is equal to the margins of the
+ * status bar (specifically the status_bar_contents view in PhoneStatusBarView). Each dot container
+ * will have its gravity set towards the corner (i.e., top-right corner gets top|right gravity), and
+ * the contained ImageView will be set to center_vertical and away from the corner horizontally. The
+ * Views will match the status bar top padding and status bar height so that the dot can appear to
+ * reside directly after the status bar system contents (basically to the right of the battery).
+ *
+ * NOTE: any operation that modifies views directly must run on the provided executor, because
+ * these views are owned by ScreenDecorations and it runs in its own thread
+ */
+
+@SysUISingleton
+class PrivacyDotViewController @Inject constructor(
+ @Main val mainExecutor: Executor,
+ val animationScheduler: SystemStatusAnimationScheduler
+) {
+ private var rotation = 0
+ private var leftSize = 0
+ private var rightSize = 0
+
+ private var sbHeightPortrait = 0
+ private var sbHeightLandscape = 0
+
+ private var hasMultipleHeights = false
+ private var needsHeightUpdate = false
+ private var needsRotationUpdate = false
+ private var needsMarginUpdate = false
+
+ private lateinit var tl: View
+ private lateinit var tr: View
+ private lateinit var bl: View
+ private lateinit var br: View
+
+ // Track which corner is active (based on orientation + RTL)
+ private var designatedCorner: View? = null
+
+ // Privacy dots are created in ScreenDecoration's UiThread, which is not the main thread
+ private var uiExecutor: Executor? = null
+
+ private val views: Sequence<View>
+ get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
+
+ fun setUiExecutor(e: Executor) {
+ uiExecutor = e
+ }
+
+ @UiThread
+ fun updateRotation(rot: Int) {
+ if (rot == rotation) {
+ return
+ }
+
+ // A rotation has started, hide the views to avoid flicker
+ setCornerVisibilities(View.INVISIBLE)
+
+ if (hasMultipleHeights && (rotation % 2) != (rot % 2)) {
+ // we've changed from vertical to horizontal; update status bar height
+ needsHeightUpdate = true
+ }
+
+ rotation = rot
+ needsRotationUpdate = true
+ }
+
+ @UiThread
+ private fun updateHeights(rot: Int) {
+ val height = when (rot) {
+ 0, 2 -> sbHeightPortrait
+ 1, 3 -> sbHeightLandscape
+ else -> 0
+ }
+
+ views.forEach { it.layoutParams.height = height }
+ }
+
+ // Update the gravity and margins of the privacy views
+ @UiThread
+ private fun updateRotations() {
+ // To keep a view in the corner, its gravity is always the description of its current corner
+ // Therefore, just figure out which view is in which corner. This turns out to be something
+ // like (myCorner - rot) mod 4, where topLeft = 0, topRight = 1, etc. and portrait = 0, and
+ // rotating the device counter-clockwise increments rotation by 1
+
+ views.forEach { corner ->
+ val rotatedCorner = rotatedCorner(cornerForView(corner))
+ (corner.layoutParams as FrameLayout.LayoutParams).apply {
+ gravity = rotatedCorner.toGravity()
+ }
+
+ // Set the dot's view gravity to hug the status bar
+ (corner.findViewById<View>(R.id.privacy_dot)
+ .layoutParams as FrameLayout.LayoutParams)
+ .gravity = rotatedCorner.innerGravity()
+ }
+ }
+
+ @UiThread
+ private fun updateCornerSizes() {
+ views.forEach { corner ->
+ val rotatedCorner = rotatedCorner(cornerForView(corner))
+ val w = widthForCorner(rotatedCorner)
+ Log.d(TAG, "updateCornerSizes: setting (${cornerForView(corner)}) to $w")
+ (corner.layoutParams as FrameLayout.LayoutParams).width = w
+ corner.requestLayout()
+ }
+ }
+
+ // Designated view will be the one at statusbar's view.END
+ @UiThread
+ private fun selectDesignatedCorner(): View? {
+ if (!this::tl.isInitialized) {
+ return null
+ }
+
+ val isRtl = tl.isLayoutRtl
+
+ return when (rotation) {
+ 0 -> if (isRtl) tl else tr
+ 1 -> if (isRtl) tr else br
+ 2 -> if (isRtl) br else bl
+ 3 -> if (isRtl) bl else tl
+ else -> throw IllegalStateException("unknown rotation")
+ }
+ }
+
+ // Track the current designated corner and maybe animate to a new rotation
+ @UiThread
+ private fun updateDesignatedCorner(newCorner: View) {
+ designatedCorner = newCorner
+
+ if (animationScheduler.hasPersistentDot) {
+ designatedCorner!!.visibility = View.VISIBLE
+ designatedCorner!!.alpha = 0f
+ designatedCorner!!.animate()
+ .alpha(1.0f)
+ .setDuration(300)
+ .start()
+ }
+ }
+
+ @UiThread
+ private fun setCornerVisibilities(vis: Int) {
+ views.forEach { corner ->
+ corner.visibility = vis
+ }
+ }
+
+ private fun cornerForView(v: View): Int {
+ return when (v) {
+ tl -> TOP_LEFT
+ tr -> TOP_RIGHT
+ bl -> BOTTOM_LEFT
+ br -> BOTTOM_RIGHT
+ else -> throw IllegalArgumentException("not a corner view")
+ }
+ }
+
+ private fun rotatedCorner(corner: Int): Int {
+ var modded = corner - rotation
+ if (modded < 0) {
+ modded += 4
+ }
+
+ return modded
+ }
+
+ private fun widthForCorner(corner: Int): Int {
+ return when (corner) {
+ TOP_LEFT, BOTTOM_LEFT -> leftSize
+ TOP_RIGHT, BOTTOM_RIGHT -> rightSize
+ else -> throw IllegalArgumentException("Unknown corner")
+ }
+ }
+
+ fun initialize(topLeft: View, topRight: View, bottomLeft: View, bottomRight: View) {
+ if (this::tl.isInitialized && this::tr.isInitialized &&
+ this::bl.isInitialized && this::br.isInitialized) {
+ if (tl == topLeft && tr == topRight && bl == bottomLeft && br == bottomRight) {
+ return
+ }
+ }
+
+ tl = topLeft
+ tr = topRight
+ bl = bottomLeft
+ br = bottomRight
+
+ designatedCorner = selectDesignatedCorner()
+ mainExecutor.execute {
+ animationScheduler.addCallback(systemStatusAnimationCallback)
+ }
+ }
+
+ /**
+ * Set the status bar height in portrait and landscape, in pixels. If they are the same you can
+ * pass the same value twice
+ */
+ fun setStatusBarHeights(portrait: Int, landscape: Int) {
+ sbHeightPortrait = portrait
+ sbHeightLandscape = landscape
+
+ hasMultipleHeights = portrait != landscape
+ }
+
+ /**
+ * The dot view containers will fill the margin in order to position the dots correctly
+ *
+ * @param left the space between the status bar contents and the left side of the screen
+ * @param right space between the status bar contents and the right side of the screen
+ */
+ fun setStatusBarMargins(left: Int, right: Int) {
+ leftSize = left
+ rightSize = right
+
+ needsMarginUpdate = true
+
+ // Margins come after PhoneStatusBarView does a layout pass, and so will always happen
+ // after rotation changes. It is safe to execute the updates from here
+ uiExecutor?.execute {
+ doUpdates(needsRotationUpdate, needsHeightUpdate, needsMarginUpdate)
+ }
+ }
+
+ private fun doUpdates(rot: Boolean, height: Boolean, width: Boolean) {
+ var newDesignatedCorner: View? = null
+
+ if (rot) {
+ needsRotationUpdate = false
+ updateRotations()
+ newDesignatedCorner = selectDesignatedCorner()
+ }
+
+ if (height) {
+ needsHeightUpdate = false
+ updateHeights(rotation)
+ }
+
+ if (width) {
+ needsMarginUpdate = false
+ updateCornerSizes()
+ }
+
+ if (newDesignatedCorner != null && newDesignatedCorner != designatedCorner) {
+ updateDesignatedCorner(newDesignatedCorner)
+ }
+ }
+
+ private val systemStatusAnimationCallback: SystemStatusAnimationCallback =
+ object : SystemStatusAnimationCallback {
+ override fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? {
+ if (designatedCorner == null) {
+ return null
+ }
+
+ val alpha = ObjectAnimator.ofFloat(
+ designatedCorner, "alpha", 0f, 1f)
+ alpha.duration = DURATION
+ alpha.interpolator = Interpolators.ALPHA_OUT
+ alpha.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animator: Animator) {
+ uiExecutor?.execute { designatedCorner?.visibility = View.VISIBLE }
+ }
+ })
+ return alpha
+ }
+
+ override fun onHidePersistentDot(): Animator? {
+ if (designatedCorner == null) {
+ return null
+ }
+
+ val alpha = ObjectAnimator.ofFloat(
+ designatedCorner, "alpha", 1f, 0f)
+ alpha.duration = DURATION
+ alpha.interpolator = Interpolators.ALPHA_OUT
+ alpha.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animator: Animator) {
+ uiExecutor?.execute { designatedCorner?.visibility = View.INVISIBLE }
+ }
+ })
+ alpha.start()
+ return null
+ }
+ }
+}
+
+const val TOP_LEFT = 0
+const val TOP_RIGHT = 1
+const val BOTTOM_RIGHT = 2
+const val BOTTOM_LEFT = 3
+private const val DURATION = 160L
+private const val TAG = "PrivacyDotViewController"
+
+private fun Int.toGravity(): Int {
+ return when (this) {
+ TOP_LEFT -> Gravity.TOP or Gravity.LEFT
+ TOP_RIGHT -> Gravity.TOP or Gravity.RIGHT
+ BOTTOM_LEFT -> Gravity.BOTTOM or Gravity.LEFT
+ BOTTOM_RIGHT -> Gravity.BOTTOM or Gravity.RIGHT
+ else -> throw IllegalArgumentException("Not a corner")
+ }
+}
+
+private fun Int.innerGravity(): Int {
+ return when (this) {
+ TOP_LEFT -> Gravity.CENTER_VERTICAL or Gravity.RIGHT
+ TOP_RIGHT -> Gravity.CENTER_VERTICAL or Gravity.LEFT
+ BOTTOM_LEFT -> Gravity.CENTER_VERTICAL or Gravity.RIGHT
+ BOTTOM_RIGHT -> Gravity.CENTER_VERTICAL or Gravity.LEFT
+ else -> throw IllegalArgumentException("Not a corner")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
new file mode 100644
index 0000000..6380dc0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.events
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.ImageView
+import com.android.settingslib.graph.ThemedBatteryDrawable
+import com.android.systemui.R
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyItem
+
+interface StatusEvent {
+ val priority: Int
+ // Whether or not to force the status bar open and show a dot
+ val forceVisible: Boolean
+ val viewCreator: (context: Context) -> View
+}
+
+class BatteryEvent : StatusEvent {
+ override val priority = 50
+ override val forceVisible = false
+
+ override val viewCreator: (context: Context) -> View = { context ->
+ val iv = ImageView(context)
+ iv.setImageDrawable(ThemedBatteryDrawable(context, Color.WHITE))
+ iv.setBackgroundDrawable(ColorDrawable(Color.GREEN))
+ iv
+ }
+
+ override fun toString(): String {
+ return javaClass.simpleName
+ }
+}
+class PrivacyEvent : StatusEvent {
+ override val priority = 100
+ override val forceVisible = true
+ var privacyItems: List<PrivacyItem> = listOf()
+
+ override val viewCreator: (context: Context) -> View = { context ->
+ val v = LayoutInflater.from(context)
+ .inflate(R.layout.ongoing_privacy_chip, null) as OngoingPrivacyChip
+ v.privacyList = privacyItems
+ v
+ }
+
+ override fun toString(): String {
+ return javaClass.simpleName
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
new file mode 100644
index 0000000..6209630
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -0,0 +1,137 @@
+/*
+ * 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.events
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.widget.FrameLayout
+
+import com.android.systemui.R
+import com.android.systemui.statusbar.SuperStatusBarViewFactory
+import com.android.systemui.statusbar.phone.StatusBarWindowController
+import com.android.systemui.statusbar.phone.StatusBarWindowView
+
+import javax.inject.Inject
+
+/**
+ * //TODO: this _probably_ doesn't control a window anymore
+ * Controls the window for system event animations.
+ */
+class SystemEventChipAnimationController @Inject constructor(
+ private val context: Context,
+ private val statusBarViewFactory: SuperStatusBarViewFactory,
+ private val statusBarWindowController: StatusBarWindowController
+) : SystemStatusChipAnimationCallback {
+ var showPersistentDot = false
+ set(value) {
+ field = value
+ statusBarWindowController.setForceStatusBarVisible(value)
+ maybeUpdateShowDot()
+ }
+
+ private lateinit var animationWindowView: FrameLayout
+ private lateinit var animationDotView: View
+ private lateinit var statusBarWindowView: StatusBarWindowView
+ private var currentAnimatedView: View? = null
+
+ // TODO: move to dagger
+ private var initialized = false
+
+ override fun onChipAnimationStart(
+ viewCreator: (context: Context) -> View,
+ @SystemAnimationState state: Int
+ ) {
+ if (!initialized) init()
+
+ if (state == ANIMATING_IN) {
+ currentAnimatedView = viewCreator(context)
+ animationWindowView.addView(currentAnimatedView, layoutParamsDefault)
+
+ // We are animating IN; chip comes in from View.END
+ currentAnimatedView?.apply {
+ translationX = width.toFloat()
+ alpha = 0f
+ visibility = View.VISIBLE
+ }
+ } else {
+ // We are animating away
+ currentAnimatedView?.apply {
+ translationX = 0f
+ alpha = 1f
+ }
+ }
+ }
+
+ override fun onChipAnimationEnd(@SystemAnimationState state: Int) {
+ if (state == ANIMATING_IN) {
+ // Finished animating in
+ currentAnimatedView?.apply {
+ translationX = 0f
+ alpha = 1f
+ }
+ } else {
+ // Finished animating away
+ currentAnimatedView?.apply {
+ visibility = View.INVISIBLE
+ }
+ animationWindowView.removeView(currentAnimatedView)
+ }
+ }
+
+ override fun onChipAnimationUpdate(
+ animator: ValueAnimator,
+ @SystemAnimationState state: Int
+ ) {
+ // Alpha is parameterized 0,1, and translation from (width, 0)
+ currentAnimatedView?.apply {
+ val amt = animator.animatedValue as Float
+
+ alpha = amt
+
+ val w = width
+ val translation = (1 - amt) * w
+ translationX = translation
+ }
+ }
+
+ private fun maybeUpdateShowDot() {
+ if (!initialized) return
+ if (!showPersistentDot && currentAnimatedView == null) {
+ animationDotView.visibility = View.INVISIBLE
+ }
+ }
+
+ private fun init() {
+ initialized = true
+ statusBarWindowView = statusBarViewFactory.statusBarWindowView
+ animationWindowView = LayoutInflater.from(context)
+ .inflate(R.layout.system_event_animation_window, null) as FrameLayout
+ animationDotView = animationWindowView.findViewById(R.id.dot_view)
+ val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
+ lp.gravity = Gravity.END or Gravity.CENTER_VERTICAL
+ statusBarWindowView.addView(animationWindowView, lp)
+ }
+
+ private val layoutParamsDefault = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).also {
+ it.gravity = Gravity.END or Gravity.CENTER_VERTICAL
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
new file mode 100644
index 0000000..b481823
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.events
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.privacy.PrivacyItemController
+import com.android.systemui.statusbar.policy.BatteryController
+import javax.inject.Inject
+
+/**
+ * Listens for system events (battery, privacy, connectivity) and allows listeners
+ * to show status bar animations when they happen
+ */
+@SysUISingleton
+class SystemEventCoordinator @Inject constructor(
+ private val batteryController: BatteryController,
+ private val privacyController: PrivacyItemController
+) {
+ private lateinit var scheduler: SystemStatusAnimationScheduler
+
+ fun startObserving() {
+ /* currently unused
+ batteryController.addCallback(batteryStateListener)
+ */
+ privacyController.addCallback(privacyStateListener)
+ }
+
+ fun stopObserving() {
+ /* currently unused
+ batteryController.removeCallback(batteryStateListener)
+ */
+ privacyController.removeCallback(privacyStateListener)
+ }
+
+ fun attachScheduler(s: SystemStatusAnimationScheduler) {
+ this.scheduler = s
+ }
+
+ fun notifyPluggedIn() {
+ scheduler.onStatusEvent(BatteryEvent())
+ }
+
+ fun notifyPrivacyItemsEmpty() {
+ scheduler.setShouldShowPersistentPrivacyIndicator(false)
+ }
+
+ fun notifyPrivacyItemsChanged() {
+ val event = PrivacyEvent()
+ event.privacyItems = privacyStateListener.currentPrivacyItems
+ scheduler.onStatusEvent(event)
+ }
+
+ private val batteryStateListener = object : BatteryController.BatteryStateChangeCallback {
+ var plugged = false
+ var stateKnown = false
+ override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
+ if (!stateKnown) {
+ stateKnown = true
+ plugged = pluggedIn
+ notifyListeners()
+ return
+ }
+
+ if (plugged != pluggedIn) {
+ plugged = pluggedIn
+ notifyListeners()
+ }
+ }
+
+ private fun notifyListeners() {
+ // We only care about the plugged in status
+ if (plugged) notifyPluggedIn()
+ }
+ }
+
+ private val privacyStateListener = object : PrivacyItemController.Callback {
+ var currentPrivacyItems = listOf<PrivacyItem>()
+
+ override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
+ if (privacyItems.isNotEmpty() && currentPrivacyItems.containsAll(privacyItems)) {
+ return
+ }
+ currentPrivacyItems = privacyItems
+ notifyListeners()
+ }
+
+ private fun notifyListeners() {
+ if (currentPrivacyItems.isEmpty()) {
+ notifyPrivacyItemsEmpty()
+ } else {
+ notifyPrivacyItemsChanged()
+ }
+ }
+ }
+}
+
+private const val TAG = "SystemEventCoordinator"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
new file mode 100644
index 0000000..b85d031
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -0,0 +1,333 @@
+/*
+ * 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.events
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.annotation.IntDef
+import android.content.Context
+import android.os.Process
+import android.util.Log
+import android.view.View
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.phone.StatusBarWindowController
+import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.util.Assert
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.time.SystemClock
+
+import javax.inject.Inject
+
+/**
+ * Dead-simple scheduler for system status events. Obeys the following principles (all values TBD):
+ * - Avoiding log spam by only allowing 12 events per minute (1event/5s)
+ * - Waits 100ms to schedule any event for debouncing/prioritization
+ * - Simple prioritization: Privacy > Battery > connectivity (encoded in StatusEvent)
+ * - Only schedules a single event, and throws away lowest priority events
+ *
+ * There are 4 basic stages of animation at play here:
+ * 1. System chrome animation OUT
+ * 2. Chip animation IN
+ * 3. Chip animation OUT; potentially into a dot
+ * 4. System chrome animation IN
+ *
+ * Thus we can keep all animations synchronized with two separate ValueAnimators, one for system
+ * chrome and the other for the chip. These can animate from 0,1 and listeners can parameterize
+ * their respective views based on the progress of the animator. Interpolation differences TBD
+ */
+@SysUISingleton
+class SystemStatusAnimationScheduler @Inject constructor(
+ private val coordinator: SystemEventCoordinator,
+ private val chipAnimationController: SystemEventChipAnimationController,
+ private val statusBarWindowController: StatusBarWindowController,
+ private val systemClock: SystemClock,
+ @Main private val executor: DelayableExecutor
+) : CallbackController<SystemStatusAnimationCallback> {
+
+ /** True from the time a scheduled event starts until it's animation finishes */
+ var isActive = false
+ private set
+
+ @SystemAnimationState var animationState: Int = IDLE
+ private set
+
+ /** True if the persistent privacy dot should be active */
+ var hasPersistentDot = false
+ private set
+
+ private var scheduledEvent: StatusEvent? = null
+ private var cancelExecutionRunnable: Runnable? = null
+ private val listeners = mutableSetOf<SystemStatusAnimationCallback>()
+
+ init {
+ coordinator.attachScheduler(this)
+ }
+
+ fun onStatusEvent(event: StatusEvent) {
+ // Ignore any updates until the system is up and running
+ if (isTooEarly()) {
+ return
+ }
+
+ // Don't deal with threading for now (no need let's be honest)
+ Assert.isMainThread()
+ if (event.priority > scheduledEvent?.priority ?: -1) {
+ if (DEBUG) {
+ Log.d(TAG, "scheduling event $event")
+ }
+ scheduleEvent(event)
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "ignoring event $event")
+ }
+ }
+ }
+
+ private fun clearDotIfVisible() {
+ notifyHidePersistentDot()
+ }
+
+ fun setShouldShowPersistentPrivacyIndicator(should: Boolean) {
+ if (hasPersistentDot == should) {
+ return
+ }
+
+ hasPersistentDot = should
+
+ if (!hasPersistentDot) {
+ clearDotIfVisible()
+ }
+ }
+
+ private fun isTooEarly(): Boolean {
+ Log.d(TAG, "time=> ${systemClock.uptimeMillis() - Process.getStartUptimeMillis()}")
+ return systemClock.uptimeMillis() - Process.getStartUptimeMillis() < MIN_UPTIME
+ }
+
+ /**
+ * Clear the scheduled event (if any) and schedule a new one
+ */
+ private fun scheduleEvent(event: StatusEvent) {
+ scheduledEvent = event
+ if (scheduledEvent!!.forceVisible) {
+ hasPersistentDot = true
+ }
+
+ // Schedule the animation to start after a debounce period
+ cancelExecutionRunnable = executor.executeDelayed({
+ cancelExecutionRunnable = null
+ animationState = ANIMATING_IN
+ statusBarWindowController.setForceStatusBarVisible(true)
+
+ val entranceAnimator = ValueAnimator.ofFloat(1f, 0f)
+ entranceAnimator.duration = ENTRANCE_ANIM_LENGTH
+ entranceAnimator.addListener(systemAnimatorAdapter)
+ entranceAnimator.addUpdateListener(systemUpdateListener)
+
+ val chipAnimator = ValueAnimator.ofFloat(0f, 1f)
+ chipAnimator.duration = CHIP_ANIM_LENGTH
+ chipAnimator.addListener(
+ ChipAnimatorAdapter(RUNNING_CHIP_ANIM, scheduledEvent!!.viewCreator))
+ chipAnimator.addUpdateListener(chipUpdateListener)
+
+ val aSet2 = AnimatorSet()
+ aSet2.playSequentially(entranceAnimator, chipAnimator)
+ aSet2.start()
+
+ executor.executeDelayed({
+ animationState = ANIMATING_OUT
+
+ val systemAnimator = ValueAnimator.ofFloat(0f, 1f)
+ systemAnimator.duration = ENTRANCE_ANIM_LENGTH
+ systemAnimator.addListener(systemAnimatorAdapter)
+ systemAnimator.addUpdateListener(systemUpdateListener)
+
+ val chipAnimator = ValueAnimator.ofFloat(1f, 0f)
+ chipAnimator.duration = CHIP_ANIM_LENGTH
+ chipAnimator.addListener(ChipAnimatorAdapter(IDLE, scheduledEvent!!.viewCreator))
+ chipAnimator.addUpdateListener(chipUpdateListener)
+
+ val aSet2 = AnimatorSet()
+
+ aSet2.play(chipAnimator).before(systemAnimator)
+ if (hasPersistentDot) {
+ val dotAnim = notifyTransitionToPersistentDot()
+ if (dotAnim != null) aSet2.playTogether(systemAnimator, dotAnim)
+ }
+
+ aSet2.start()
+
+ statusBarWindowController.setForceStatusBarVisible(false)
+ scheduledEvent = null
+ }, DISPLAY_LENGTH)
+ }, DELAY)
+ }
+
+ private fun notifyTransitionToPersistentDot(): Animator? {
+ val anims: List<Animator> = listeners.mapNotNull {
+ it.onSystemStatusAnimationTransitionToPersistentDot()
+ }
+ if (anims.isNotEmpty()) {
+ val aSet = AnimatorSet()
+ aSet.playTogether(anims)
+ return aSet
+ }
+
+ return null
+ }
+
+ private fun notifyHidePersistentDot(): Animator? {
+ val anims: List<Animator> = listeners.mapNotNull {
+ it.onHidePersistentDot()
+ }
+
+ if (anims.isNotEmpty()) {
+ val aSet = AnimatorSet()
+ aSet.playTogether(anims)
+ return aSet
+ }
+
+ return null
+ }
+
+ private fun notifySystemStart() {
+ listeners.forEach { it.onSystemChromeAnimationStart() }
+ }
+
+ private fun notifySystemFinish() {
+ listeners.forEach { it.onSystemChromeAnimationEnd() }
+ }
+
+ private fun notifySystemAnimationUpdate(anim: ValueAnimator) {
+ listeners.forEach { it.onSystemChromeAnimationUpdate(anim) }
+ }
+
+ override fun addCallback(listener: SystemStatusAnimationCallback) {
+ Assert.isMainThread()
+
+ if (listeners.isEmpty()) {
+ coordinator.startObserving()
+ }
+ listeners.add(listener)
+ }
+
+ override fun removeCallback(listener: SystemStatusAnimationCallback) {
+ Assert.isMainThread()
+
+ listeners.remove(listener)
+ if (listeners.isEmpty()) {
+ coordinator.stopObserving()
+ }
+ }
+
+ private val systemUpdateListener = ValueAnimator.AnimatorUpdateListener {
+ anim -> notifySystemAnimationUpdate(anim)
+ }
+
+ private val systemAnimatorAdapter = object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(p0: Animator?) {
+ notifySystemFinish()
+ }
+
+ override fun onAnimationStart(p0: Animator?) {
+ notifySystemStart()
+ }
+ }
+
+ private val chipUpdateListener = ValueAnimator.AnimatorUpdateListener {
+ anim -> chipAnimationController.onChipAnimationUpdate(anim, animationState)
+ }
+
+ inner class ChipAnimatorAdapter(
+ @SystemAnimationState val endState: Int,
+ val viewCreator: (context: Context) -> View
+ ) : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(p0: Animator?) {
+ chipAnimationController.onChipAnimationEnd(animationState)
+ animationState = endState
+ }
+
+ override fun onAnimationStart(p0: Animator?) {
+ chipAnimationController.onChipAnimationStart(viewCreator, animationState)
+ }
+ }
+}
+
+/**
+ * The general idea here is that this scheduler will run two value animators, and provide
+ * animator-like callbacks for each kind of animation. The SystemChrome animation is expected to
+ * create space for the chip animation to display. This means hiding the system elements in the
+ * status bar and keyguard.
+ *
+ * TODO: the chip animation really only has one client, we can probably remove it from this
+ * interface
+ *
+ * The value animators themselves are simple animators from 0.0 to 1.0. Listeners can apply any
+ * interpolation they choose but realistically these are most likely to be simple alpha transitions
+ */
+interface SystemStatusAnimationCallback {
+ @JvmDefault fun onSystemChromeAnimationUpdate(animator: ValueAnimator) {}
+ @JvmDefault fun onSystemChromeAnimationStart() {}
+ @JvmDefault fun onSystemChromeAnimationEnd() {}
+
+ // Best method name, change my mind
+ @JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? { return null }
+ @JvmDefault fun onHidePersistentDot(): Animator? { return null }
+}
+
+interface SystemStatusChipAnimationCallback {
+ fun onChipAnimationUpdate(animator: ValueAnimator, @SystemAnimationState state: Int) {}
+
+ fun onChipAnimationStart(
+ viewCreator: (context: Context) -> View,
+ @SystemAnimationState state: Int
+ ) {}
+
+ fun onChipAnimationEnd(@SystemAnimationState state: Int) {}
+}
+
+/**
+ */
+@Retention(AnnotationRetention.SOURCE)
+@IntDef(
+ value = [
+ IDLE, ANIMATING_IN, RUNNING_CHIP_ANIM, ANIMATING_OUT
+ ]
+)
+annotation class SystemAnimationState
+
+/** No animation is in progress */
+const val IDLE = 0
+/** System is animating out, and chip is animating in */
+const val ANIMATING_IN = 1
+/** Chip has animated in and is awaiting exit animation, and optionally playing its own animation */
+const val RUNNING_CHIP_ANIM = 2
+/** Chip is animating away and system is animating back */
+const val ANIMATING_OUT = 3
+
+private const val TAG = "SystemStatusAnimationScheduler"
+private const val DELAY: Long = 100
+private const val DISPLAY_LENGTH = 5000L
+private const val ENTRANCE_ANIM_LENGTH = 500L
+private const val CHIP_ANIM_LENGTH = 500L
+private const val MIN_UPTIME: Long = 5 * 1000
+
+private const val DEBUG = false
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
index 5748c4a..2537b19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
@@ -18,6 +18,7 @@
import android.content.Intent;
import android.service.notification.StatusBarNotification;
+import android.view.View;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -33,7 +34,8 @@
void startNotificationGutsIntent(Intent intent, int appUid,
ExpandableNotificationRow row);
- void startHistoryIntent(boolean showHistory);
+ /** Called when the user clicks "Manage" or "History" in the Shade. */
+ void startHistoryIntent(View view, boolean showHistory);
default boolean isCollapsingToShowActivityOverLockscreen() {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 78fcf18c..f8543f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -170,8 +170,8 @@
}
private void updateColors() {
- mNormalColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground)
- .getDefaultColor();
+ mNormalColor = Utils.getColorAttrDefaultColor(mContext,
+ com.android.internal.R.attr.colorSurface);
mTintedRippleColor = mContext.getColor(
R.color.notification_ripple_tinted_color);
mNormalRippleColor = mContext.getColor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 4ed5056..3bf0ddb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -106,10 +106,6 @@
showHistory(mShowHistory);
}
- public boolean isButtonVisible() {
- return mManageButton.getAlpha() != 0.0f;
- }
-
@Override
public ExpandableViewState createExpandableViewState() {
return new FooterViewState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index fa6f23d..3434f67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -44,6 +44,7 @@
import android.content.pm.ShortcutManager;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -181,7 +182,7 @@
showPriorityOnboarding();
} else if (mSelectedAction == ACTION_FAVORITE && getPriority() != mSelectedAction) {
mShadeController.animateCollapsePanels();
- mPeopleSpaceWidgetManager.requestPinAppWidget(mShortcutInfo);
+ mPeopleSpaceWidgetManager.requestPinAppWidget(mShortcutInfo, new Bundle());
}
mGutsContainer.closeControls(v, true);
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
index 9bbe616..270721f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
@@ -28,6 +28,7 @@
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
+import android.os.Bundle
import android.text.SpannableStringBuilder
import android.text.style.BulletSpan
import android.view.Gravity
@@ -86,7 +87,7 @@
Prefs.putBoolean(context, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING_IN_S, true)
dialog.dismiss()
shadeController.animateCollapsePanels()
- peopleSpaceWidgetManager.requestPinAppWidget(shortcutInfo)
+ peopleSpaceWidgetManager.requestPinAppWidget(shortcutInfo, Bundle())
}
private fun settings() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 8446b4e6..caf4720 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -60,7 +60,7 @@
private NotificationShelf mShelf;
private int mZDistanceBetweenElements;
private int mBaseZHeight;
- private int mMaxLayoutHeight;
+ private int mContentHeight;
private ExpandableView mLastVisibleBackgroundChild;
private float mCurrentScrollVelocity;
private int mStatusBarState;
@@ -84,6 +84,75 @@
private boolean mIsShadeOpening;
private float mSectionPadding;
+ /** Distance of top of notifications panel from top of screen. */
+ private float mStackY = 0;
+
+ /** Height of notifications panel. */
+ private float mStackHeight = 0;
+
+ /** Fraction of shade expansion. */
+ private float mExpansionFraction;
+
+ /** Height of the notifications panel without top padding when expansion completes. */
+ private float mStackEndHeight;
+
+ /**
+ * @return Height of the notifications panel without top padding when expansion completes.
+ */
+ public float getStackEndHeight() {
+ return mStackEndHeight;
+ }
+
+ /**
+ * @param stackEndHeight Height of the notifications panel without top padding
+ * when expansion completes.
+ */
+ public void setStackEndHeight(float stackEndHeight) {
+ mStackEndHeight = stackEndHeight;
+ }
+
+ /**
+ * @param stackY Distance of top of notifications panel from top of screen.
+ */
+ public void setStackY(float stackY) {
+ mStackY = stackY;
+ }
+
+ /**
+ * @return Distance of top of notifications panel from top of screen.
+ */
+ public float getStackY() {
+ return mStackY;
+ }
+
+ /**
+ * @param expansionFraction Fraction of shade expansion.
+ */
+ public void setExpansionFraction(float expansionFraction) {
+ mExpansionFraction = expansionFraction;
+ }
+
+ /**
+ * @return Fraction of shade expansion.
+ */
+ public float getExpansionFraction() {
+ return mExpansionFraction;
+ }
+
+ /**
+ * @param stackHeight Height of notifications panel.
+ */
+ public void setStackHeight(float stackHeight) {
+ mStackHeight = stackHeight;
+ }
+
+ /**
+ * @return Height of notifications panel.
+ */
+ public float getStackHeight() {
+ return mStackHeight;
+ }
+
/** Tracks the state from AlertingNotificationManager#hasNotifications() */
private boolean mHasAlertEntries;
@@ -263,8 +332,8 @@
if (mDozeAmount == 1.0f && !isPulseExpanding()) {
return mShelf.getHeight();
}
- int height = Math.max(mLayoutMinHeight,
- Math.min(mLayoutHeight, mMaxLayoutHeight) - mTopPadding);
+ int height = (int) Math.max(mLayoutMinHeight,
+ Math.min(mLayoutHeight, mContentHeight) - mTopPadding);
if (ignorePulseHeight) {
return height;
}
@@ -313,8 +382,12 @@
return mShelf;
}
- public void setLayoutMaxHeight(int maxLayoutHeight) {
- mMaxLayoutHeight = maxLayoutHeight;
+ public void setContentHeight(int contentHeight) {
+ mContentHeight = contentHeight;
+ }
+
+ public float getContentHeight() {
+ return mContentHeight;
}
/**
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 fb72ac3..751573a 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
@@ -658,6 +658,14 @@
y = getHeight() - getEmptyBottomMargin();
mDebugPaint.setColor(Color.GREEN);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+
+ y = (int) (mAmbientState.getStackY());
+ mDebugPaint.setColor(Color.CYAN);
+ canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+
+ y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
+ mDebugPaint.setColor(Color.BLUE);
+ canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
}
}
@@ -1123,18 +1131,37 @@
mTopPaddingNeedsAnimation = true;
mNeedsAnimation = true;
}
+ updateStackPosition();
requestChildrenUpdate();
notifyHeightChangeListener(null, animate);
}
}
/**
+ * Apply expansion fraction to the y position and height of the notifications panel.
+ */
+ private void updateStackPosition() {
+ // Consider interpolating from an mExpansionStartY for use on lockscreen and AOD
+ mAmbientState.setStackY(
+ MathUtils.lerp(0, mTopPadding, mAmbientState.getExpansionFraction()));
+ final float shadeBottom = getHeight() - getEmptyBottomMargin();
+ mAmbientState.setStackEndHeight(shadeBottom - mTopPadding);
+ mAmbientState.setStackHeight(
+ MathUtils.lerp(0, shadeBottom - mTopPadding, mAmbientState.getExpansionFraction()));
+ }
+
+ /**
* Update the height of the panel.
*
* @param height the expanded height of the panel
*/
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void setExpandedHeight(float height) {
+ final float shadeBottom = getHeight() - getEmptyBottomMargin();
+ final float expansionFraction = MathUtils.constrain(height / shadeBottom, 0f, 1f);
+ mAmbientState.setExpansionFraction(expansionFraction);
+ updateStackPosition();
+
mExpandedHeight = height;
setIsExpanded(height > 0);
int minExpansionHeight = getMinExpansionHeight();
@@ -2067,7 +2094,8 @@
mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomMargin;
updateScrollability();
clampScrollPosition();
- mAmbientState.setLayoutMaxHeight(mContentHeight);
+ updateStackPosition();
+ mAmbientState.setContentHeight(mContentHeight);
}
/**
@@ -4849,7 +4877,7 @@
clearNotifications(ROWS_ALL, true /* closeShade */);
});
footerView.setManageButtonClickListener(v -> {
- mNotificationActivityStarter.startHistoryIntent(mFooterView.isHistoryShown());
+ mNotificationActivityStarter.startHistoryIntent(v, mFooterView.isHistoryShown());
});
setFooterView(footerView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 7776e69..4fc49ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -788,6 +788,10 @@
return mView.getTranslationX();
}
+ public int indexOfChild(View view) {
+ return mView.indexOfChild(view);
+ }
+
public void setOnHeightChangedListener(
ExpandableView.OnHeightChangedListener listener) {
mView.setOnHeightChangedListener(listener);
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 bbdbe80..3e1a781 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
@@ -20,21 +20,22 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
-import android.util.Log;
import android.util.MathUtils;
import android.view.View;
import android.view.ViewGroup;
import com.android.systemui.R;
-import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.notification.dagger.SilentHeader;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.FooterView;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* The Algorithm of the {@link com.android.systemui.statusbar.notification.stack
@@ -92,20 +93,16 @@
// First we reset the view states to their default values.
resetChildViewStates();
-
initAlgorithmState(mHostView, algorithmState, ambientState);
-
updatePositionsForState(algorithmState, ambientState);
-
updateZValuesForState(algorithmState, ambientState);
-
updateHeadsUpStates(algorithmState, ambientState);
updatePulsingStates(algorithmState, ambientState);
updateDimmedActivatedHideSensitive(ambientState, algorithmState);
updateClipping(algorithmState, ambientState);
updateSpeedBumpState(algorithmState, speedBumpIndex);
- updateShelfState(ambientState);
+ updateShelfState(algorithmState, ambientState);
getNotificationChildrenStates(algorithmState, ambientState);
}
@@ -144,10 +141,13 @@
}
- private void updateShelfState(AmbientState ambientState) {
+ private void updateShelfState(
+ StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState) {
+
NotificationShelf shelf = ambientState.getShelf();
if (shelf != null) {
- shelf.updateState(ambientState);
+ shelf.updateState(algorithmState, ambientState);
}
}
@@ -172,7 +172,8 @@
&& ((ExpandableNotificationRow) child).isPinned();
if (mClipNotificationScrollToTop
&& (!state.inShelf || (isHeadsUp && !firstHeadsUp))
- && newYTranslation < clipStart) {
+ && newYTranslation < clipStart
+ && !ambientState.isShadeOpening()) {
// The previous view is overlapping on top, clip!
float overlapAmount = clipStart - newYTranslation;
state.clipTopAmount = (int) overlapAmount;
@@ -217,7 +218,6 @@
private void initAlgorithmState(ViewGroup hostView, StackScrollAlgorithmState state,
AmbientState ambientState) {
float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
-
int scrollY = ambientState.getScrollY();
// Due to the overScroller, the stackscroller can have negative scroll state. This is
@@ -230,7 +230,6 @@
state.visibleChildren.clear();
state.visibleChildren.ensureCapacity(childCount);
int notGoneIndex = 0;
- ExpandableView lastView = null;
for (int i = 0; i < childCount; i++) {
ExpandableView v = (ExpandableView) hostView.getChildAt(i);
if (v.getVisibility() != View.GONE) {
@@ -255,12 +254,101 @@
}
}
}
- ExpandableNotificationRow expandingNotification = ambientState.getExpandingNotification();
- state.indexOfExpandingNotification = expandingNotification != null
- ? expandingNotification.isChildInGroup()
- ? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
- : state.visibleChildren.indexOf(expandingNotification)
- : -1;
+
+ state.firstViewInShelf = null;
+ // Save y, sectionStart, sectionEnd from when shade is fully expanded.
+ // Consider updating these states in updateContentView instead so that we don't have to
+ // recalculate in every frame.
+ float currentY = -scrollY;
+ int sectionStartIndex = 0;
+ int sectionEndIndex = 0;
+ for (int i = 0; i < state.visibleChildren.size(); i++) {
+ final ExpandableView view = state.visibleChildren.get(i);
+ // Add space between sections.
+ final boolean applyGapHeight = childNeedsGapHeight(
+ ambientState.getSectionProvider(), i,
+ view, getPreviousView(i, state));
+ if (applyGapHeight) {
+ currentY += mGapHeight;
+ }
+
+ // Save index of first view in the shelf
+ final float shelfStart = ambientState.getStackEndHeight()
+ - ambientState.getShelf().getIntrinsicHeight();
+ if (currentY >= shelfStart
+ && !(view instanceof FooterView)
+ && state.firstViewInShelf == null) {
+ state.firstViewInShelf = view;
+ }
+
+ // Record y position when fully expanded
+ ExpansionData expansionData = new ExpansionData();
+ expansionData.fullyExpandedY = currentY;
+ state.expansionData.put(view, expansionData);
+
+ if (ambientState.getSectionProvider()
+ .beginsSection(view, getPreviousView(i, state))) {
+
+ // Save section start/end for views in the section before this new section
+ ExpandableView sectionStartView = state.visibleChildren.get(sectionStartIndex);
+ final float sectionStart =
+ state.expansionData.get(sectionStartView).fullyExpandedY;
+
+ ExpandableView sectionEndView = state.visibleChildren.get(sectionEndIndex);
+ float sectionEnd = state.expansionData.get(sectionEndView).fullyExpandedY
+ + sectionEndView.getIntrinsicHeight();
+
+ // If we show the shelf, trim section end to shelf start
+ // This means section end > start for views in the shelf
+ if (state.firstViewInShelf != null && sectionEnd > shelfStart) {
+ sectionEnd = shelfStart;
+ }
+
+ // Update section bounds of every view in the previous section
+ // Consider using shared SectionInfo for views in same section to avoid looping back
+ for (int j = sectionStartIndex; j < i; j++) {
+ ExpandableView sectionView = state.visibleChildren.get(j);
+ ExpansionData viewExpansionData =
+ state.expansionData.get(sectionView);
+ viewExpansionData.sectionStart = sectionStart;
+ viewExpansionData.sectionEnd = sectionEnd;
+ state.expansionData.put(sectionView, viewExpansionData);
+ }
+ sectionStartIndex = i;
+
+ if (view instanceof FooterView) {
+ // Also record section bounds for FooterView (same as its own)
+ // because it is the last view and we won't get to this point again
+ // after the loop ends
+ ExpansionData footerExpansionData = state.expansionData.get(view);
+ footerExpansionData.sectionStart = expansionData.fullyExpandedY;
+ footerExpansionData.sectionEnd = expansionData.fullyExpandedY
+ + view.getIntrinsicHeight();
+ state.expansionData.put(view, footerExpansionData);
+ }
+ }
+ sectionEndIndex = i;
+ currentY = currentY
+ + getMaxAllowedChildHeight(view)
+ + mPaddingBetweenElements;
+ }
+
+ // Which view starts the section of the view right before the shelf?
+ // Save it for later when we clip views in that section to shelf start.
+ state.firstViewInOverflowSection = null;
+ if (state.firstViewInShelf != null) {
+ ExpandableView nextView = null;
+ final int startIndex = state.visibleChildren.indexOf(state.firstViewInShelf);
+ for (int i = startIndex - 1; i >= 0; i--) {
+ ExpandableView view = state.visibleChildren.get(i);
+ if (nextView != null && ambientState.getSectionProvider()
+ .beginsSection(nextView, view)) {
+ break;
+ }
+ nextView = view;
+ }
+ state.firstViewInOverflowSection = nextView;
+ }
}
private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex,
@@ -272,6 +360,10 @@
return notGoneIndex;
}
+ private ExpandableView getPreviousView(int i, StackScrollAlgorithmState algorithmState) {
+ return i > 0 ? algorithmState.visibleChildren.get(i - 1) : null;
+ }
+
/**
* Determine the positions for the views. This is the main part of the algorithm.
*
@@ -288,6 +380,15 @@
}
}
+ private void setLocation(ExpandableViewState expandableViewState, float currentYPosition,
+ int i) {
+ expandableViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
+ if (currentYPosition <= 0) {
+ expandableViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
+ }
+ }
+
+ // TODO(b/172289889) polish shade open from HUN
/**
* Populates the {@link ExpandableViewState} for a single child.
*
@@ -306,53 +407,84 @@
StackScrollAlgorithmState algorithmState,
AmbientState ambientState,
float currentYPosition) {
- ExpandableView child = algorithmState.visibleChildren.get(i);
- ExpandableView previousChild = i > 0 ? algorithmState.visibleChildren.get(i - 1) : null;
+
+ ExpandableView view = algorithmState.visibleChildren.get(i);
+ ExpandableViewState viewState = view.getViewState();
+ viewState.location = ExpandableViewState.LOCATION_UNKNOWN;
+ viewState.alpha = 1f - ambientState.getHideAmount();
+
+ if (view.mustStayOnScreen() && viewState.yTranslation >= 0) {
+ // Even if we're not scrolled away we're in view and we're also not in the
+ // shelf. We can relax the constraints and let us scroll off the top!
+ float end = viewState.yTranslation + viewState.height + ambientState.getStackY();
+ viewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation();
+ }
+
+ // TODO(b/172289889) move sectionFraction and showSection to initAlgorithmState
+ // Get fraction of section showing, and later apply it to view height and gaps between views
+ float sectionFraction = 1f;
+ boolean showSection = true;
+
+ if (!ambientState.isOnKeyguard()
+ && !ambientState.isPulseExpanding()
+ && ambientState.isExpansionChanging()) {
+
+ final ExpansionData expansionData = algorithmState.expansionData.get(view);
+ final float sectionHeight = expansionData.sectionEnd - expansionData.sectionStart;
+ sectionFraction = MathUtils.constrain(
+ (ambientState.getStackHeight() - expansionData.sectionStart) / sectionHeight,
+ 0f, 1f);
+ showSection = expansionData.sectionStart < ambientState.getStackHeight();
+ }
+
+ // Add gap between sections.
final boolean applyGapHeight =
childNeedsGapHeight(
ambientState.getSectionProvider(), i,
- child, previousChild);
- ExpandableViewState childViewState = child.getViewState();
- childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
-
+ view, getPreviousView(i, algorithmState));
if (applyGapHeight) {
- currentYPosition += mGapHeight;
- }
- int childHeight = getMaxAllowedChildHeight(child);
- childViewState.yTranslation = currentYPosition;
- childViewState.alpha = 1f - ambientState.getHideAmount();
-
- boolean isFooterView = child instanceof FooterView;
- boolean isEmptyShadeView = child instanceof EmptyShadeView;
-
- childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
- float inset = ambientState.getTopPadding() + ambientState.getStackTranslation()
- + ambientState.getSectionPadding();
- if (child.mustStayOnScreen() && childViewState.yTranslation >= 0) {
- // Even if we're not scrolled away we're in view and we're also not in the
- // shelf. We can relax the constraints and let us scroll off the top!
- float end = childViewState.yTranslation + childViewState.height + inset;
- childViewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation();
- }
- if (isFooterView) {
- childViewState.yTranslation = Math.min(childViewState.yTranslation,
- ambientState.getInnerHeight() - childHeight);
- } else if (isEmptyShadeView) {
- childViewState.yTranslation = ambientState.getInnerHeight() - childHeight
- + ambientState.getStackTranslation() * 0.25f;
- } else if (child != ambientState.getTrackedHeadsUpRow()) {
- clampPositionToShelf(child, childViewState, ambientState);
+ currentYPosition += sectionFraction * mGapHeight;
}
- currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
- if (currentYPosition <= 0) {
- childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
- }
- if (childViewState.location == ExpandableViewState.LOCATION_UNKNOWN) {
- Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
+ viewState.yTranslation = currentYPosition;
+
+ if (view instanceof SectionHeaderView) {
+ // Add padding before sections for overscroll effect.
+ viewState.yTranslation += ambientState.getSectionPadding();
}
- childViewState.yTranslation += inset;
+ if (view != ambientState.getTrackedHeadsUpRow()) {
+ if (ambientState.isExpansionChanging()) {
+ viewState.hidden = !showSection;
+ viewState.inShelf = algorithmState.firstViewInShelf != null
+ && i >= algorithmState.visibleChildren.indexOf(
+ algorithmState.firstViewInShelf)
+ && !(view instanceof FooterView);
+ } else {
+ // When pulsing (incoming notification on AOD), innerHeight is 0; clamp all
+ // to shelf start, thereby hiding all notifications (except the first one, which we
+ // later unhide in updatePulsingState)
+ final int shelfStart = ambientState.getInnerHeight()
+ - ambientState.getShelf().getIntrinsicHeight();
+ if (!(view instanceof FooterView)) {
+ viewState.yTranslation = Math.min(viewState.yTranslation, shelfStart);
+ }
+ if (viewState.yTranslation >= shelfStart) {
+ viewState.hidden = !view.isExpandAnimationRunning()
+ && !view.hasExpandingChild()
+ && !(view instanceof FooterView);
+ viewState.inShelf = true;
+ // Notifications in the shelf cannot be visible HUNs.
+ viewState.headsUpIsVisible = false;
+ }
+ }
+ viewState.height = (int) MathUtils.lerp(
+ 0, getMaxAllowedChildHeight(view), sectionFraction);
+ }
+
+ currentYPosition += viewState.height + sectionFraction * mPaddingBetweenElements;
+ setLocation(view.getViewState(), currentYPosition, i);
+ viewState.yTranslation += ambientState.getStackY();
return currentYPosition;
}
@@ -393,10 +525,10 @@
int visibleIndex,
View child,
View previousChild) {
-
- boolean needsGapHeight = sectionProvider.beginsSection(child, previousChild)
- && visibleIndex > 0;
- return needsGapHeight;
+ return sectionProvider.beginsSection(child, previousChild)
+ && visibleIndex > 0
+ && !(previousChild instanceof SilentHeader)
+ && !(child instanceof FooterView);
}
private void updatePulsingStates(StackScrollAlgorithmState algorithmState,
@@ -514,42 +646,6 @@
childState.yTranslation = newTranslation;
}
- /**
- * Clamp the height of the child down such that its end is at most on the beginning of
- * the shelf.
- *
- * @param childViewState the view state of the child
- * @param ambientState the ambient state
- */
- private void clampPositionToShelf(ExpandableView child,
- ExpandableViewState childViewState,
- AmbientState ambientState) {
- if (ambientState.getShelf() == null) {
- return;
- }
-
- ExpandableNotificationRow trackedHeadsUpRow = ambientState.getTrackedHeadsUpRow();
- boolean isBeforeTrackedHeadsUp = trackedHeadsUpRow != null
- && mHostView.indexOfChild(child) < mHostView.indexOfChild(trackedHeadsUpRow);
-
- int shelfStart = ambientState.getInnerHeight()
- - ambientState.getShelf().getIntrinsicHeight();
- if (ambientState.isAppearing() && !child.isAboveShelf() && !isBeforeTrackedHeadsUp) {
- // Don't show none heads-up notifications while in appearing phase.
- childViewState.yTranslation = Math.max(childViewState.yTranslation, shelfStart);
- }
- childViewState.yTranslation = Math.min(childViewState.yTranslation, shelfStart);
- if (child instanceof SectionHeaderView) {
- // Add padding before sections for overscroll effect.
- childViewState.yTranslation += ambientState.getSectionPadding();
- }
- if (childViewState.yTranslation >= shelfStart) {
- childViewState.hidden = !child.isExpandAnimationRunning() && !child.hasExpandingChild();
- childViewState.inShelf = true;
- childViewState.headsUpIsVisible = false;
- }
- }
-
protected int getMaxAllowedChildHeight(View child) {
if (child instanceof ExpandableView) {
ExpandableView expandableView = (ExpandableView) child;
@@ -641,6 +737,35 @@
this.mIsExpanded = isExpanded;
}
+ /**
+ * Data used to layout views while shade expansion changes.
+ */
+ public class ExpansionData {
+
+ /**
+ * Y position of top of first view in section.
+ */
+ public float sectionStart;
+
+ /**
+ * Y position of bottom of last view in section.
+ */
+ public float sectionEnd;
+
+ /**
+ * Y position of view when shade is fully expanded.
+ * Does not include distance between top notifications panel and top of screen.
+ */
+ public float fullyExpandedY;
+
+ /**
+ * Whether this notification is in the same section as the notification right before the
+ * shelf. Used to determine which notification should be clipped to shelf start while
+ * shade expansion changes.
+ */
+ public boolean inOverflowingSection;
+ }
+
public class StackScrollAlgorithmState {
/**
@@ -649,15 +774,26 @@
public int scrollY;
/**
+ * First view in shelf.
+ */
+ public ExpandableView firstViewInShelf;
+
+ /**
+ * First view in section overflowing into shelf while shade expansion changes.
+ */
+ public ExpandableView firstViewInOverflowSection;
+
+ /**
+ * Map of view to ExpansionData used for layout during shade expansion.
+ * Use view instead of index as key, because visibleChildren indices do not match the ones
+ * used in the shelf.
+ */
+ public Map<ExpandableView, ExpansionData> expansionData = new HashMap<>();
+
+ /**
* The children from the host view which are not gone.
*/
- public final ArrayList<ExpandableView> visibleChildren = new ArrayList<ExpandableView>();
-
- private int indexOfExpandingNotification;
-
- public int getIndexOfExpandingNotification() {
- return indexOfExpandingNotification;
- }
+ public final ArrayList<ExpandableView> visibleChildren = new ArrayList<>();
}
/**
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 40e6b40..1eccfd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -19,10 +19,16 @@
import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
+
+import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Parcelable;
+import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
@@ -36,6 +42,9 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
@@ -44,6 +53,8 @@
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import org.jetbrains.annotations.NotNull;
+
import java.util.ArrayList;
import java.util.List;
@@ -55,7 +66,8 @@
* updated by the StatusBarIconController and DarkIconManager while it is attached.
*/
public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks,
- StatusBarStateController.StateListener {
+ StatusBarStateController.StateListener,
+ SystemStatusAnimationCallback {
public static final String TAG = "CollapsedStatusBarFragment";
private static final String EXTRA_PANEL_STATE = "panel_state";
@@ -78,6 +90,8 @@
private View mOperatorNameFrame;
private CommandQueue mCommandQueue;
private OngoingCallController mOngoingCallController;
+ private final SystemStatusAnimationScheduler mAnimationScheduler;
+ private final PrivacyDotViewController mDotViewController;
private List<String> mBlockedIcons = new ArrayList<>();
@@ -103,8 +117,14 @@
};
@Inject
- public CollapsedStatusBarFragment(OngoingCallController ongoingCallController) {
+ public CollapsedStatusBarFragment(
+ OngoingCallController ongoingCallController,
+ SystemStatusAnimationScheduler animationScheduler,
+ PrivacyDotViewController dotViewController
+ ) {
mOngoingCallController = ongoingCallController;
+ mAnimationScheduler = animationScheduler;
+ mDotViewController = dotViewController;
}
@Override
@@ -127,6 +147,9 @@
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mStatusBar = (PhoneStatusBarView) view;
+ View contents = mStatusBar.findViewById(R.id.status_bar_contents);
+ contents.addOnLayoutChangeListener(mStatusBarLayoutListener);
+ updateStatusBarLocation(contents.getLeft(), contents.getRight());
if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
mStatusBar.restoreHierarchyState(
savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
@@ -139,10 +162,12 @@
Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
mClockView = mStatusBar.findViewById(R.id.clock);
+ mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
showSystemIconArea(false);
showClock(false);
initEmergencyCryptkeeperText();
initOperatorName();
+ mAnimationScheduler.addCallback(this);
}
@Override
@@ -158,7 +183,7 @@
super.onResume();
mCommandQueue.addCallback(this);
mStatusBarStateController.addCallback(this);
- mOngoingCallController.addCallback(mOngoingCallListener);
+ initOngoingCallChip();
}
@Override
@@ -197,8 +222,6 @@
}
statusBarCenteredIconArea.addView(mCenteredIconArea);
- initOngoingCallChip();
-
// Default to showing until we know otherwise.
showNotificationIconArea(false);
}
@@ -208,6 +231,7 @@
if (displayId != getContext().getDisplayId()) {
return;
}
+ Log.d(TAG, "disable: ");
state1 = adjustDisableFlags(state1);
final int old1 = mDisabled1;
final int diff1 = state1 ^ old1;
@@ -248,7 +272,7 @@
state |= DISABLE_CLOCK;
}
- if (mOngoingCallController.getHasOngoingCall()) {
+ if (mOngoingCallController.hasOngoingCall()) {
state |= DISABLE_NOTIFICATION_ICONS;
}
@@ -292,19 +316,22 @@
return false;
}
- public void hideSystemIconArea(boolean animate) {
+ private void hideSystemIconArea(boolean animate) {
animateHide(mSystemIconArea, animate);
}
- public void showSystemIconArea(boolean animate) {
- animateShow(mSystemIconArea, animate);
+ private void showSystemIconArea(boolean animate) {
+ // Only show the system icon area if we are not currently animating
+ if (mAnimationScheduler.getAnimationState() == IDLE) {
+ animateShow(mSystemIconArea, animate);
+ }
}
- public void hideClock(boolean animate) {
+ private void hideClock(boolean animate) {
animateHiddenState(mClockView, clockHiddenMode(), animate);
}
- public void showClock(boolean animate) {
+ private void showClock(boolean animate) {
animateShow(mClockView, animate);
}
@@ -420,17 +447,65 @@
}
private void initOngoingCallChip() {
- mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
+ mOngoingCallController.addCallback(mOngoingCallListener);
mOngoingCallController.setChipView(mOngoingCallChip);
}
@Override
- public void onStateChanged(int newState) {
-
- }
+ public void onStateChanged(int newState) { }
@Override
public void onDozingChanged(boolean isDozing) {
disable(getContext().getDisplayId(), mDisabled1, mDisabled1, false /* animate */);
}
+
+ @Override
+ public void onSystemChromeAnimationStart() {
+ if (mAnimationScheduler.getAnimationState() == ANIMATING_OUT
+ && !isSystemIconAreaDisabled()) {
+ mSystemIconArea.setVisibility(View.VISIBLE);
+ mSystemIconArea.setAlpha(0f);
+ }
+ }
+
+ @Override
+ public void onSystemChromeAnimationEnd() {
+ // Make sure the system icons are out of the way
+ if (mAnimationScheduler.getAnimationState() == ANIMATING_IN) {
+ mSystemIconArea.setVisibility(View.INVISIBLE);
+ mSystemIconArea.setAlpha(0f);
+ } else {
+ if (isSystemIconAreaDisabled()) {
+ // don't unhide
+ return;
+ }
+
+ mSystemIconArea.setAlpha(1f);
+ mSystemIconArea.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void onSystemChromeAnimationUpdate(@NotNull ValueAnimator animator) {
+ mSystemIconArea.setAlpha((float) animator.getAnimatedValue());
+ }
+
+ private boolean isSystemIconAreaDisabled() {
+ return (mDisabled1 & DISABLE_SYSTEM_INFO) != 0 || (mDisabled2 & DISABLE2_SYSTEM_ICONS) != 0;
+ }
+
+ private void updateStatusBarLocation(int left, int right) {
+ int leftMargin = left - mStatusBar.getLeft();
+ int rightMargin = mStatusBar.getRight() - right;
+
+ mDotViewController.setStatusBarMargins(leftMargin, rightMargin);
+ }
+
+ // Listen for view end changes of PhoneStatusBarView and publish that to the privacy dot
+ private View.OnLayoutChangeListener mStatusBarLayoutListener =
+ (view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ if (left != oldLeft || right != oldRight) {
+ updateStatusBarLocation(left, right);
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index c3325b1..8cef23f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -68,6 +68,7 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
@@ -154,6 +155,7 @@
private StatusBar mStatusBar;
private KeyguardAffordanceHelper mAffordanceHelper;
private FalsingManager mFalsingManager;
+ @Nullable private Executor mUiExecutor;
private boolean mUserSetupComplete;
private boolean mPrewarmBound;
private Messenger mPrewarmMessenger;
@@ -181,6 +183,7 @@
private String mLeftButtonStr;
private boolean mDozing;
private int mIndicationBottomMargin;
+ private int mIndicationPadding;
private float mDarkAmount;
private int mBurnInXOffset;
private int mBurnInYOffset;
@@ -274,6 +277,10 @@
mAccessibilityController = Dependency.get(AccessibilityController.class);
mActivityIntentHelper = new ActivityIntentHelper(getContext());
updateLeftAffordance();
+
+ mIndicationPadding = getResources().getDimensionPixelSize(
+ R.dimen.keyguard_indication_area_padding);
+ updateWalletVisibility();
}
@Override
@@ -356,6 +363,10 @@
lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
mWalletButton.setLayoutParams(lp);
+
+ mIndicationPadding = getResources().getDimensionPixelSize(
+ R.dimen.keyguard_indication_area_padding);
+ updateWalletVisibility();
}
private void updateRightAffordanceIcon() {
@@ -429,11 +440,13 @@
}
private void updateWalletVisibility() {
- if (mDozing || !mWalletEnabled) {
+ if (mDozing || !mWalletEnabled || !mHasCard) {
mWalletButton.setVisibility(GONE);
+ mIndicationArea.setPadding(0, 0, 0, 0);
} else {
mWalletButton.setVisibility(VISIBLE);
mWalletButton.setOnClickListener(this::onWalletClick);
+ mIndicationArea.setPadding(mIndicationPadding, 0, mIndicationPadding, 0);
}
}
@@ -659,6 +672,13 @@
updateCameraVisibility();
}
+ @Override
+ public void onKeyguardShowingChanged() {
+ if (mKeyguardStateController.isShowing()) {
+ queryWalletCards();
+ }
+ }
+
private void inflateCameraPreview() {
View previewBefore = mCameraPreview;
boolean visibleBefore = false;
@@ -897,18 +917,20 @@
public void initWallet(QuickAccessWalletClient client, Executor uiExecutor, boolean enabled) {
mQuickAccessWalletClient = client;
mWalletEnabled = enabled && client.isWalletFeatureAvailable();
+ mUiExecutor = uiExecutor;
+ queryWalletCards();
- if (mWalletEnabled) {
- queryWalletCards(uiExecutor);
- }
updateWalletVisibility();
}
- private void queryWalletCards(Executor uiExecutor) {
+ private void queryWalletCards() {
+ if (!mWalletEnabled || mUiExecutor == null) {
+ return;
+ }
GetWalletCardsRequest request =
new GetWalletCardsRequest(1 /* cardWidth */, 1 /* cardHeight */,
1 /* iconSizePx */, 2 /* maxCards */);
- mQuickAccessWalletClient.getWalletCards(uiExecutor, request, mCardRetriever);
+ mQuickAccessWalletClient.getWalletCards(mUiExecutor, request, mCardRetriever);
}
private void onWalletClick(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 707135c3..30d9841 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -16,11 +16,13 @@
package com.android.systemui.statusbar.phone
+import android.annotation.IntDef
import android.content.Context
import android.content.pm.PackageManager
import android.hardware.biometrics.BiometricSourceType
import android.provider.Settings
import com.android.systemui.Dumpable
+import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -37,9 +39,18 @@
private val mKeyguardStateController: KeyguardStateController
private val statusBarStateController: StatusBarStateController
+ @BypassOverride private val bypassOverride: Int
private var hasFaceFeature: Boolean
private var pendingUnlock: PendingUnlock? = null
+ @IntDef(
+ FACE_UNLOCK_BYPASS_NO_OVERRIDE,
+ FACE_UNLOCK_BYPASS_ALWAYS,
+ FACE_UNLOCK_BYPASS_NEVER
+ )
+ @Retention(AnnotationRetention.SOURCE)
+ private annotation class BypassOverride
+
/**
* Pending unlock info:
*
@@ -60,7 +71,14 @@
* If face unlock dismisses the lock screen or keeps user on keyguard for the current user.
*/
var bypassEnabled: Boolean = false
- get() = field && mKeyguardStateController.isFaceAuthEnabled
+ get() {
+ val enabled = when (bypassOverride) {
+ FACE_UNLOCK_BYPASS_ALWAYS -> true
+ FACE_UNLOCK_BYPASS_NEVER -> false
+ else -> field
+ }
+ return enabled && mKeyguardStateController.isFaceAuthEnabled
+ }
private set
var bouncerShowing: Boolean = false
@@ -86,6 +104,8 @@
this.mKeyguardStateController = keyguardStateController
this.statusBarStateController = statusBarStateController
+ bypassOverride = context.resources.getInteger(R.integer.config_face_unlock_bypass_override)
+
hasFaceFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)
if (!hasFaceFeature) {
return
@@ -198,5 +218,9 @@
companion object {
const val BYPASS_PANEL_FADE_DURATION = 67
+
+ private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0
+ private const val FACE_UNLOCK_BYPASS_ALWAYS = 1
+ private const val FACE_UNLOCK_BYPASS_NEVER = 2
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 0694737..c22fec9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -18,7 +18,10 @@
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
+import android.animation.ValueAnimator;
import android.annotation.ColorInt;
import android.content.Context;
import android.content.res.Configuration;
@@ -47,6 +50,8 @@
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -64,8 +69,11 @@
/**
* The header group on Keyguard.
*/
-public class KeyguardStatusBarView extends RelativeLayout
- implements BatteryStateChangeCallback, OnUserInfoChangedListener, ConfigurationListener {
+public class KeyguardStatusBarView extends RelativeLayout implements
+ BatteryStateChangeCallback,
+ OnUserInfoChangedListener,
+ ConfigurationListener,
+ SystemStatusAnimationCallback {
private static final int LAYOUT_NONE = 0;
private static final int LAYOUT_CUTOUT = 1;
@@ -96,6 +104,8 @@
private ViewGroup mStatusIconArea;
private int mLayoutState = LAYOUT_NONE;
+ private SystemStatusAnimationScheduler mAnimationScheduler;
+
/**
* Draw this many pixels into the left/right side of the cutout to optimally use the space
*/
@@ -125,6 +135,7 @@
loadDimens();
loadBlockList();
mBatteryController = Dependency.get(BatteryController.class);
+ mAnimationScheduler = Dependency.get(SystemStatusAnimationScheduler.class);
}
@Override
@@ -349,6 +360,7 @@
mIconManager = new TintedIconManager(findViewById(R.id.statusIcons));
mIconManager.setBlockList(mBlockedIcons);
Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
+ mAnimationScheduler.addCallback(this);
onThemeChanged();
}
@@ -358,6 +370,7 @@
Dependency.get(UserInfoController.class).removeCallback(this);
Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
Dependency.get(ConfigurationController.class).removeCallback(this);
+ mAnimationScheduler.removeCallback(this);
}
@Override
@@ -509,4 +522,30 @@
mBatteryView.dump(fd, pw, args);
}
}
+
+ /** SystemStatusAnimationCallback */
+ @Override
+ public void onSystemChromeAnimationStart() {
+ if (mAnimationScheduler.getAnimationState() == ANIMATING_OUT) {
+ mSystemIconsContainer.setVisibility(View.VISIBLE);
+ mSystemIconsContainer.setAlpha(0f);
+ }
+ }
+
+ @Override
+ public void onSystemChromeAnimationEnd() {
+ // Make sure the system icons are out of the way
+ if (mAnimationScheduler.getAnimationState() == ANIMATING_IN) {
+ mSystemIconsContainer.setVisibility(View.INVISIBLE);
+ mSystemIconsContainer.setAlpha(0f);
+ } else {
+ mSystemIconsContainer.setAlpha(1f);
+ mSystemIconsContainer.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void onSystemChromeAnimationUpdate(ValueAnimator anim) {
+ mSystemIconsContainer.setAlpha((float) anim.getAnimatedValue());
+ }
}
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 6a35293..c4d8840 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1997,12 +1997,35 @@
float qsExpansionFraction = getQsExpansionFraction();
mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
- mScrimController.setQsPosition(qsExpansionFraction,
- calculateQsBottomPosition(qsExpansionFraction));
+ int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
+ mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
+ setNotificationBounds(qsExpansionFraction, qsPanelBottomY);
mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
mDepthController.setQsPanelExpansion(qsExpansionFraction);
}
+ private void setNotificationBounds(float qsExpansionFraction, int qsPanelBottomY) {
+ float top = 0;
+ float bottom = 0;
+ float left = 0;
+ float right = 0;
+ if (qsPanelBottomY > 0) {
+ // notification shade is expanding/expanded
+ if (!mShouldUseSplitNotificationShade) {
+ top = qsPanelBottomY;
+ bottom = getView().getBottom();
+ left = getView().getLeft();
+ right = getView().getRight();
+ } else {
+ top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
+ bottom = mNotificationStackScrollLayoutController.getHeight();
+ left = mNotificationStackScrollLayoutController.getLeft();
+ right = mNotificationStackScrollLayoutController.getRight();
+ }
+ }
+ mScrimController.setNotificationsBounds(left, top, right, bottom);
+ }
+
private int calculateQsBottomPosition(float qsExpansionFraction) {
int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight();
if (qsExpansionFraction != 0.0) {
@@ -2030,7 +2053,7 @@
private float calculateNotificationsTopPadding() {
if (mShouldUseSplitNotificationShade && !mKeyguardShowing) {
- return mSplitShadeNotificationsTopPadding;
+ return mSplitShadeNotificationsTopPadding + mQsNotificationTopPadding;
}
if (mKeyguardShowing && (mQsExpandImmediate
|| mIsExpanding && mQsExpandedWhenExpandingStarted)) {
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 5aecb72..0c8122c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -666,7 +666,9 @@
pw.println(TAG + ":");
pw.println(" mKeyguardDisplayMode=" + mKeyguardDisplayMode);
pw.println(mCurrentState);
- mNotificationShadeView.getViewRootImpl().dump(" ", pw);
+ if (mNotificationShadeView != null && mNotificationShadeView.getViewRootImpl() != null) {
+ mNotificationShadeView.getViewRootImpl().dump(" ", pw);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 58488ef..5e9c758 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -487,6 +487,13 @@
}
/**
+ * Set bounds for notifications background, all coordinates are absolute
+ */
+ public void setNotificationsBounds(float left, float top, float right, float bottom) {
+ mNotificationsScrim.setDrawableBounds(left, top, right, bottom);
+ }
+
+ /**
* Current state of the QuickSettings when pulling it from the top.
*
* @param expansionFraction From 0 to 1 where 0 means collapsed and 1 expanded.
@@ -496,7 +503,6 @@
if (isNaN(expansionFraction)) {
return;
}
- shiftNotificationsScrim(qsPanelBottomY);
updateNotificationsScrimAlpha(expansionFraction, qsPanelBottomY);
if (mQsExpansion != expansionFraction) {
mQsExpansion = expansionFraction;
@@ -511,14 +517,6 @@
}
}
- private void shiftNotificationsScrim(int qsPanelBottomY) {
- if (qsPanelBottomY > 0) {
- mNotificationsScrim.setTranslationY(qsPanelBottomY);
- } else {
- mNotificationsScrim.setTranslationY(0);
- }
- }
-
private void updateNotificationsScrimAlpha(float qsExpansion, int qsPanelBottomY) {
float newAlpha = 0;
if (qsPanelBottomY > 0) {
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 d386ebd..0e99d3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -208,6 +208,8 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.charging.WiredChargingRippleController;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
@@ -426,6 +428,8 @@
private final DemoModeController mDemoModeController;
private NotificationsController mNotificationsController;
private final OngoingCallController mOngoingCallController;
+ private final SystemStatusAnimationScheduler mAnimationScheduler;
+ private final PrivacyDotViewController mDotViewController;
// expanded notifications
// the sliding/resizing panel within the notification window
@@ -794,6 +798,8 @@
BrightnessSlider.Factory brightnessSliderFactory,
WiredChargingRippleController chargingRippleAnimationController,
OngoingCallController ongoingCallController,
+ SystemStatusAnimationScheduler animationScheduler,
+ PrivacyDotViewController dotViewController,
TunerService tunerService,
FeatureFlags featureFlags) {
super(context);
@@ -875,6 +881,8 @@
mBrightnessSliderFactory = brightnessSliderFactory;
mChargingRippleAnimationController = chargingRippleAnimationController;
mOngoingCallController = ongoingCallController;
+ mAnimationScheduler = animationScheduler;
+ mDotViewController = dotViewController;
mFeatureFlags = featureFlags;
tunerService.addTunable(
@@ -1171,7 +1179,10 @@
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container,
- new CollapsedStatusBarFragment(mOngoingCallController),
+ new CollapsedStatusBarFragment(
+ mOngoingCallController,
+ mAnimationScheduler,
+ mDotViewController),
CollapsedStatusBarFragment.TAG)
.commit();
@@ -1788,7 +1799,15 @@
@Override
public void startActivity(Intent intent, boolean dismissShade) {
- startActivityDismissingKeyguard(intent, false, dismissShade);
+ startActivityDismissingKeyguard(intent, false /* onlyProvisioned */, dismissShade);
+ }
+
+ @Override
+ public void startActivity(Intent intent, boolean dismissShade,
+ ActivityLaunchAnimator.Controller animationController) {
+ startActivityDismissingKeyguard(intent, false, dismissShade,
+ false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
+ 0 /* flags */, animationController);
}
@Override
@@ -4603,9 +4622,6 @@
} else {
options = ActivityOptions.makeBasic();
}
- // Anything launched from the notification shade should always go into the secondary
- // split-screen windowing mode.
- options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
return options;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 3404528..4356b52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -39,6 +39,7 @@
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.EventLog;
+import android.view.View;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.NotificationVisibility;
@@ -462,7 +463,7 @@
mActivityStarter.dismissKeyguardThenExecute(() -> {
AsyncTask.execute(() -> {
ActivityLaunchAnimator.Controller animationController = null;
- if (!mStatusBar.isOccluded() && mStatusBar.areLaunchAnimationsEnabled()) {
+ if (mStatusBar.areLaunchAnimationsEnabled()) {
animationController = new StatusBarLaunchAnimatorController(
mNotificationAnimationProvider.getAnimatorController(row), mStatusBar,
true /* isActivityIntent */);
@@ -495,7 +496,7 @@
}
@Override
- public void startHistoryIntent(boolean showHistory) {
+ public void startHistoryIntent(View view, boolean showHistory) {
mActivityStarter.dismissKeyguardThenExecute(() -> {
AsyncTask.execute(() -> {
Intent intent = showHistory ? new Intent(
@@ -506,11 +507,27 @@
if (showHistory) {
tsb.addNextIntent(intent);
}
- tsb.startActivities(null, UserHandle.CURRENT);
- // Putting it back on the main thread, since we're touching views
- mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
+ ActivityLaunchAnimator.Controller animationController = null;
+ if (mStatusBar.areLaunchAnimationsEnabled()) {
+ animationController = new StatusBarLaunchAnimatorController(
+ ActivityLaunchAnimator.Controller.fromView(view), mStatusBar,
+ true /* isActivityIntent */);
+ }
+
+ mActivityLaunchAnimator.startIntentWithAnimation(animationController,
+ (adapter) -> tsb.startActivities(
+ getActivityOptions(mStatusBar.getDisplayId(), adapter),
+ UserHandle.CURRENT));
+
+ // Note that other cases when we should still collapse (like activity already on
+ // top) is handled by the StatusBarLaunchAnimatorController.
+ boolean shouldCollapse = animationController == null;
+ if (shouldCollapse) {
+ // Putting it back on the main thread, since we're touching views
+ mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
+ }
});
return true;
}, null, false /* afterKeyguardGone */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 2c2779e..24e6db8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -61,6 +61,8 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.charging.WiredChargingRippleController;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -209,6 +211,8 @@
BrightnessSlider.Factory brightnessSliderFactory,
WiredChargingRippleController chargingRippleAnimationController,
OngoingCallController ongoingCallController,
+ SystemStatusAnimationScheduler animationScheduler,
+ PrivacyDotViewController dotViewController,
TunerService tunerService,
FeatureFlags featureFlags) {
return new StatusBar(
@@ -293,6 +297,8 @@
brightnessSliderFactory,
chargingRippleAnimationController,
ongoingCallController,
+ animationScheduler,
+ dotViewController,
tunerService,
featureFlags);
}
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 b55db6f..96473c2 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
@@ -18,11 +18,14 @@
import android.app.Notification
import android.app.Notification.CallStyle.CALL_TYPE_ONGOING
+import android.content.Intent
import android.util.Log
import android.view.ViewGroup
import android.widget.Chronometer
import com.android.systemui.R
+import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -38,11 +41,12 @@
class OngoingCallController @Inject constructor(
private val notifCollection: CommonNotifCollection,
private val featureFlags: FeatureFlags,
- private val systemClock: SystemClock
+ private val systemClock: SystemClock,
+ private val activityStarter: ActivityStarter
) : CallbackController<OngoingCallListener> {
- var hasOngoingCall = false
- private set
+ /** Null if there's no ongoing call. */
+ private var ongoingCallInfo: OngoingCallInfo? = null
private var chipView: ViewGroup? = null
private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
@@ -50,25 +54,16 @@
private val notifListener = object : NotifCollectionListener {
override fun onEntryUpdated(entry: NotificationEntry) {
if (isOngoingCallNotification(entry)) {
- val timeView = chipView?.findViewById<Chronometer>(R.id.ongoing_call_chip_time)
- if (timeView != null) {
- hasOngoingCall = true
- val callStartTime = entry.sbn.notification.`when`
- timeView.base = callStartTime -
- System.currentTimeMillis() +
- systemClock.elapsedRealtime()
- timeView.start()
- mListeners.forEach { l -> l.onOngoingCallStarted(animate = true) }
- } else if (DEBUG) {
- Log.w(TAG, "Ongoing call chip view could not be found; " +
- "Not displaying chip in status bar")
- }
+ ongoingCallInfo = OngoingCallInfo(
+ entry.sbn.notification.`when`,
+ entry.sbn.notification.contentIntent.intent)
+ updateChip()
}
}
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
if (isOngoingCallNotification(entry)) {
- hasOngoingCall = false
+ ongoingCallInfo = null
mListeners.forEach { l -> l.onOngoingCallEnded(animate = true) }
}
}
@@ -80,10 +75,23 @@
}
}
- fun setChipView(chipView: ViewGroup?) {
+ /**
+ * Sets the chip view that will contain ongoing call information.
+ *
+ * Should only be called from [CollapedStatusBarFragment].
+ */
+ fun setChipView(chipView: ViewGroup) {
this.chipView = chipView
+ if (hasOngoingCall()) {
+ updateChip()
+ }
}
+ /**
+ * Returns true if there's an active ongoing call that can be displayed in a status bar chip.
+ */
+ fun hasOngoingCall(): Boolean = ongoingCallInfo != null
+
override fun addCallback(listener: OngoingCallListener) {
synchronized(mListeners) {
if (!mListeners.contains(listener)) {
@@ -97,6 +105,43 @@
mListeners.remove(listener)
}
}
+
+ private fun updateChip() {
+ val currentOngoingCallInfo = ongoingCallInfo ?: return
+
+ val currentChipView = chipView
+ val timeView =
+ currentChipView?.findViewById<Chronometer>(R.id.ongoing_call_chip_time)
+
+ if (currentChipView != null && timeView != null) {
+ timeView.base = currentOngoingCallInfo.callStartTime -
+ System.currentTimeMillis() +
+ systemClock.elapsedRealtime()
+ timeView.start()
+
+ currentChipView.setOnClickListener {
+ activityStarter.postStartActivityDismissingKeyguard(
+ currentOngoingCallInfo.intent, 0,
+ ActivityLaunchAnimator.Controller.fromView(it))
+ }
+
+ mListeners.forEach { l -> l.onOngoingCallStarted(animate = true) }
+ } else {
+ // If we failed to update the chip, don't store the ongoing call info. Then
+ // [hasOngoingCall] will return false and we fall back to typical notification handling.
+ ongoingCallInfo = null
+
+ if (DEBUG) {
+ Log.w(TAG, "Ongoing call chip view could not be found; " +
+ "Not displaying chip in status bar")
+ }
+ }
+ }
+
+ private class OngoingCallInfo(
+ val callStartTime: Long,
+ val intent: Intent
+ )
}
private fun isOngoingCallNotification(entry: NotificationEntry): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index fd3641c..f3a95f7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -21,8 +21,10 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.provider.Settings;
import android.text.TextUtils;
+import android.view.ContextThemeWrapper;
import android.view.View;
import com.android.systemui.R;
@@ -177,4 +179,23 @@
&& resources.getBoolean(R.bool.config_use_split_notification_shade);
}
+ /**
+ * Returns the color provided at the specified {@param attrIndex} in {@param a} if it exists,
+ * otherwise, returns the color from the private attribute {@param privAttrId}.
+ */
+ public static int getPrivateAttrColorIfUnset(ContextThemeWrapper ctw, TypedArray a,
+ int attrIndex, int defColor, int privAttrId) {
+ // If the index is specified, use that value
+ if (a.hasValue(attrIndex)) {
+ return a.getColor(attrIndex, defColor);
+ }
+
+ // Otherwise fallback to the value of the private attribute
+ int[] customAttrs = { privAttrId };
+ a = ctw.obtainStyledAttributes(customAttrs);
+ int color = a.getColor(0, defColor);
+ a.recycle();
+ return color;
+ }
+
}
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 d195062..d1a2c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -222,6 +222,11 @@
if (mIsDismissed) {
return;
}
+ int cardWidthPx = mCardCarousel.getCardWidthPx();
+ int cardHeightPx = mCardCarousel.getCardHeightPx();
+ if (cardWidthPx == 0 || cardHeightPx == 0) {
+ return;
+ }
if (!mHasRegisteredListener) {
// Listener is registered even when device is locked. Should only be registered once.
mWalletClient.addWalletServiceEventListener(this);
@@ -231,8 +236,6 @@
mWalletView.show();
mWalletView.hideErrorMessage();
int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size);
- int cardWidthPx = mCardCarousel.getCardWidthPx();
- int cardHeightPx = mCardCarousel.getCardHeightPx();
GetWalletCardsRequest request =
new GetWalletCardsRequest(cardWidthPx, cardHeightPx, iconSizePx, MAX_CARDS);
mWalletClient.getWalletCards(mExecutor, request, this);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 55b80dd..db77366 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -633,7 +633,7 @@
} else {
mNotificationGroupManager.onEntryRemoved(entry);
}
- });
+ }, mSysuiMainExecutor);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 74b79d5..81bb819 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -268,6 +268,7 @@
public void onStop() {
mSysUiMainExecutor.execute(() -> {
if (oneHanded.isOneHandedEnabled()) {
+ // Log metrics for 3-button navigation mode.
oneHanded.stopOneHanded(
OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
} else if (oneHanded.isSwipeToNotificationEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 3fc3d89..f96d344 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -381,8 +381,8 @@
@WMSingleton
@Provides
static StartingWindowController provideStartingWindowController(Context context,
- @ShellSplashscreenThread ShellExecutor executor, TransactionPool pool) {
- return new StartingWindowController(context, executor, pool);
+ @ShellSplashscreenThread ShellExecutor splashScreenExecutor, TransactionPool pool) {
+ return new StartingWindowController(context, splashScreenExecutor, pool);
}
//
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index a870915..4d4acd9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -16,16 +16,15 @@
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.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.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.test.suitebuilder.annotation.SmallTest;
@@ -38,13 +37,13 @@
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
@@ -87,12 +86,8 @@
@Mock
NotificationIconAreaController mNotificationIconAreaController;
@Mock
- ContentResolver mContentResolver;
- @Mock
BroadcastDispatcher mBroadcastDispatcher;
@Mock
- private PluginManager mPluginManager;
- @Mock
private FeatureFlags mFeatureFlags;
@Mock
private Executor mExecutor;
@@ -106,6 +101,12 @@
BatteryController mBatteryController;
@Mock
ConfigurationController mConfigurationController;
+ @Mock
+ BcSmartspaceDataPlugin mSmartspaceDataProvider;
+ @Mock
+ SmartspaceView mSmartspaceView;
+ @Mock
+ SystemUIFactory mSystemUIFactory;
private KeyguardClockSwitchController mController;
private View mStatusArea;
@@ -129,27 +130,28 @@
when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
when(mView.isAttachedToWindow()).thenReturn(true);
when(mResources.getString(anyInt())).thenReturn("h:mm");
+ when(mSystemUIFactory.getSmartspaceDataProvider()).thenReturn(mSmartspaceDataProvider);
mController = new KeyguardClockSwitchController(
mView,
- mResources,
mStatusBarStateController,
mColorExtractor,
mClockManager,
mKeyguardSliceViewController,
mNotificationIconAreaController,
- mContentResolver,
mBroadcastDispatcher,
- mPluginManager,
mFeatureFlags,
mExecutor,
mBatteryController,
- mConfigurationController);
+ mConfigurationController,
+ mSystemUIFactory
+ );
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
- mStatusArea = mock(View.class);
+ mStatusArea = new View(getContext());
when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea);
+ when(mSmartspaceDataProvider.getView(any())).thenReturn(mSmartspaceView);
}
@@ -210,43 +212,37 @@
}
@Test
- public void testSmartspacePluginConnectedRemovesKeyguardStatusArea() {
+ public void testSmartspaceEnabledRemovesKeyguardStatusArea() {
+ when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
mController.init();
- BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class);
- TestView view = mock(TestView.class);
- when(plugin.getView(any())).thenReturn(view);
-
- mController.mPluginListener.onPluginConnected(plugin, mContext);
- verify(mStatusArea).setVisibility(View.GONE);
+ assertEquals(View.GONE, mStatusArea.getVisibility());
}
@Test
- public void testSmartspacePluginDisconnectedShowsKeyguardStatusArea() {
+ public void testSmartspaceEnabledNoDataProviderShowsKeyguardStatusArea() {
+ when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
+ when(mSystemUIFactory.getSmartspaceDataProvider()).thenReturn(null);
mController.init();
- BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class);
- TestView view = mock(TestView.class);
- when(plugin.getView(any())).thenReturn(view);
+ assertEquals(View.VISIBLE, mStatusArea.getVisibility());
+ }
- mController.mPluginListener.onPluginConnected(plugin, mContext);
- mController.mPluginListener.onPluginDisconnected(plugin);
- verify(mStatusArea).setVisibility(View.VISIBLE);
+ @Test
+ public void testSmartspaceDisabledShowsKeyguardStatusArea() {
+ when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(false);
+ mController.init();
+
+ assertEquals(View.VISIBLE, mStatusArea.getVisibility());
}
@Test
public void testThemeChangeNotifiesSmartspace() {
mController.init();
+ verify(mSmartspaceView).setPrimaryTextColor(anyInt());
- BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class);
- TestView view = mock(TestView.class);
- when(plugin.getView(any())).thenReturn(view);
-
- mController.mPluginListener.onPluginConnected(plugin, mContext);
-
- reset(view);
mController.getConfigurationListener().onThemeChanged();
- verify(view).setPrimaryTextColor(anyInt());
+ verify(mSmartspaceView, times(2)).setPrimaryTextColor(anyInt());
}
private void verifyAttachment(VerificationMode times) {
@@ -257,8 +253,9 @@
verify(mView, times).updateColors(mGradientColors);
}
- private static class TestView extends View implements BcSmartspaceDataPlugin.SmartspaceView {
- TestView(Context context, AttributeSet attrs) {
+ private static class SmartspaceView extends View
+ implements BcSmartspaceDataPlugin.SmartspaceView {
+ SmartspaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index d544f73..42cc1fa9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -49,7 +49,6 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.hardware.biometrics.BiometricManager;
-import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.face.FaceManager;
@@ -188,8 +187,7 @@
when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
doAnswer(invocation -> {
IBiometricEnabledOnKeyguardCallback callback = invocation.getArgument(0);
- callback.onChanged(BiometricSourceType.FACE, true /* enabled */,
- KeyguardUpdateMonitor.getCurrentUser());
+ callback.onChanged(true /* enabled */, KeyguardUpdateMonitor.getCurrentUser());
return null;
}).when(mBiometricManager).registerEnabledOnKeyguardCallback(any());
when(mFaceManager.isHardwareDetected()).thenReturn(true);
@@ -495,6 +493,11 @@
}
private void testFingerprintWhenStrongAuth(int strongAuth) {
+ // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
+ // will trigger updateBiometricListeningState();
+ clearInvocations(mFingerprintManager);
+ mKeyguardUpdateMonitor.resetBiometricListeningState();
+
when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
mTestableLooper.processAllMessages();
@@ -537,7 +540,7 @@
authCallback.onAuthenticationFailed();
// THEN aod interrupt is cancelled
- verify(mAuthController).onCancelAodInterrupt();
+ verify(mAuthController).onCancelUdfps();
}
@Test
@@ -557,7 +560,7 @@
authCallback.onAuthenticationError(0, "");
// THEN aod interrupt is cancelled
- verify(mAuthController).onCancelAodInterrupt();
+ verify(mAuthController).onCancelUdfps();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 59262cf..3252750 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -63,6 +63,7 @@
import com.android.systemui.R.dimen;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
@@ -94,6 +95,8 @@
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
private UserTracker mUserTracker;
+ @Mock
+ private PrivacyDotViewController mDotViewController;
@Before
public void setup() {
@@ -116,7 +119,7 @@
mContext.addMockSystemService(DisplayManager.class, mDisplayManager);
mScreenDecorations = spy(new ScreenDecorations(mContext, mMainHandler, mSecureSettings,
- mBroadcastDispatcher, mTunerService, mUserTracker) {
+ mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController) {
@Override
public void start() {
super.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 3f0831c..78c6717 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -233,9 +233,9 @@
TEST_UID, TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME,
AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);
assertEquals(2,
- mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size());
- assertEquals(1,
- mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID_OTHER)).size());
+ mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID), false).size());
+ assertEquals(1, mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID_OTHER),
+ false).size());
}
@Test
@@ -245,11 +245,11 @@
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true);
assertEquals(0, mController.getActiveAppOpsForUser(
- UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
+ UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE), false).size());
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
TEST_UID_NON_USER_SENSITIVE, SYSTEM_PKG, true);
assertEquals(0, mController.getActiveAppOpsForUser(
- UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
+ UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE), false).size());
}
@Test
@@ -441,7 +441,19 @@
}
@Test
- public void testOnlyRecordAudioPaused() {
+ public void testPausedPhoneCallMicrophoneFilteredOut() {
+ mController.addCallback(new int[]{AppOpsManager.OP_PHONE_CALL_MICROPHONE}, mCallback);
+ mTestableLooper.processAllMessages();
+
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_PHONE_CALL_MICROPHONE, TEST_UID, TEST_PACKAGE_NAME, true);
+ mTestableLooper.processAllMessages();
+
+ assertTrue(mController.getActiveAppOps().isEmpty());
+ }
+
+ @Test
+ public void testOnlyRecordAudioPhoneCallMicrophonePaused() {
mController.addCallback(new int[]{
AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_CAMERA
@@ -532,6 +544,40 @@
}
@Test
+ public void testPhoneCallMicrophoneFilteredWhenMicDisabled() {
+ mController.addCallback(
+ new int[]{AppOpsManager.OP_PHONE_CALL_MICROPHONE, AppOpsManager.OP_CAMERA},
+ mCallback);
+ mTestableLooper.processAllMessages();
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_PHONE_CALL_MICROPHONE, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+ mTestableLooper.processAllMessages();
+ List<AppOpItem> list = mController.getActiveAppOps();
+ assertEquals(1, list.size());
+ assertEquals(AppOpsManager.OP_PHONE_CALL_MICROPHONE, list.get(0).getCode());
+ assertFalse(list.get(0).isDisabled());
+
+ // Add a camera op, and disable the microphone. The camera op should be the only op returned
+ mController.onSensorBlockedChanged(MICROPHONE, true);
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+ mTestableLooper.processAllMessages();
+ list = mController.getActiveAppOps();
+ assertEquals(1, list.size());
+ assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode());
+
+
+ // Re enable the microphone, and verify the op returns
+ mController.onSensorBlockedChanged(MICROPHONE, false);
+ mTestableLooper.processAllMessages();
+
+ list = mController.getActiveAppOps();
+ assertEquals(2, list.size());
+ int micIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 1 : 0;
+ assertEquals(AppOpsManager.OP_PHONE_CALL_MICROPHONE, list.get(micIdx).getCode());
+ }
+
+ @Test
public void testCameraFilteredWhenCameraDisabled() {
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA},
mCallback);
@@ -563,6 +609,39 @@
assertEquals(AppOpsManager.OP_CAMERA, list.get(cameraIdx).getCode());
}
+ @Test
+ public void testPhoneCallCameraFilteredWhenCameraDisabled() {
+ mController.addCallback(
+ new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_PHONE_CALL_CAMERA},
+ mCallback);
+ mTestableLooper.processAllMessages();
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_PHONE_CALL_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+ mTestableLooper.processAllMessages();
+ List<AppOpItem> list = mController.getActiveAppOps();
+ assertEquals(1, list.size());
+ assertEquals(AppOpsManager.OP_PHONE_CALL_CAMERA, list.get(0).getCode());
+ assertFalse(list.get(0).isDisabled());
+
+ // Add an audio op, and disable the camera. The audio op should be the only op returned
+ mController.onSensorBlockedChanged(CAMERA, true);
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+ mTestableLooper.processAllMessages();
+ list = mController.getActiveAppOps();
+ assertEquals(1, list.size());
+ assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode());
+
+ // Re enable the camera, and verify the op returns
+ mController.onSensorBlockedChanged(CAMERA, false);
+ mTestableLooper.processAllMessages();
+
+ list = mController.getActiveAppOps();
+ assertEquals(2, list.size());
+ int cameraIdx = list.get(0).getCode() == AppOpsManager.OP_PHONE_CALL_CAMERA ? 0 : 1;
+ assertEquals(AppOpsManager.OP_PHONE_CALL_CAMERA, list.get(cameraIdx).getCode());
+ }
+
private class TestHandler extends AppOpsControllerImpl.H {
TestHandler(Looper looper) {
mController.super(looper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index bbd3ce8..0aa182f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -259,7 +259,7 @@
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
// WHEN it is cancelled
- mUdfpsController.onCancelAodInterrupt();
+ mUdfpsController.onCancelUdfps();
// THEN the illumination is hidden
verify(mUdfpsView).stopIllumination();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
index c912419..8e00d10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.classifier;
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE;
import static com.google.common.truth.Truth.assertThat;
@@ -103,4 +104,11 @@
assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 1).isFalse())
.isFalse();
}
+
+ @Test
+ public void testPass_QsSwipeAlwaysPasses() {
+ mClassifier.onTouchEvent(appendDownEvent(1, 1));
+ assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 1).isFalse())
+ .isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
index 32537b4..1d61e29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
@@ -22,6 +22,7 @@
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
import static com.android.systemui.classifier.Classifier.PULSE_EXPAND;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
import static com.android.systemui.classifier.Classifier.UNLOCK;
@@ -320,4 +321,46 @@
when(mDataProvider.isRight()).thenReturn(true);
assertThat(mClassifier.classifyGesture(BRIGHTNESS_SLIDER, 0.5, 0).isFalse()).isTrue();
}
+
+ @Test
+ public void testPass_QsSwipe() {
+ when(mDataProvider.isVertical()).thenReturn(false);
+
+ when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse();
+
+ when(mDataProvider.isUp()).thenReturn(false);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse();
+ }
+
+ @Test
+ public void testFalse_QsSwipe() {
+ when(mDataProvider.isVertical()).thenReturn(true);
+
+ when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue();
+
+ when(mDataProvider.isUp()).thenReturn(false);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
index 42e88b0..63ce98a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
@@ -22,6 +22,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import android.app.IWallpaperManager;
import android.os.PowerManager;
import android.testing.AndroidTestingRunner;
@@ -43,9 +44,12 @@
private WakefulnessLifecycle mWakefulness;
private WakefulnessLifecycle.Observer mWakefulnessObserver;
+ private IWallpaperManager mWallpaperManager;
+
@Before
public void setUp() throws Exception {
- mWakefulness = new WakefulnessLifecycle();
+ mWallpaperManager = mock(IWallpaperManager.class);
+ mWakefulness = new WakefulnessLifecycle(mContext, mWallpaperManager);
mWakefulnessObserver = mock(WakefulnessLifecycle.Observer.class);
mWakefulness.addObserver(mWakefulnessObserver);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index 0ce03ad..81ca4c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -22,8 +22,6 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
@@ -35,10 +33,7 @@
import android.app.INotificationManager;
import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
import android.app.Person;
-import android.app.people.ConversationChannel;
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
@@ -47,7 +42,6 @@
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -57,7 +51,6 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.ContactsContract;
-import android.service.notification.ConversationChannelWrapper;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.util.DisplayMetrics;
@@ -81,10 +74,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
@RunWith(AndroidTestingRunner.class)
@SmallTest
@@ -185,8 +176,6 @@
@Mock
private IPeopleManager mPeopleManager;
@Mock
- private LauncherApps mLauncherApps;
- @Mock
private IAppWidgetService mIAppWidgetService;
@Mock
private AppWidgetManager mAppWidgetManager;
@@ -239,84 +228,6 @@
}
@Test
- public void testGetRecentTilesReturnsSortedListWithOnlyRecentConversations() throws Exception {
- // Ensure the less-recent Important conversation is before more recent conversations.
- ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID_1, false, 3);
- ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID_1 + 1, true, 3);
- ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID_1 + 2,
- true, 1);
- when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
- new ParceledListSlice(Arrays.asList(
- newerNonImportantConversation, newerImportantConversation,
- olderImportantConversation)));
-
- // Ensure the non-Important conversation is sorted between these recent conversations.
- ConversationChannel recentConversationBeforeNonImportantConversation =
- getConversationChannel(
- SHORTCUT_ID_1 + 3, 4);
- ConversationChannel recentConversationAfterNonImportantConversation =
- getConversationChannel(SHORTCUT_ID_1 + 4,
- 2);
- when(mPeopleManager.getRecentConversations()).thenReturn(
- new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
- recentConversationBeforeNonImportantConversation)));
-
- List<String> orderedShortcutIds = PeopleSpaceUtils.getRecentTiles(
- mContext, mNotificationManager, mPeopleManager,
- mLauncherApps, mNotificationEntryManager)
- .stream().map(tile -> tile.getId()).collect(Collectors.toList());
-
- // Check for sorted recent conversations.
- assertThat(orderedShortcutIds).containsExactly(
- recentConversationBeforeNonImportantConversation.getShortcutInfo().getId(),
- newerNonImportantConversation.getShortcutInfo().getId(),
- recentConversationAfterNonImportantConversation.getShortcutInfo().getId())
- .inOrder();
- }
-
- @Test
- public void testGetPriorityTilesReturnsSortedListWithOnlyImportantConversations()
- throws Exception {
- // Ensure the less-recent Important conversation is before more recent conversations.
- ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID_1, false, 3);
- ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID_1 + 1, true, 3);
- ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
- SHORTCUT_ID_1 + 2,
- true, 1);
- when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
- new ParceledListSlice(Arrays.asList(
- newerNonImportantConversation, newerImportantConversation,
- olderImportantConversation)));
-
- // Ensure the non-Important conversation is sorted between these recent conversations.
- ConversationChannel recentConversationBeforeNonImportantConversation =
- getConversationChannel(
- SHORTCUT_ID_1 + 3, 4);
- ConversationChannel recentConversationAfterNonImportantConversation =
- getConversationChannel(SHORTCUT_ID_1 + 4,
- 2);
- when(mPeopleManager.getRecentConversations()).thenReturn(
- new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
- recentConversationBeforeNonImportantConversation)));
-
- List<String> orderedShortcutIds = PeopleSpaceUtils.getPriorityTiles(
- mContext, mNotificationManager, mPeopleManager,
- mLauncherApps, mNotificationEntryManager)
- .stream().map(tile -> tile.getId()).collect(Collectors.toList());
-
- // Check for sorted priority conversations.
- assertThat(orderedShortcutIds).containsExactly(
- newerImportantConversation.getShortcutInfo().getId(),
- olderImportantConversation.getShortcutInfo().getId())
- .inOrder();
- }
-
- @Test
public void testGetMessagingStyleMessagesNoMessage() {
Notification notification = new Notification.Builder(mContext, "test")
.setContentTitle("TEST_TITLE")
@@ -570,30 +481,4 @@
verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any());
}
-
- private ConversationChannelWrapper getConversationChannelWrapper(String shortcutId,
- boolean importantConversation, long lastInteractionTimestamp) throws Exception {
- ConversationChannelWrapper convo = new ConversationChannelWrapper();
- NotificationChannel notificationChannel = new NotificationChannel(shortcutId,
- "channel" + shortcutId,
- NotificationManager.IMPORTANCE_DEFAULT);
- notificationChannel.setImportantConversation(importantConversation);
- convo.setNotificationChannel(notificationChannel);
- convo.setShortcutInfo(new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
- "name").build());
- when(mPeopleManager.getLastInteraction(anyString(), anyInt(),
- eq(shortcutId))).thenReturn(lastInteractionTimestamp);
- return convo;
- }
-
- private ConversationChannel getConversationChannel(String shortcutId,
- long lastInteractionTimestamp) throws Exception {
- ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
- "name").build();
- ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
- lastInteractionTimestamp, false);
- when(mPeopleManager.getLastInteraction(anyString(), anyInt(),
- eq(shortcutId))).thenReturn(lastInteractionTimestamp);
- return convo;
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index 107ac83..3cc55f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -114,8 +114,6 @@
when(mMockContext.getString(R.string.birthday_status)).thenReturn(
mContext.getString(R.string.birthday_status));
- when(mMockContext.getString(R.string.basic_status)).thenReturn(
- mContext.getString(R.string.basic_status));
when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
when(mMockContext.getString(R.string.over_timestamp)).thenReturn(
mContext.getString(R.string.over_timestamp));
@@ -126,7 +124,6 @@
when(resources.getConfiguration()).thenReturn(configuration);
when(resources.getDisplayMetrics()).thenReturn(displayMetrics);
TextView textView = mock(TextView.class);
- // when(new TextView(mMockContext)).thenReturn(textView);
when(textView.getLineHeight()).thenReturn(16);
when(mPackageManager.getApplicationIcon(anyString())).thenReturn(null);
mPeopleTileViewHelper = new PeopleTileViewHelper(mContext,
@@ -134,16 +131,41 @@
}
@Test
- public void testCreateRemoteViewsWithLastInteractionTime() {
+ public void testCreateRemoteViewsWithLastInteractionTimeUnderOneDayHidden() {
RemoteViews views = new PeopleTileViewHelper(mContext,
PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
View result = views.apply(mContext, null);
+ // Not showing last interaction.
+ assertEquals(View.GONE, result.findViewById(R.id.last_interaction).getVisibility());
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_large));
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_height_for_large));
+ RemoteViews largeView = new PeopleTileViewHelper(mContext,
+ PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
+ View largeResult = largeView.apply(mContext, null);
+
+ // Not showing last interaction.
+ assertEquals(View.GONE, largeResult.findViewById(R.id.last_interaction).getVisibility());
+ }
+
+ @Test
+ public void testCreateRemoteViewsWithLastInteractionTime() {
+ PeopleSpaceTile tileWithLastInteraction =
+ PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setLastInteractionTimestamp(
+ 123445L).build();
+ RemoteViews views = new PeopleTileViewHelper(mContext,
+ tileWithLastInteraction, 0, mOptions).getViews();
+ View result = views.apply(mContext, null);
+
TextView name = (TextView) result.findViewById(R.id.name);
assertEquals(name.getText(), NAME);
// Has last interaction.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.last_interaction).getVisibility());
TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
- assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+ assertEquals(lastInteraction.getText(), "Over 2 weeks ago");
// No availability.
assertEquals(View.GONE, result.findViewById(R.id.availability).getVisibility());
// Shows person icon.
@@ -154,7 +176,7 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_medium) - 1);
RemoteViews smallView = new PeopleTileViewHelper(mContext,
- PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
+ tileWithLastInteraction, 0, mOptions).getViews();
View smallResult = smallView.apply(mContext, null);
// Show name over predefined icon.
@@ -171,14 +193,15 @@
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_height_for_large));
RemoteViews largeView = new PeopleTileViewHelper(mContext,
- PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
+ tileWithLastInteraction, 0, mOptions).getViews();
View largeResult = largeView.apply(mContext, null);
name = (TextView) largeResult.findViewById(R.id.name);
assertEquals(name.getText(), NAME);
// Has last interaction.
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.last_interaction).getVisibility());
lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
- assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+ assertEquals(lastInteraction.getText(), "Over 2 weeks ago");
// No availability.
assertEquals(View.GONE, result.findViewById(R.id.availability).getVisibility());
// Shows person icon.
@@ -202,8 +225,7 @@
TextView name = (TextView) result.findViewById(R.id.name);
assertEquals(name.getText(), NAME);
// Has last interaction over status.
- TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
- assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+ assertEquals(View.GONE, result.findViewById(R.id.last_interaction).getVisibility());
// Has availability.
assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
// Has person icon.
@@ -237,14 +259,13 @@
name = (TextView) largeResult.findViewById(R.id.name);
assertEquals(name.getText(), NAME);
// Has last interaction.
- lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
- assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+ assertEquals(View.GONE, largeResult.findViewById(R.id.last_interaction).getVisibility());
// Has availability.
- assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
// Shows person icon.
- assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.person_icon).getVisibility());
// No status.
- assertThat((View) result.findViewById(R.id.text_content)).isNull();
+ assertThat((View) largeResult.findViewById(R.id.text_content)).isNull();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
index 0ef3ca2..ccb40e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
@@ -29,6 +29,7 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
+import android.os.UserManager;
import android.service.notification.NotificationListenerService;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -83,6 +84,8 @@
private BubblesManager mBubblesManager;
@Mock
private NotificationListenerService.Ranking mRanking;
+ @Mock
+ private UserManager mUserManager;
@Captor
private ArgumentCaptor<NotificationVisibility> mNotificationVisibilityCaptor;
@@ -93,7 +96,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mActivity = new LaunchConversationActivity(mNotificationEntryManager,
- Optional.of(mBubblesManager));
+ Optional.of(mBubblesManager), mUserManager);
mActivity.setIsForTesting(true, mIStatusBarService);
mIntent = new Intent();
mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, "tile ID");
@@ -113,6 +116,7 @@
when(mNotifEntryCanBubble.canBubble()).thenReturn(true);
when(mNotifEntryNoRanking.getRanking()).thenReturn(null);
when(mRanking.getRank()).thenReturn(NOTIF_RANK);
+ when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
}
@Test
@@ -176,4 +180,18 @@
anyInt(), any(), anyInt(), anyInt(), any());
verify(mBubblesManager, times(1)).expandStackAndSelectBubble(eq(mNotifEntryCanBubble));
}
+
+ @Test
+ public void testQuietModeOpensQuietModeDialog() throws Exception {
+ mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY,
+ NOTIF_KEY);
+ when(mUserManager.isQuietModeEnabled(eq(USER_HANDLE))).thenReturn(true);
+ mActivity.setIntent(mIntent);
+ mActivity.onCreate(new Bundle());
+
+ assertThat(mActivity.isFinishing()).isTrue();
+ verify(mIStatusBarService, never()).onNotificationClear(any(),
+ anyInt(), any(), anyInt(), anyInt(), any());
+ verify(mBubblesManager, never()).expandStackAndSelectBubble(any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 7125500..1ab5d34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -35,7 +35,9 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -45,8 +47,10 @@
import static java.util.Objects.requireNonNull;
+import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.app.Person;
import android.app.people.ConversationChannel;
import android.app.people.ConversationStatus;
@@ -59,11 +63,14 @@
import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.service.notification.ConversationChannelWrapper;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
@@ -95,6 +102,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -169,6 +177,10 @@
private NotificationEntryManager mNotificationEntryManager;
@Mock
private PackageManager mPackageManager;
+ @Mock
+ private INotificationManager mNotificationManager;
+ @Mock
+ private UserManager mUserManager;
@Captor
private ArgumentCaptor<NotificationHandler> mListenerCaptor;
@@ -189,7 +201,8 @@
mProvider = new PeopleSpaceWidgetProvider();
mProvider.setPeopleSpaceWidgetManager(mManager);
mManager.setAppWidgetManager(mAppWidgetManager, mIPeopleManager, mPeopleManager,
- mLauncherApps, mNotificationEntryManager, mPackageManager, true, mProvider);
+ mLauncherApps, mNotificationEntryManager, mPackageManager, true, mProvider,
+ mUserManager, mNotificationManager);
mManager.attach(mListenerService);
verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
@@ -201,6 +214,98 @@
addTileForWidget(PERSON_TILE_WITH_SAME_URI, WIDGET_ID_WITH_SAME_URI);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
.thenReturn(new Bundle());
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
+ }
+
+ @Test
+ public void testGetRecentTilesReturnsSortedListWithOnlyRecentConversations() throws Exception {
+ // Ensure the less-recent Important conversation is before more recent conversations.
+ ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
+ SHORTCUT_ID, false, 3);
+ ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
+ SHORTCUT_ID + 1, true, 3);
+ ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
+ SHORTCUT_ID + 2,
+ true, 1);
+ when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
+ new ParceledListSlice(Arrays.asList(
+ newerNonImportantConversation, newerImportantConversation,
+ olderImportantConversation)));
+
+ // Ensure the non-Important conversation is sorted between these recent conversations.
+ ConversationChannel recentConversationBeforeNonImportantConversation =
+ getConversationChannel(
+ SHORTCUT_ID + 3, 4);
+ ConversationChannel recentConversationAfterNonImportantConversation =
+ getConversationChannel(SHORTCUT_ID + 4,
+ 2);
+ when(mIPeopleManager.getRecentConversations()).thenReturn(
+ new ParceledListSlice(Arrays.asList(recentConversationAfterNonImportantConversation,
+ recentConversationBeforeNonImportantConversation)));
+
+ List<String> orderedShortcutIds = mManager.getRecentTiles()
+ .stream().map(tile -> tile.getId()).collect(Collectors.toList());
+
+ // Check for sorted recent conversations.
+ assertThat(orderedShortcutIds).containsExactly(
+ recentConversationBeforeNonImportantConversation.getShortcutInfo().getId(),
+ newerNonImportantConversation.getShortcutInfo().getId(),
+ recentConversationAfterNonImportantConversation.getShortcutInfo().getId())
+ .inOrder();
+ }
+
+ @Test
+ public void testGetPriorityTilesReturnsSortedListWithOnlyImportantConversations()
+ throws Exception {
+ // Ensure the less-recent Important conversation is before more recent conversations.
+ ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
+ SHORTCUT_ID, false, 3);
+ ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
+ SHORTCUT_ID + 1, true, 3);
+ ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
+ SHORTCUT_ID + 2,
+ true, 1);
+ when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
+ new ParceledListSlice(Arrays.asList(
+ newerNonImportantConversation, newerImportantConversation,
+ olderImportantConversation)));
+
+ List<String> orderedShortcutIds = mManager.getPriorityTiles()
+ .stream().map(tile -> tile.getId()).collect(Collectors.toList());
+
+ // Check for sorted priority conversations.
+ assertThat(orderedShortcutIds).containsExactly(
+ newerImportantConversation.getShortcutInfo().getId(),
+ olderImportantConversation.getShortcutInfo().getId())
+ .inOrder();
+ }
+
+ @Test
+ public void testGetTilesReturnsNothingInQuietMode()
+ throws Exception {
+ // Ensure the less-recent Important conversation is before more recent conversations.
+ ConversationChannelWrapper newerNonImportantConversation = getConversationChannelWrapper(
+ SHORTCUT_ID, false, 3);
+ ConversationChannelWrapper newerImportantConversation = getConversationChannelWrapper(
+ SHORTCUT_ID + 1, true, 3);
+ ConversationChannelWrapper olderImportantConversation = getConversationChannelWrapper(
+ SHORTCUT_ID + 2,
+ true, 1);
+ when(mNotificationManager.getConversations(anyBoolean())).thenReturn(
+ new ParceledListSlice(Arrays.asList(
+ newerNonImportantConversation, newerImportantConversation,
+ olderImportantConversation)));
+ ConversationChannel recentConversation =
+ getConversationChannel(
+ SHORTCUT_ID + 3, 4);
+ when(mIPeopleManager.getRecentConversations()).thenReturn(
+ new ParceledListSlice(Arrays.asList(recentConversation)));
+
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
+
+ // Check nothing returned.
+ assertThat(mManager.getPriorityTiles()).isEmpty();
+ assertThat(mManager.getRecentTiles()).isEmpty();
}
@Test
@@ -1038,7 +1143,7 @@
when(mAppWidgetManager.requestPinAppWidget(any(), any(), any())).thenReturn(true);
ShortcutInfo info = new ShortcutInfo.Builder(mMockContext, SHORTCUT_ID).build();
- boolean valid = mManager.requestPinAppWidget(info);
+ boolean valid = mManager.requestPinAppWidget(info, new Bundle());
assertThat(valid).isTrue();
verify(mAppWidgetManager, times(1)).requestPinAppWidget(
@@ -1052,7 +1157,7 @@
when(mIPeopleManager.getConversation(PACKAGE_NAME, 0, SHORTCUT_ID)).thenReturn(null);
ShortcutInfo info = new ShortcutInfo.Builder(mMockContext, SHORTCUT_ID).build();
- boolean valid = mManager.requestPinAppWidget(info);
+ boolean valid = mManager.requestPinAppWidget(info, new Bundle());
assertThat(valid).isFalse();
verify(mAppWidgetManager, never()).requestPinAppWidget(any(), any(), any());
@@ -1208,4 +1313,30 @@
editor.putStringSet(contactUri.toString(), storedWidgetIdsByUri);
editor.apply();
}
+
+ private ConversationChannelWrapper getConversationChannelWrapper(String shortcutId,
+ boolean importantConversation, long lastInteractionTimestamp) throws Exception {
+ ConversationChannelWrapper convo = new ConversationChannelWrapper();
+ NotificationChannel notificationChannel = new NotificationChannel(shortcutId,
+ "channel" + shortcutId,
+ NotificationManager.IMPORTANCE_DEFAULT);
+ notificationChannel.setImportantConversation(importantConversation);
+ convo.setNotificationChannel(notificationChannel);
+ convo.setShortcutInfo(new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
+ "name").build());
+ when(mIPeopleManager.getLastInteraction(anyString(), anyInt(),
+ eq(shortcutId))).thenReturn(lastInteractionTimestamp);
+ return convo;
+ }
+
+ private ConversationChannel getConversationChannel(String shortcutId,
+ long lastInteractionTimestamp) throws Exception {
+ ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
+ "name").build();
+ ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
+ lastInteractionTimestamp, false);
+ when(mIPeopleManager.getLastInteraction(anyString(), anyInt(),
+ eq(shortcutId))).thenReturn(lastInteractionTimestamp);
+ return convo;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
index 791dd12..05a1e4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -28,6 +28,7 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpsController
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.settings.UserTracker
@@ -43,6 +44,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Captor
@@ -86,6 +88,8 @@
private lateinit var privacyLogger: PrivacyLogger
@Mock
private lateinit var keyguardStateController: KeyguardStateController
+ @Mock
+ private lateinit var appOpsController: AppOpsController
@Captor
private lateinit var dialogDismissedCaptor: ArgumentCaptor<PrivacyDialog.OnDialogDismissed>
@Captor
@@ -131,6 +135,7 @@
uiExecutor,
privacyLogger,
keyguardStateController,
+ appOpsController,
dialogProvider
)
}
@@ -143,18 +148,27 @@
}
@Test
+ fun testMicMutedParameter() {
+ `when`(appOpsController.isMicMuted).thenReturn(true)
+ controller.showDialog(context)
+ backgroundExecutor.runAllReady()
+
+ verify(permissionManager).getIndicatorAppOpUsageData(true)
+ }
+
+ @Test
fun testPermissionManagerOnlyCalledInBackgroundThread() {
controller.showDialog(context)
- verify(permissionManager, never()).indicatorAppOpUsageData
+ verify(permissionManager, never()).getIndicatorAppOpUsageData(anyBoolean())
backgroundExecutor.runAllReady()
- verify(permissionManager).indicatorAppOpUsageData
+ verify(permissionManager).getIndicatorAppOpUsageData(anyBoolean())
}
@Test
fun testPackageManagerOnlyCalledInBackgroundThread() {
val usage = createMockPermGroupUsage()
`when`(usage.isPhoneCall).thenReturn(false)
- `when`(permissionManager.indicatorAppOpUsageData).thenReturn(listOf(usage))
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
controller.showDialog(context)
verify(packageManager, never()).getApplicationInfoAsUser(anyString(), anyInt(), anyInt())
@@ -217,7 +231,7 @@
isPhoneCall = false,
attribution = TEST_ATTRIBUTION
)
- `when`(permissionManager.indicatorAppOpUsageData).thenReturn(listOf(usage))
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
controller.showDialog(context)
exhaustExecutors()
@@ -246,7 +260,7 @@
packageName = "${TEST_PACKAGE_NAME}_microphone",
permGroupName = PERM_MICROPHONE
)
- `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
listOf(usage_microphone, usage_camera)
)
@@ -269,7 +283,7 @@
packageName = "${TEST_PACKAGE_NAME}_recent",
isActive = false
)
- `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
listOf(usage_recent, usage_active)
)
@@ -292,7 +306,7 @@
isActive = true,
lastAccess = 1L
)
- `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
listOf(usage_active, usage_active_moreRecent)
)
controller.showDialog(context)
@@ -319,7 +333,7 @@
isActive = false,
lastAccess = 2L
)
- `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
listOf(usage_recent, usage_mostRecent, usage_moreRecent)
)
@@ -342,7 +356,7 @@
permGroupName = PERM_LOCATION
)
- `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
listOf(usage_camera, usage_location, usage_microphone)
)
`when`(privacyItemController.micCameraAvailable).thenReturn(false)
@@ -366,7 +380,7 @@
permGroupName = PERM_LOCATION
)
- `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
listOf(usage_camera, usage_location, usage_microphone)
)
`when`(privacyItemController.locationAvailable).thenReturn(false)
@@ -392,7 +406,7 @@
permGroupName = PERM_LOCATION
)
- `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
listOf(usage_camera, usage_location, usage_microphone)
)
`when`(privacyItemController.micCameraAvailable).thenReturn(true)
@@ -416,7 +430,7 @@
permGroupName = PERM_LOCATION
)
- `when`(permissionManager.indicatorAppOpUsageData).thenReturn(
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
listOf(usage_camera, usage_location, usage_microphone)
)
`when`(privacyItemController.micCameraAvailable).thenReturn(false)
@@ -433,7 +447,8 @@
val usage_enterprise = createMockPermGroupUsage(
uid = generateUidForUser(ENT_USER_ID)
)
- `when`(permissionManager.indicatorAppOpUsageData).thenReturn(listOf(usage_enterprise))
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_enterprise))
controller.showDialog(context)
exhaustExecutors()
@@ -446,7 +461,8 @@
val usage_other = createMockPermGroupUsage(
uid = generateUidForUser(ENT_USER_ID + 1)
)
- `when`(permissionManager.indicatorAppOpUsageData).thenReturn(listOf(usage_other))
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean()))
+ .thenReturn(listOf(usage_other))
controller.showDialog(context)
exhaustExecutors()
@@ -514,7 +530,8 @@
}
private fun setUpDefaultMockResponses() {
- `when`(permissionManager.indicatorAppOpUsageData).thenReturn(emptyList())
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(emptyList())
+ `when`(appOpsController.isMicMuted).thenReturn(false)
`when`(packageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
.thenAnswer {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index bba1c6a..e4d7b1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -46,7 +46,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyList
import org.mockito.Captor
import org.mockito.Mock
@@ -156,7 +156,7 @@
fun testDistinctItems() {
doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0)))
- .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ .`when`(appOpsController).getActiveAppOps(anyBoolean())
privacyItemController.addCallback(callback)
executor.runAllReady()
@@ -168,7 +168,7 @@
fun testSimilarItemsDifferentTimeStamp() {
doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
- .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ .`when`(appOpsController).getActiveAppOps(anyBoolean())
privacyItemController.addCallback(callback)
executor.runAllReady()
@@ -215,7 +215,7 @@
@Test
fun testMultipleCallbacksAreUpdated() {
- doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
val otherCallback = mock(PrivacyItemController.Callback::class.java)
privacyItemController.addCallback(callback)
@@ -233,7 +233,7 @@
@Test
fun testRemoveCallback() {
- doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
val otherCallback = mock(PrivacyItemController.Callback::class.java)
privacyItemController.addCallback(callback)
privacyItemController.addCallback(otherCallback)
@@ -254,7 +254,7 @@
fun testListShouldNotHaveNull() {
doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0),
AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
- .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ .`when`(appOpsController).getActiveAppOps(anyBoolean())
privacyItemController.addCallback(callback)
executor.runAllReady()
executor.runAllReady()
@@ -292,7 +292,7 @@
doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
- .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ .`when`(appOpsController).getActiveAppOps(anyBoolean())
privacyItemController.addCallback(callback)
executor.runAllReady()
@@ -306,7 +306,7 @@
@Test
fun testNotUpdated_LocationChangeWhenOnlyMicCamera() {
doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
- .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ .`when`(appOpsController).getActiveAppOps(anyBoolean())
privacyItemController.addCallback(callback)
changeLocation(false)
@@ -338,7 +338,7 @@
fun testLogListUpdated() {
doReturn(listOf(
AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0))
- ).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ ).`when`(appOpsController).getActiveAppOps(anyBoolean())
privacyItemController.addCallback(callback)
executor.runAllReady()
@@ -362,10 +362,10 @@
}
@Test
- fun testListRequestedForAllUsers() {
+ fun testListRequestedShowPaused() {
privacyItemController.addCallback(callback)
executor.runAllReady()
- verify(appOpsController).getActiveAppOpsForUser(UserHandle.USER_ALL)
+ verify(appOpsController).getActiveAppOps(true)
}
@Test
@@ -377,7 +377,7 @@
doReturn(listOf(
AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0),
AppOpItem(AppOpsManager.OP_CAMERA, otherUserUid, TEST_PACKAGE_NAME, 0))
- ).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ ).`when`(appOpsController).getActiveAppOps(anyBoolean())
privacyItemController.userTrackerCallback.onUserChanged(otherUser, mContext)
executor.runAllReady()
@@ -401,7 +401,7 @@
AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0),
AppOpItem(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, 0),
AppOpItem(AppOpsManager.OP_PHONE_CALL_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0))
- ).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ ).`when`(appOpsController).getActiveAppOps(anyBoolean())
privacyItemController.userTrackerCallback.onUserChanged(otherUser, mContext)
executor.runAllReady()
@@ -424,7 +424,7 @@
AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, 0),
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, 0),
AppOpItem(AppOpsManager.OP_PHONE_CALL_MICROPHONE, TEST_UID, TEST_PACKAGE_NAME, 0))
- ).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ ).`when`(appOpsController).getActiveAppOps(anyBoolean())
privacyItemController.userTrackerCallback.onUserChanged(otherUser, mContext)
executor.runAllReady()
@@ -442,7 +442,7 @@
fun testPassageOfTimeDoesNotRemoveIndicators() {
doReturn(listOf(
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
- )).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ )).`when`(appOpsController).getActiveAppOps(anyBoolean())
privacyItemController.addCallback(callback)
@@ -458,12 +458,12 @@
// Start with some element at time 0
doReturn(listOf(
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
- )).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ )).`when`(appOpsController).getActiveAppOps(anyBoolean())
privacyItemController.addCallback(callback)
executor.runAllReady()
// Then remove it at time HOLD + 1
- doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS + 1)
verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
@@ -481,12 +481,12 @@
// Start with some element at time 0
doReturn(listOf(
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
- )).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ )).`when`(appOpsController).getActiveAppOps(anyBoolean())
privacyItemController.addCallback(callback)
executor.runAllReady()
// Then remove it at time HOLD - 1
- doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS - 1)
verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
@@ -504,12 +504,12 @@
// Start with some element at time 0
doReturn(listOf(
AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, elapsedTime)
- )).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ )).`when`(appOpsController).getActiveAppOps(anyBoolean())
privacyItemController.addCallback(callback)
executor.runAllReady()
// Then remove it at time HOLD - 1
- doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+ doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOps(anyBoolean())
fakeClock.advanceTime(PrivacyItemController.TIME_TO_HOLD_INDICATORS - 1)
verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
@@ -525,6 +525,30 @@
assertTrue(privacyItemController.privacyList.isEmpty())
}
+ @Test
+ fun testPausedElementsAreRemoved() {
+ val item = AppOpItem(
+ AppOpsManager.OP_RECORD_AUDIO,
+ TEST_UID,
+ TEST_PACKAGE_NAME,
+ elapsedTime
+ )
+ `when`(appOpsController.getActiveAppOps(anyBoolean())).thenReturn(listOf(item))
+ privacyItemController.addCallback(callback)
+ executor.runAllReady()
+
+ item.isDisabled = true
+ fakeClock.advanceTime(1)
+ verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+ argCaptorCallback.value.onActiveStateChanged(
+ AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, false)
+
+ executor.runAllReady()
+
+ verify(callback).onPrivacyItemsChanged(emptyList())
+ assertTrue(privacyItemController.privacyList.isEmpty())
+ }
+
private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
private fun changeLocation(value: Boolean?) = changeProperty(LOCATION_INDICATOR, value)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index bacc493..53eae8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -32,6 +32,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTileView;
@@ -94,6 +95,7 @@
QSTileView mQSTileView;
@Mock
PagedTileLayout mPagedTileLayout;
+ FalsingManagerFake mFalsingManager = new FalsingManagerFake();
@Mock
FeatureFlags mFeatureFlags;
@@ -121,7 +123,7 @@
mQSTileHost, mQSCustomizerController, true, mMediaHost,
mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
- mFeatureFlags
+ mFalsingManager, mFeatureFlags
);
mController.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index acedf59..4f88599 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -81,7 +81,7 @@
mTestableLooper.runWithLooper(() -> {
mQsPanel = new QSPanel(mContext, null);
- mQsPanel.initialize(false);
+ mQsPanel.initialize();
mQsPanel.onFinishInflate();
// Provides a parent with non-zero size for QSPanel
mParentView = new FrameLayout(mContext);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
index 62cc9b7..3d53062 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
@@ -49,7 +49,7 @@
MockitoAnnotations.initMocks(this);
TestableLooper.get(this).runWithLooper(() -> mTileAdapter =
- new TileAdapter(mContext, mQSTileHost, new UiEventLoggerFake(), /* qsFlag */false));
+ new TileAdapter(mContext, mQSTileHost, new UiEventLoggerFake()));
}
@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 d236023..4bba0d0 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
@@ -233,6 +233,11 @@
}
@Test
+ public void testGetTileLabel() {
+ assertEquals(mContext.getString(R.string.wallet_title), mTile.getTileLabel().toString());
+ }
+
+ @Test
public void testHandleUpdateState_hasCard_deviceLocked_tileInactive() {
when(mKeyguardStateController.isUnlocked()).thenReturn(false);
QSTile.State state = new QSTile.State();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index 03f93fa..8c7d762 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -82,7 +82,7 @@
when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
ScreenshotNotificationSmartActionsProvider smartActionsProvider = mock(
ScreenshotNotificationSmartActionsProvider.class);
- when(smartActionsProvider.getActions(any(), any(), any(), any(), any()))
+ when(smartActionsProvider.getActions(any(), any(), any(), any(), any(), any()))
.thenThrow(RuntimeException.class);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
mScreenshotSmartActions.getSmartActionsFuture(
@@ -128,7 +128,7 @@
mScreenshotSmartActions.getSmartActionsFuture(
"", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider,
true, UserHandle.of(UserHandle.myUserId()));
- verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any());
+ verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any(), any());
assertNotNull(smartActionsFuture);
List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
assertEquals(Collections.emptyList(), smartActions);
@@ -142,7 +142,8 @@
mScreenshotSmartActions.getSmartActionsFuture(
"", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, true,
UserHandle.of(UserHandle.myUserId()));
- verify(mSmartActionsProvider, times(1)).getActions(any(), any(), any(), any(), any());
+ verify(mSmartActionsProvider, times(1)).getActions(
+ any(), any(), any(), any(), any(), any());
}
// Tests for a hardware bitmap, a completed future is returned.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java
index 87a7757..c2e58ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java
@@ -19,6 +19,7 @@
import static junit.framework.Assert.assertEquals;
import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.testing.AndroidTestingRunner;
@@ -89,4 +90,17 @@
mView.setTint(tint);
assertEquals(mView.getTint(), tint);
}
+
+ @Test
+ public void setDrawableBounds_propagatesToDrawable() {
+ ColorDrawable drawable = new ColorDrawable();
+ Rect expectedBounds = new Rect(100, 100, 100, 100);
+ mView.setDrawable(drawable);
+ mView.setDrawableBounds(100, 100, 100, 100);
+
+ assertEquals(expectedBounds, drawable.getBounds());
+ // set bounds that are different from expected drawable bounds
+ mView.onLayout(true, 200, 200, 200, 200);
+ assertEquals(expectedBounds, drawable.getBounds());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 12e341a5..5d29f52 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -31,7 +31,6 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
@@ -239,7 +238,7 @@
when(mBuilder.build()).thenReturn(mock(PriorityOnboardingDialogController.class));
- when(mPeopleSpaceWidgetManager.requestPinAppWidget(any())).thenReturn(true);
+ when(mPeopleSpaceWidgetManager.requestPinAppWidget(any(), any())).thenReturn(true);
}
@Test
@@ -1289,7 +1288,7 @@
mNotificationInfo.findViewById(R.id.done).performClick();
// THEN the user is presented with the People Tile pinning request
- verify(mPeopleSpaceWidgetManager, times(1)).requestPinAppWidget(any());
+ verify(mPeopleSpaceWidgetManager, times(1)).requestPinAppWidget(any(), any());
}
@Test
@@ -1325,7 +1324,7 @@
mNotificationInfo.findViewById(R.id.done).performClick();
// THEN the user is not presented with the People Tile pinning request
- verify(mPeopleSpaceWidgetManager, never()).requestPinAppWidget(mShortcutInfo);
+ verify(mPeopleSpaceWidgetManager, never()).requestPinAppWidget(eq(mShortcutInfo), any());
}
@Test
@@ -1364,6 +1363,6 @@
mNotificationInfo.findViewById(R.id.done).performClick();
// THEN the user is not presented with the People Tile pinning request
- verify(mPeopleSpaceWidgetManager, never()).requestPinAppWidget(mShortcutInfo);
+ verify(mPeopleSpaceWidgetManager, never()).requestPinAppWidget(eq(mShortcutInfo), any());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 67fd5eb..0e3e0cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -37,6 +37,8 @@
import com.android.systemui.R;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
@@ -58,6 +60,8 @@
private View mCenteredNotificationAreaView;
private StatusBarStateController mStatusBarStateController;
private OngoingCallController mOngoingCallController;
+ private SystemStatusAnimationScheduler mAnimationScheduler;
+ private PrivacyDotViewController mDotViewController;
public CollapsedStatusBarFragmentTest() {
super(CollapsedStatusBarFragment.class);
@@ -180,7 +184,7 @@
Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture());
OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue());
- when(mOngoingCallController.getHasOngoingCall()).thenReturn(true);
+ when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
listener.onOngoingCallStarted(/* animate= */ false);
assertEquals(View.VISIBLE,
@@ -201,7 +205,7 @@
Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture());
OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue());
- when(mOngoingCallController.getHasOngoingCall()).thenReturn(false);
+ when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
listener.onOngoingCallEnded(/* animate= */ false);
assertEquals(View.GONE,
@@ -212,6 +216,11 @@
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
mOngoingCallController = mock(OngoingCallController.class);
- return new CollapsedStatusBarFragment(mOngoingCallController);
+ mAnimationScheduler = mock(SystemStatusAnimationScheduler.class);
+ mDotViewController = mock(PrivacyDotViewController.class);
+ return new CollapsedStatusBarFragment(
+ mOngoingCallController,
+ mAnimationScheduler,
+ mDotViewController);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 08d6d2d..11f96c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -38,6 +38,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.IWallpaperManager;
import android.app.Notification;
import android.app.StatusBarManager;
import android.app.trust.TrustManager;
@@ -113,6 +114,8 @@
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.charging.WiredChargingRippleController;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -263,8 +266,11 @@
@Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
@Mock private WiredChargingRippleController mWiredChargingRippleController;
@Mock private OngoingCallController mOngoingCallController;
+ @Mock private SystemStatusAnimationScheduler mAnimationScheduler;
+ @Mock private PrivacyDotViewController mDotViewController;
@Mock private TunerService mTunerService;
@Mock private FeatureFlags mFeatureFlags;
+ @Mock private IWallpaperManager mWallpaperManager;
private ShadeController mShadeController;
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private InitController mInitController = new InitController();
@@ -323,7 +329,8 @@
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
- WakefulnessLifecycle wakefulnessLifecycle = new WakefulnessLifecycle();
+ WakefulnessLifecycle wakefulnessLifecycle =
+ new WakefulnessLifecycle(mContext, mWallpaperManager);
wakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
wakefulnessLifecycle.dispatchFinishedWakingUp();
@@ -429,6 +436,8 @@
mBrightnessSliderFactory,
mWiredChargingRippleController,
mOngoingCallController,
+ mAnimationScheduler,
+ mDotViewController,
mTunerService,
mFeatureFlags);
when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
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 d87d1d1..c244290 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
@@ -19,14 +19,16 @@
import android.app.Notification
import android.app.PendingIntent
import android.app.Person
+import android.content.Intent
import android.service.notification.NotificationListenerService.REASON_USER_STOPPED
-import androidx.test.filters.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.widget.LinearLayout
+import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -43,6 +45,7 @@
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@@ -55,6 +58,7 @@
private lateinit var notifCollectionListener: NotifCollectionListener
@Mock private lateinit var mockOngoingCallListener: OngoingCallListener
+ @Mock private lateinit var mockActivityStarter: ActivityStarter
private lateinit var chipView: LinearLayout
@@ -71,7 +75,8 @@
`when`(featureFlags.isOngoingCallStatusBarChipEnabled).thenReturn(true)
val notificationCollection = mock(CommonNotifCollection::class.java)
- controller = OngoingCallController(notificationCollection, featureFlags, FakeSystemClock())
+ controller = OngoingCallController(
+ notificationCollection, featureFlags, FakeSystemClock(), mockActivityStarter)
controller.init()
controller.addCallback(mockOngoingCallListener)
controller.setChipView(chipView)
@@ -111,22 +116,24 @@
@Test
fun hasOngoingCall_noOngoingCallNotifSent_returnsFalse() {
- assertThat(controller.hasOngoingCall).isFalse()
+ assertThat(controller.hasOngoingCall()).isFalse()
}
@Test
fun hasOngoingCall_ongoingCallNotifSentAndChipViewSet_returnsTrue() {
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- assertThat(controller.hasOngoingCall).isTrue()
+ assertThat(controller.hasOngoingCall()).isTrue()
}
@Test
- fun hasOngoingCall_ongoingCallNotifSentButNoChipView_returnsFalse() {
- controller.setChipView(null)
+ fun hasOngoingCall_ongoingCallNotifSentButInvalidChipView_returnsFalse() {
+ val invalidChipView = LinearLayout(context)
+ controller.setChipView(invalidChipView)
+
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- assertThat(controller.hasOngoingCall).isFalse()
+ assertThat(controller.hasOngoingCall()).isFalse()
}
@Test
@@ -136,12 +143,42 @@
notifCollectionListener.onEntryUpdated(ongoingCallNotifEntry)
notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
- assertThat(controller.hasOngoingCall).isFalse()
+ assertThat(controller.hasOngoingCall()).isFalse()
+ }
+
+ /**
+ * This test fakes a theme change during an ongoing call.
+ *
+ * When a theme change happens, [CollapsedStatusBarFragment] and its views get re-created, so
+ * [OngoingCallController.setChipView] gets called with a new view. If there's an active ongoing
+ * call when the theme changes, the new view needs to be updated with the call information.
+ */
+ @Test
+ fun setChipView_whenHasOngoingCallIsTrue_listenerNotifiedWithNewView() {
+ // Start an ongoing call.
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+ lateinit var newChipView: LinearLayout
+ TestableLooper.get(this).runWithLooper {
+ newChipView = LayoutInflater.from(mContext)
+ .inflate(R.layout.ongoing_call_chip, null) as LinearLayout
+ }
+
+ // Change the chip view associated with the controller.
+ controller.setChipView(newChipView)
+
+ // Verify the listener was notified once for the initial call and again when the new view
+ // was set.
+ verify(mockOngoingCallListener, times(2)).onOngoingCallStarted(anyBoolean())
}
private fun createOngoingCallNotifEntry(): NotificationEntry {
val notificationEntryBuilder = NotificationEntryBuilder()
notificationEntryBuilder.modifyNotification(context).style = ongoingCallStyle
+
+ val contentIntent = mock(PendingIntent::class.java)
+ `when`(contentIntent.intent).thenReturn(mock(Intent::class.java))
+ notificationEntryBuilder.modifyNotification(context).setContentIntent(contentIntent)
return notificationEntryBuilder.build()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 4471778..40439a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -36,7 +36,6 @@
import android.app.Instrumentation;
import android.net.ConnectivityManager;
-import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
@@ -323,34 +322,37 @@
public void setConnectivityViaCallbackInNetworkControllerForVcn(
int networkType, boolean validated, boolean isConnected, VcnTransportInfo info) {
- mNetCapabilities.setTransportInfo(info);
- setConnectivityCommon(networkType, validated, isConnected);
- mDefaultCallbackInNetworkController.onCapabilitiesChanged(
- mNetwork, new NetworkCapabilities(mNetCapabilities));
+ final NetworkCapabilities.Builder builder =
+ new NetworkCapabilities.Builder(mNetCapabilities);
+ builder.setTransportInfo(info);
+ setConnectivityCommon(builder, networkType, validated, isConnected);
+ mDefaultCallbackInNetworkController.onCapabilitiesChanged(mNetwork, builder.build());
}
public void setConnectivityViaCallbackInNetworkController(
int networkType, boolean validated, boolean isConnected, WifiInfo wifiInfo) {
+ final NetworkCapabilities.Builder builder =
+ new NetworkCapabilities.Builder(mNetCapabilities);
if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
- mNetCapabilities.setTransportInfo(wifiInfo);
+ builder.setTransportInfo(wifiInfo);
}
- setConnectivityCommon(networkType, validated, isConnected);
- mDefaultCallbackInNetworkController.onCapabilitiesChanged(
- mNetwork, new NetworkCapabilities(mNetCapabilities));
+ setConnectivityCommon(builder, networkType, validated, isConnected);
+ mDefaultCallbackInNetworkController.onCapabilitiesChanged(mNetwork, builder.build());
}
public void setConnectivityViaCallbackInWifiTracker(
int networkType, boolean validated, boolean isConnected, WifiInfo wifiInfo) {
+ final NetworkCapabilities.Builder builder =
+ new NetworkCapabilities.Builder(mNetCapabilities);
if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
- mNetCapabilities.setTransportInfo(wifiInfo);
+ builder.setTransportInfo(wifiInfo);
}
- setConnectivityCommon(networkType, validated, isConnected);
+ setConnectivityCommon(builder, networkType, validated, isConnected);
if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
if (isConnected) {
- mNetworkCallback.onAvailable(mNetwork,
- new NetworkCapabilities(mNetCapabilities), new LinkProperties(), false);
- mNetworkCallback.onCapabilitiesChanged(
- mNetwork, new NetworkCapabilities(mNetCapabilities));
+ final NetworkCapabilities newCap = builder.build();
+ mNetworkCallback.onAvailable(mNetwork);
+ mNetworkCallback.onCapabilitiesChanged(mNetwork, newCap);
} else {
mNetworkCallback.onLost(mNetwork);
}
@@ -359,16 +361,16 @@
public void setConnectivityViaCallbackInWifiTrackerForVcn(
int networkType, boolean validated, boolean isConnected, VcnTransportInfo info) {
- mNetCapabilities.setTransportInfo(info);
- setConnectivityCommon(networkType, validated, isConnected);
+ final NetworkCapabilities.Builder builder =
+ new NetworkCapabilities.Builder(mNetCapabilities);
+ builder.setTransportInfo(info);
+ setConnectivityCommon(builder, networkType, validated, isConnected);
if (networkType == NetworkCapabilities.TRANSPORT_CELLULAR) {
if (isConnected) {
- mNetworkCallback.onAvailable(mNetwork,
- new NetworkCapabilities(mNetCapabilities), new LinkProperties(), false);
- mNetworkCallback.onCapabilitiesChanged(
- mNetwork, new NetworkCapabilities(mNetCapabilities));
- mDefaultCallbackInWifiTracker.onCapabilitiesChanged(
- mNetwork, new NetworkCapabilities(mNetCapabilities));
+ final NetworkCapabilities newCap = builder.build();
+ mNetworkCallback.onAvailable(mNetwork);
+ mNetworkCallback.onCapabilitiesChanged(mNetwork, newCap);
+ mDefaultCallbackInWifiTracker.onCapabilitiesChanged(mNetwork, newCap);
} else {
mNetworkCallback.onLost(mNetwork);
}
@@ -377,26 +379,28 @@
public void setConnectivityViaDefaultCallbackInWifiTracker(
int networkType, boolean validated, boolean isConnected, WifiInfo wifiInfo) {
+ final NetworkCapabilities.Builder builder =
+ new NetworkCapabilities.Builder(mNetCapabilities);
if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
- mNetCapabilities.setTransportInfo(wifiInfo);
+ builder.setTransportInfo(wifiInfo);
}
- setConnectivityCommon(networkType, validated, isConnected);
+ setConnectivityCommon(builder, networkType, validated, isConnected);
mDefaultCallbackInWifiTracker.onCapabilitiesChanged(
- mNetwork, new NetworkCapabilities(mNetCapabilities));
+ mNetwork, builder.build());
}
- private void setConnectivityCommon(
+ private static void setConnectivityCommon(NetworkCapabilities.Builder builder,
int networkType, boolean validated, boolean isConnected){
// TODO: Separate out into several NetworkCapabilities.
if (isConnected) {
- mNetCapabilities.addTransportType(networkType);
+ builder.addTransportType(networkType);
} else {
- mNetCapabilities.removeTransportType(networkType);
+ builder.removeTransportType(networkType);
}
if (validated) {
- mNetCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ builder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
} else {
- mNetCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index ed87a40..c38a547 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -202,8 +202,8 @@
@Test
public void testNetworkRequest() {
verify(mConnectivityManager, times(1)).registerNetworkCallback(argThat(
- (NetworkRequest request) -> request.networkCapabilities.getUids() == null
- && request.networkCapabilities.getCapabilities().length == 0
+ (NetworkRequest request) ->
+ request.equals(new NetworkRequest.Builder().clearCapabilities().build())
), any(NetworkCallback.class));
}
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 653946e..6f6ef72 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
@@ -71,15 +71,17 @@
public class WalletScreenControllerTest extends SysuiTestCase {
private static final int MAX_CARDS = 10;
+ private static final int CARD_CAROUSEL_WIDTH = 10;
private static final String CARD_ID = "card_id";
private static final CharSequence SHORTCUT_SHORT_LABEL = "View all";
private static final CharSequence SHORTCUT_LONG_LABEL = "Add a payment method";
private static final CharSequence SERVICE_LABEL = "Wallet app";
- private final WalletView mWalletView = new WalletView(mContext);
private final Drawable mWalletLogo = mContext.getDrawable(android.R.drawable.ic_lock_lock);
private final Intent mWalletIntent = new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
.setComponent(new ComponentName(mContext.getPackageName(), "WalletActivity"));
+ private WalletView mWalletView;
+
@Mock
QuickAccessWalletClient mWalletClient;
@Mock
@@ -104,6 +106,8 @@
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
when(mUserTracker.getUserContext()).thenReturn(mContext);
+ mWalletView = new WalletView(mContext);
+ mWalletView.getCardCarousel().setExpectedViewWidth(CARD_CAROUSEL_WIDTH);
when(mWalletClient.getLogo()).thenReturn(mWalletLogo);
when(mWalletClient.getShortcutLongLabel()).thenReturn(SHORTCUT_LONG_LABEL);
when(mWalletClient.getShortcutShortLabel()).thenReturn(SHORTCUT_SHORT_LABEL);
@@ -132,7 +136,12 @@
verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
- mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+ QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
+ mCallbackCaptor.getValue();
+
+ assertEquals(mController, callback);
+
+ callback.onWalletCardsRetrieved(response);
mTestableLooper.processAllMessages();
assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility());
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ne/strings.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ne/strings.xml
index 8765aeb..0b019ae8 100644
--- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ne/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-ne/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="display_cutout_emulation_overlay" msgid="1677693377327336341">"कुनाको कटआउट"</string>
+ <string name="display_cutout_emulation_overlay" msgid="1677693377327336341">"कर्नर कटआउट"</string>
</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-ne/strings.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-ne/strings.xml
index 61653c3..ac29086 100644
--- a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-ne/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-ne/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="display_cutout_emulation_overlay" msgid="3523556473422419323">"झरनाको कटआउट"</string>
+ <string name="display_cutout_emulation_overlay" msgid="3523556473422419323">"वाटरफल कटआउट"</string>
</resources>
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 05d4ba5..9abe00f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -149,7 +149,6 @@
*/
public class AccessibilityManagerService extends IAccessibilityManager.Stub
implements AbstractAccessibilityServiceConnection.SystemSupport,
- AccessibilityTrace,
AccessibilityUserState.ServiceInfoChangeListener,
AccessibilityWindowManager.AccessibilityEventSender,
AccessibilitySecurityPolicy.AccessibilityUserManager,
@@ -244,7 +243,7 @@
final SparseArray<AccessibilityUserState> mUserStates = new SparseArray<>();
private final UiAutomationManager mUiAutomationManager = new UiAutomationManager(mLock);
- private final WindowManagerInternal.AccessibilityControllerInternal mA11yController;
+ private final AccessibilityTraceManager mTraceManager;
private int mCurrentUserId = UserHandle.USER_SYSTEM;
@@ -290,7 +289,8 @@
mContext = context;
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
- mA11yController = mWindowManagerService.getAccessibilityController();
+ mTraceManager = new AccessibilityTraceManager(
+ mWindowManagerService.getAccessibilityController(), this);
mMainHandler = new MainHandler(mContext.getMainLooper());
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = packageManager;
@@ -311,7 +311,8 @@
mContext = context;
mPowerManager = context.getSystemService(PowerManager.class);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
- mA11yController = mWindowManagerService.getAccessibilityController();
+ mTraceManager = new AccessibilityTraceManager(
+ mWindowManagerService.getAccessibilityController(), this);
mMainHandler = new MainHandler(mContext.getMainLooper());
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = mContext.getPackageManager();
@@ -338,24 +339,25 @@
@Override
public int getCurrentUserIdLocked() {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".getCurrentUserIdLocked");
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".getCurrentUserIdLocked");
}
return mCurrentUserId;
}
@Override
public boolean isAccessibilityButtonShown() {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".isAccessibilityButtonShown");
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".isAccessibilityButtonShown");
}
return mIsAccessibilityButtonShown;
}
@Override
public void onServiceInfoChangedLocked(AccessibilityUserState userState) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".onServiceInfoChangedLocked", "userState=" + userState);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(
+ LOG_TAG + ".onServiceInfoChangedLocked", "userState=" + userState);
}
mSecurityPolicy.onBoundServicesChangedLocked(userState.mUserId,
userState.mBoundServices);
@@ -376,6 +378,12 @@
}
}
+ AccessibilityUserState getCurrentUserState() {
+ synchronized (mLock) {
+ return getCurrentUserStateLocked();
+ }
+ }
+
private AccessibilityUserState getUserState(int userId) {
synchronized (mLock) {
return getUserStateLocked(userId);
@@ -416,8 +424,8 @@
PackageMonitor monitor = new PackageMonitor() {
@Override
public void onSomePackagesChanged() {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".PM.onSomePackagesChanged");
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged");
}
synchronized (mLock) {
@@ -444,8 +452,8 @@
// mBindingServices in binderDied() during updating. Remove services from this
// package from mBindingServices, and then update the user state to re-bind new
// versions of them.
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".PM.onPackageUpdateFinished",
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".PM.onPackageUpdateFinished",
"packageName=" + packageName + ";uid=" + uid);
}
synchronized (mLock) {
@@ -477,8 +485,8 @@
@Override
public void onPackageRemoved(String packageName, int uid) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".PM.onPackageRemoved",
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".PM.onPackageRemoved",
"packageName=" + packageName + ";uid=" + uid);
}
@@ -521,9 +529,10 @@
@Override
public boolean onHandleForceStop(Intent intent, String[] packages,
int uid, boolean doit) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".PM.onHandleForceStop", "intent=" + intent + ";packages="
- + packages + ";uid=" + uid + ";doit=" + doit);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".PM.onHandleForceStop",
+ "intent=" + intent + ";packages=" + packages + ";uid=" + uid
+ + ";doit=" + doit);
}
synchronized (mLock) {
final int userId = getChangingUserId();
@@ -571,8 +580,9 @@
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".BR.onReceive", "context=" + context + ";intent=" + intent);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".BR.onReceive",
+ "context=" + context + ";intent=" + intent);
}
String action = intent.getAction();
@@ -658,8 +668,9 @@
@Override
public long addClient(IAccessibilityManagerClient callback, int userId) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".addClient", "callback=" + callback + ";userId=" + userId);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".addClient",
+ "callback=" + callback + ";userId=" + userId);
}
synchronized (mLock) {
@@ -700,8 +711,9 @@
@Override
public void sendAccessibilityEvent(AccessibilityEvent event, int userId) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".sendAccessibilityEvent", "event=" + event + ";userId=" + userId);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent",
+ "event=" + event + ";userId=" + userId);
}
boolean dispatchEvent = false;
@@ -795,9 +807,9 @@
*/
@Override
public void registerSystemAction(RemoteAction action, int actionId) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".registerSystemAction", "action=" + action + ";actionId="
- + actionId);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".registerSystemAction",
+ "action=" + action + ";actionId=" + actionId);
}
mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
getSystemActionPerformer().registerSystemAction(actionId, action);
@@ -810,8 +822,8 @@
*/
@Override
public void unregisterSystemAction(int actionId) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".unregisterSystemAction", "actionId=" + actionId);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".unregisterSystemAction", "actionId=" + actionId);
}
mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
getSystemActionPerformer().unregisterSystemAction(actionId);
@@ -827,8 +839,9 @@
@Override
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".getInstalledAccessibilityServiceList", "userId=" + userId);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".getInstalledAccessibilityServiceList",
+ "userId=" + userId);
}
synchronized (mLock) {
@@ -848,8 +861,8 @@
@Override
public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType,
int userId) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".getEnabledAccessibilityServiceList",
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".getEnabledAccessibilityServiceList",
"feedbackType=" + feedbackType + ";userId=" + userId);
}
@@ -881,8 +894,8 @@
@Override
public void interrupt(int userId) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".interrupt", "userId=" + userId);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".interrupt", "userId=" + userId);
}
List<IAccessibilityServiceClient> interfacesToInterrupt;
@@ -911,8 +924,8 @@
}
for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) {
try {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".IAccessibilityServiceClient.onInterrupt");
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".IAccessibilityServiceClient.onInterrupt");
}
interfacesToInterrupt.get(i).onInterrupt();
} catch (RemoteException re) {
@@ -926,8 +939,8 @@
public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
IAccessibilityInteractionConnection connection, String packageName,
int userId) throws RemoteException {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".addAccessibilityInteractionConnection",
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".addAccessibilityInteractionConnection",
"windowToken=" + windowToken + "leashToken=" + leashToken + ";connection="
+ connection + "; packageName=" + packageName + ";userId=" + userId);
}
@@ -938,8 +951,9 @@
@Override
public void removeAccessibilityInteractionConnection(IWindow window) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".removeAccessibilityInteractionConnection", "window=" + window);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".removeAccessibilityInteractionConnection",
+ "window=" + window);
}
mA11yWindowManager.removeAccessibilityInteractionConnection(window);
}
@@ -947,8 +961,8 @@
@Override
public void setPictureInPictureActionReplacingConnection(
IAccessibilityInteractionConnection connection) throws RemoteException {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".setPictureInPictureActionReplacingConnection",
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".setPictureInPictureActionReplacingConnection",
"connection=" + connection);
}
mSecurityPolicy.enforceCallingPermission(Manifest.permission.MODIFY_ACCESSIBILITY_DATA,
@@ -961,8 +975,8 @@
IAccessibilityServiceClient serviceClient,
AccessibilityServiceInfo accessibilityServiceInfo,
int flags) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".registerUiTestAutomationService", "owner=" + owner
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".registerUiTestAutomationService", "owner=" + owner
+ ";serviceClient=" + serviceClient + ";accessibilityServiceInfo="
+ accessibilityServiceInfo + ";flags=" + flags);
}
@@ -973,16 +987,16 @@
synchronized (mLock) {
mUiAutomationManager.registerUiTestAutomationServiceLocked(owner, serviceClient,
mContext, accessibilityServiceInfo, sIdCounter++, mMainHandler,
- mSecurityPolicy, this, this, mWindowManagerService, getSystemActionPerformer(),
- mA11yWindowManager, flags);
+ mSecurityPolicy, this, getTraceManager(), mWindowManagerService,
+ getSystemActionPerformer(), mA11yWindowManager, flags);
onUserStateChangedLocked(getCurrentUserStateLocked());
}
}
@Override
public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".unregisterUiTestAutomationService",
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".unregisterUiTestAutomationService",
"serviceClient=" + serviceClient);
}
synchronized (mLock) {
@@ -993,8 +1007,9 @@
@Override
public void temporaryEnableAccessibilityStateUntilKeyguardRemoved(
ComponentName service, boolean touchExplorationEnabled) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".temporaryEnableAccessibilityStateUntilKeyguardRemoved",
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(
+ LOG_TAG + ".temporaryEnableAccessibilityStateUntilKeyguardRemoved",
"service=" + service + ";touchExplorationEnabled=" + touchExplorationEnabled);
}
@@ -1026,8 +1041,9 @@
@Override
public IBinder getWindowToken(int windowId, int userId) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".getWindowToken", "windowId=" + windowId + ";userId=" + userId);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".getWindowToken",
+ "windowId=" + windowId + ";userId=" + userId);
}
mSecurityPolicy.enforceCallingPermission(
@@ -1069,8 +1085,8 @@
*/
@Override
public void notifyAccessibilityButtonClicked(int displayId, String targetName) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".notifyAccessibilityButtonClicked",
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonClicked",
"displayId=" + displayId + ";targetName=" + targetName);
}
@@ -1099,8 +1115,9 @@
*/
@Override
public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".notifyAccessibilityButtonVisibilityChanged", "shown=" + shown);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonVisibilityChanged",
+ "shown=" + shown);
}
mSecurityPolicy.enforceCallingOrSelfPermission(
@@ -1131,8 +1148,8 @@
*/
@Override
public void onSystemActionsChanged() {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".onSystemActionsChanged");
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".onSystemActionsChanged");
}
synchronized (mLock) {
@@ -1197,8 +1214,9 @@
@Override
public @Nullable MotionEventInjector getMotionEventInjectorForDisplayLocked(int displayId) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".getMotionEventInjectorForDisplayLocked", "displayId=" + displayId);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".getMotionEventInjectorForDisplayLocked",
+ "displayId=" + displayId);
}
final long endMillis = SystemClock.uptimeMillis() + WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS;
@@ -1411,7 +1429,7 @@
private int getClientStateLocked(AccessibilityUserState userState) {
return userState.getClientStateLocked(
mUiAutomationManager.isUiAutomationRunningLocked(),
- mA11yController.isAccessibilityTracingEnabled());
+ mTraceManager.isA11yTracingEnabled());
}
private InteractionBridge getInteractionBridge() {
@@ -1770,9 +1788,10 @@
@Override
public void persistComponentNamesToSettingLocked(String settingName,
Set<ComponentName> componentNames, int userId) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".persistComponentNamesToSettingLocked", "settingName=" + settingName
- + ";componentNames=" + componentNames + ";userId=" + userId);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".persistComponentNamesToSettingLocked",
+ "settingName=" + settingName + ";componentNames=" + componentNames + ";userId="
+ + userId);
}
persistColonDelimitedSetToSettingLocked(settingName, userId, componentNames,
@@ -1859,8 +1878,9 @@
if (service == null) {
service = new AccessibilityServiceConnection(userState, mContext, componentName,
installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
- this, this, mWindowManagerService, getSystemActionPerformer(),
- mA11yWindowManager, mActivityTaskManagerService);
+ this, getTraceManager(), mWindowManagerService,
+ getSystemActionPerformer(), mA11yWindowManager,
+ mActivityTaskManagerService);
} else if (userState.mBoundServices.contains(service)) {
continue;
}
@@ -1892,6 +1912,12 @@
updateAccessibilityEnabledSettingLocked(userState);
}
+ void scheduleUpdateClientsIfNeeded(AccessibilityUserState userState) {
+ synchronized (mLock) {
+ scheduleUpdateClientsIfNeededLocked(userState);
+ }
+ }
+
private void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
final int clientState = getClientStateLocked(userState);
if (userState.getLastSentClientStateLocked() != clientState
@@ -2737,8 +2763,9 @@
@GuardedBy("mLock")
@Override
public MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".getCompatibleMagnificationSpecLocked", "windowId=" + windowId);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".getCompatibleMagnificationSpecLocked",
+ "windowId=" + windowId);
}
IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
@@ -2752,8 +2779,8 @@
@Override
public KeyEventDispatcher getKeyEventDispatcher() {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".getKeyEventDispatcher");
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".getKeyEventDispatcher");
}
if (mKeyEventDispatcher == null) {
@@ -2768,9 +2795,10 @@
@SuppressWarnings("AndroidFrameworkPendingIntentMutability")
public PendingIntent getPendingIntentActivity(Context context, int requestCode, Intent intent,
int flags) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".getPendingIntentActivity", "context=" + context + ";requestCode="
- + requestCode + ";intent=" + intent + ";flags=" + flags);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".getPendingIntentActivity",
+ "context=" + context + ";requestCode=" + requestCode + ";intent=" + intent
+ + ";flags=" + flags);
}
@@ -2788,8 +2816,9 @@
*/
@Override
public void performAccessibilityShortcut(String targetName) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".performAccessibilityShortcut", "targetName=" + targetName);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".performAccessibilityShortcut",
+ "targetName=" + targetName);
}
if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
@@ -2976,8 +3005,9 @@
@Override
public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".getAccessibilityShortcutTargets", "shortcutType=" + shortcutType);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".getAccessibilityShortcutTargets",
+ "shortcutType=" + shortcutType);
}
if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
@@ -3049,8 +3079,9 @@
@Override
public void sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".sendAccessibilityEventForCurrentUserLocked", "event=" + event);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEventForCurrentUserLocked",
+ "event=" + event);
}
sendAccessibilityEventLocked(event, mCurrentUserId);
@@ -3074,8 +3105,9 @@
*/
@Override
public boolean sendFingerprintGesture(int gestureKeyCode) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".sendFingerprintGesture", "gestureKeyCode=" + gestureKeyCode);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".sendFingerprintGesture",
+ "gestureKeyCode=" + gestureKeyCode);
}
synchronized(mLock) {
@@ -3099,8 +3131,9 @@
*/
@Override
public int getAccessibilityWindowId(@Nullable IBinder windowToken) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".getAccessibilityWindowId", "windowToken=" + windowToken);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".getAccessibilityWindowId",
+ "windowToken=" + windowToken);
}
synchronized (mLock) {
@@ -3120,8 +3153,8 @@
*/
@Override
public long getRecommendedTimeoutMillis() {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".getRecommendedTimeoutMillis");
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".getRecommendedTimeoutMillis");
}
synchronized(mLock) {
@@ -3138,8 +3171,9 @@
@Override
public void setWindowMagnificationConnection(
IWindowMagnificationConnection connection) throws RemoteException {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".setWindowMagnificationConnection", "connection=" + connection);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".setWindowMagnificationConnection",
+ "connection=" + connection);
}
mSecurityPolicy.enforceCallingOrSelfPermission(
@@ -3172,8 +3206,8 @@
@Override
public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".associateEmbeddedHierarchy",
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".associateEmbeddedHierarchy",
"host=" + host + ";embedded=" + embedded);
}
@@ -3184,8 +3218,8 @@
@Override
public void disassociateEmbeddedHierarchy(@NonNull IBinder token) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".disassociateEmbeddedHierarchy", "token=" + token);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".disassociateEmbeddedHierarchy", "token=" + token);
}
synchronized (mLock) {
@@ -3265,8 +3299,8 @@
@Override
public FullScreenMagnificationController getFullScreenMagnificationController() {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".getFullScreenMagnificationController");
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".getFullScreenMagnificationController");
}
synchronized (mLock) {
return mMagnificationController.getFullScreenMagnificationController();
@@ -3275,8 +3309,9 @@
@Override
public void onClientChangeLocked(boolean serviceInfoChanged) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".onClientChangeLocked", "serviceInfoChanged=" + serviceInfoChanged);
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".onClientChangeLocked",
+ "serviceInfoChanged=" + serviceInfoChanged);
}
AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
@@ -3314,9 +3349,9 @@
AccessibilityServiceConnection service = new AccessibilityServiceConnection(
userState, mContext,
COMPONENT_NAME, info, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
- AccessibilityManagerService.this, AccessibilityManagerService.this,
- mWindowManagerService, getSystemActionPerformer(), mA11yWindowManager,
- mActivityTaskManagerService) {
+ AccessibilityManagerService.this,
+ AccessibilityManagerService.this.getTraceManager(), mWindowManagerService,
+ getSystemActionPerformer(), mA11yWindowManager, mActivityTaskManagerService) {
@Override
public boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
return true;
@@ -3805,8 +3840,8 @@
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".setGestureDetectionPassthroughRegion",
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".setGestureDetectionPassthroughRegion",
"displayId=" + displayId + ";region=" + region);
}
@@ -3820,8 +3855,8 @@
@Override
public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
- if (isA11yTracingEnabled()) {
- logTrace(LOG_TAG + ".setTouchExplorationPassthroughRegion",
+ if (mTraceManager.isA11yTracingEnabled()) {
+ mTraceManager.logTrace(LOG_TAG + ".setTouchExplorationPassthroughRegion",
"displayId=" + displayId + ";region=" + region);
}
@@ -3863,46 +3898,7 @@
}
- @Override
- public boolean isA11yTracingEnabled() {
- return mA11yController.isAccessibilityTracingEnabled();
- }
-
- @Override
- public void startTrace() {
- if (!mA11yController.isAccessibilityTracingEnabled()) {
- mA11yController.startTrace();
- final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
- scheduleUpdateClientsIfNeededLocked(userState);
- }
- }
-
- @Override
- public void stopTrace() {
- if (mA11yController.isAccessibilityTracingEnabled()) {
- mA11yController.stopTrace();
- final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
- scheduleUpdateClientsIfNeededLocked(userState);
- }
- }
-
- @Override
- public void logTrace(String where) {
- logTrace(where, "");
- }
-
- @Override
- public void logTrace(String where, String callingParams) {
- mA11yController.logTrace(where, callingParams, "".getBytes(),
- Binder.getCallingUid(), Thread.currentThread().getStackTrace());
- }
-
- @Override
- public void logTrace(long timestamp, String where, String callingParams, int processId,
- long threadId, int callingUid, StackTraceElement[] callStack) {
- if (mA11yController.isAccessibilityTracingEnabled()) {
- mA11yController.logTrace(where, callingParams, "".getBytes(), callingUid, callStack,
- timestamp, processId, threadId);
- }
+ AccessibilityTraceManager getTraceManager() {
+ return mTraceManager;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
index 16ce177..6396960 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
@@ -59,9 +59,8 @@
return runCallSystemAction();
}
case "start-trace":
- return startTrace();
case "stop-trace":
- return stopTrace();
+ return mService.getTraceManager().onShellCommand(cmd);
}
return -1;
}
@@ -107,16 +106,6 @@
return -1;
}
- private int startTrace() {
- mService.startTrace();
- return 0;
- }
-
- private int stopTrace() {
- mService.stopTrace();
- return 0;
- }
-
private Integer parseUserId() {
final String option = getNextOption();
if (option != null) {
@@ -142,9 +131,6 @@
pw.println(" Get whether binding to services provided by instant apps is allowed.");
pw.println(" call-system-action <ACTION_ID>");
pw.println(" Calls the system action with the given action id.");
- pw.println(" start-trace");
- pw.println(" Start the debug tracing.");
- pw.println(" stop-trace");
- pw.println(" Stop the debug tracing.");
+ mService.getTraceManager().onHelp(pw);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
new file mode 100644
index 0000000..6105e8a
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
@@ -0,0 +1,100 @@
+/**
+ * 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.accessibility;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+
+import com.android.server.wm.WindowManagerInternal;
+
+import java.io.PrintWriter;
+
+/**
+ * Manager of accessibility trace.
+ */
+class AccessibilityTraceManager implements AccessibilityTrace {
+ private final WindowManagerInternal.AccessibilityControllerInternal mA11yController;
+ private final AccessibilityManagerService mService;
+
+ AccessibilityTraceManager(
+ @NonNull WindowManagerInternal.AccessibilityControllerInternal a11yController,
+ @NonNull AccessibilityManagerService service) {
+ mA11yController = a11yController;
+ mService = service;
+ }
+
+ @Override
+ public boolean isA11yTracingEnabled() {
+ return mA11yController.isAccessibilityTracingEnabled();
+ }
+
+ @Override
+ public void startTrace() {
+ if (!mA11yController.isAccessibilityTracingEnabled()) {
+ mA11yController.startTrace();
+ mService.scheduleUpdateClientsIfNeeded(mService.getCurrentUserState());
+ }
+ }
+
+ @Override
+ public void stopTrace() {
+ if (mA11yController.isAccessibilityTracingEnabled()) {
+ mA11yController.stopTrace();
+ mService.scheduleUpdateClientsIfNeeded(mService.getCurrentUserState());
+ }
+ }
+
+ @Override
+ public void logTrace(String where) {
+ logTrace(where, "");
+ }
+
+ @Override
+ public void logTrace(String where, String callingParams) {
+ mA11yController.logTrace(where, callingParams, "".getBytes(),
+ Binder.getCallingUid(), Thread.currentThread().getStackTrace());
+ }
+
+ @Override
+ public void logTrace(long timestamp, String where, String callingParams, int processId,
+ long threadId, int callingUid, StackTraceElement[] callStack) {
+ if (mA11yController.isAccessibilityTracingEnabled()) {
+ mA11yController.logTrace(where, callingParams, "".getBytes(), callingUid, callStack,
+ timestamp, processId, threadId);
+ }
+ }
+
+ int onShellCommand(String cmd) {
+ switch (cmd) {
+ case "start-trace": {
+ startTrace();
+ return 0;
+ }
+ case "stop-trace": {
+ stopTrace();
+ return 0;
+ }
+ }
+ return -1;
+ }
+
+ void onHelp(PrintWriter pw) {
+ pw.println(" start-trace");
+ pw.println(" Start the debug tracing.");
+ pw.println(" stop-trace");
+ pw.println(" Stop the debug tracing.");
+ }
+}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index e251700..d922d2b 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1589,6 +1589,7 @@
public ParceledListSlice<AppWidgetProviderInfo> getInstalledProvidersForProfile(int categoryFilter,
int profileId, String packageName) {
final int userId = UserHandle.getCallingUserId();
+ final int callingUid = Binder.getCallingUid();
if (DEBUG) {
Slog.i(TAG, "getInstalledProvidersForProfiles() " + userId);
@@ -1601,7 +1602,7 @@
synchronized (mLock) {
if (mSecurityPolicy.isCallerInstantAppLocked()) {
- Slog.w(TAG, "Instant uid " + Binder.getCallingUid()
+ Slog.w(TAG, "Instant uid " + callingUid
+ " cannot access widget providers");
return ParceledListSlice.emptyList();
}
@@ -1614,11 +1615,12 @@
for (int i = 0; i < providerCount; i++) {
Provider provider = mProviders.get(i);
AppWidgetProviderInfo info = provider.getInfoLocked(mContext);
+ final String providerPackageName = provider.id.componentName.getPackageName();
// Ignore an invalid provider, one not matching the filter,
// or one that isn't in the given package, if any.
boolean inPackage = packageName == null
- || provider.id.componentName.getPackageName().equals(packageName);
+ || providerPackageName.equals(packageName);
if (provider.zombie || (info.widgetCategory & categoryFilter) == 0 || !inPackage) {
continue;
}
@@ -1627,7 +1629,9 @@
final int providerProfileId = info.getProfile().getIdentifier();
if (providerProfileId == profileId
&& mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
- provider.id.componentName.getPackageName(), providerProfileId)) {
+ providerPackageName, providerProfileId)
+ && !mPackageManagerInternal.filterAppAccess(providerPackageName, callingUid,
+ userId)) {
result.add(cloneIfLocalBinder(info));
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f8b770b..6bf4967 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1311,7 +1311,9 @@
@Override
public void onServiceDied(@NonNull RemoteFillService service) {
Slog.w(TAG, "removing session because service died");
- forceRemoveFromServiceLocked();
+ synchronized (mLock) {
+ forceRemoveFromServiceLocked();
+ }
}
// AutoFillUiCallback
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5cbcacf..5dc11c5 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -132,7 +132,6 @@
],
static_libs: [
- "protolog-lib",
"time_zone_distro",
"time_zone_distro_installer",
"android.hardware.authsecret-V1.0-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index a9eb2c1..5dd9b0e 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -825,6 +825,12 @@
String packageName, int userId);
/**
+ * Return the enabled setting for a package component (activity, receiver, service, provider).
+ */
+ public abstract @PackageManager.EnabledState int getComponentEnabledSetting(
+ @NonNull ComponentName componentName, int callingUid, int userId);
+
+ /**
* Extra field name for the token of a request to enable rollback for a
* package.
*/
diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
index aa56da5..197321f 100644
--- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
+++ b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
@@ -106,6 +107,7 @@
}
@VisibleForTesting
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
void handleAirplaneModeChange() {
if (shouldSkipAirplaneModeChange()) {
Log.i(TAG, "Ignore airplane mode change");
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 09cfac0..c3a5d1f 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -23,6 +23,8 @@
import static android.os.UserHandle.USER_SYSTEM;
import android.Manifest;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -304,6 +306,19 @@
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
"Need BLUETOOTH_PRIVILEGED permission");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return onFactoryResetInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ private boolean onFactoryResetInternal() {
// Wait for stable state if bluetooth is temporary state.
int state = getState();
if (state == BluetoothAdapter.STATE_BLE_TURNING_ON
@@ -343,6 +358,7 @@
return false;
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public void onAirplaneModeChanged() {
synchronized (this) {
if (isBluetoothPersistedStateOn()) {
@@ -698,18 +714,12 @@
Slog.w(TAG, "Callback is null in unregisterAdapter");
return;
}
- if (!checkConnectPermissionForPreflight(mContext)) {
- return;
- }
synchronized (mCallbacks) {
mCallbacks.unregister(callback);
}
}
public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
- if (!checkConnectPermissionForPreflight(mContext)) {
- return;
- }
if (callback == null) {
Slog.w(TAG, "registerStateChangeCallback: Callback is null!");
return;
@@ -720,9 +730,6 @@
}
public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
- if (!checkConnectPermissionForPreflight(mContext)) {
- return;
- }
if (callback == null) {
Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!");
return;
@@ -935,6 +942,7 @@
return appCount;
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private boolean checkBluetoothPermissions(String packageName, boolean requireForeground) {
if (isBluetoothDisallowed()) {
if (DBG) {
@@ -990,6 +998,7 @@
return true;
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean disableBle(String packageName, IBinder token) throws RemoteException {
if (!checkBluetoothPermissions(packageName, false)) {
if (DBG) {
@@ -1040,6 +1049,7 @@
* Call IBluetooth.onLeServiceUp() to continue if Bluetooth should be on,
* call IBluetooth.onBrEdrDown() to disable if Bluetooth should be off.
*/
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
private void continueFromBleOnState() {
if (DBG) {
Slog.d(TAG, "continueFromBleOnState()");
@@ -1072,6 +1082,10 @@
* Inform BluetoothAdapter instances that BREDR part is down
* and turn off all service and stack if no LE app needs it
*/
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
private void sendBrEdrDownCallback() {
if (DBG) {
Slog.d(TAG, "Calling sendBrEdrDownCallback callbacks");
@@ -1265,12 +1279,14 @@
*
* @hide
*/
+ @SuppressLint("AndroidFrameworkRequiresPermission")
private boolean checkBluetoothPermissionWhenWirelessConsentRequired() {
int result = mContext.checkCallingPermission(
android.Manifest.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED);
return result == PackageManager.PERMISSION_GRANTED;
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public void unbindAndFinish() {
if (DBG) {
Slog.d(TAG, "unbindAndFinish(): " + mBluetooth + " mBinding = " + mBinding
@@ -2300,6 +2316,10 @@
}
}
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED
+ })
private void restartForReason(int reason) {
try {
mBluetoothLock.readLock().lock();
@@ -2376,6 +2396,7 @@
}
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private void handleEnable(boolean quietMode) {
mQuietEnable = quietMode;
@@ -2418,6 +2439,7 @@
return true;
}
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private void handleDisable() {
try {
mBluetoothLock.readLock().lock();
@@ -2475,6 +2497,10 @@
mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT);
}
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
private void bluetoothStateChangeHandler(int prevState, int newState) {
boolean isStandardBroadcast = true;
if (prevState == newState) { // No change. Nothing to do.
@@ -2615,6 +2641,10 @@
}
}
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
private void recoverBluetoothServiceFromError(boolean clearBle) {
Slog.e(TAG, "recoverBluetoothServiceFromError");
try {
@@ -2848,6 +2878,7 @@
*
* <p>Should be used in situations where the app op should not be noted.
*/
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private static boolean checkConnectPermissionForPreflight(Context context) {
int permissionCheckResult = PermissionChecker.checkCallingOrSelfPermissionForPreflight(
context, BLUETOOTH_CONNECT);
diff --git a/services/core/java/com/android/server/BluetoothModeChangeHelper.java b/services/core/java/com/android/server/BluetoothModeChangeHelper.java
index 242fa84..3642e4d 100644
--- a/services/core/java/com/android/server/BluetoothModeChangeHelper.java
+++ b/services/core/java/com/android/server/BluetoothModeChangeHelper.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothHearingAid;
@@ -101,6 +102,7 @@
}
@VisibleForTesting
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public void onAirplaneModeChanged(BluetoothManagerService managerService) {
managerService.onAirplaneModeChanged();
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d9cc4b4..809ef41 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -630,7 +630,9 @@
}
private static IDnsResolver getDnsResolver(Context context) {
- return IDnsResolver.Stub.asInterface(DnsResolverServiceManager.getService(context));
+ final DnsResolverServiceManager dsm = context.getSystemService(
+ DnsResolverServiceManager.class);
+ return IDnsResolver.Stub.asInterface(dsm.getService());
}
/** Handler thread used for all of the handlers below. */
@@ -1353,8 +1355,8 @@
new NetworkInfo(TYPE_NONE, 0, "", ""),
new LinkProperties(), new NetworkCapabilities(),
new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null,
- new NetworkAgentConfig(), this, null, null, 0, INVALID_UID, mQosCallbackTracker,
- mDeps);
+ new NetworkAgentConfig(), this, null, null, 0, INVALID_UID,
+ mLingerDelayMs, mQosCallbackTracker, mDeps);
}
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -1924,7 +1926,7 @@
newNc.setAdministratorUids(new int[0]);
if (!checkAnyPermissionOf(
callerPid, callerUid, android.Manifest.permission.NETWORK_FACTORY)) {
- newNc.setSubIds(Collections.emptySet());
+ newNc.setSubscriptionIds(Collections.emptySet());
}
return newNc;
@@ -2147,7 +2149,7 @@
PermissionUtils.enforceNetworkStackPermission(mContext);
final ArrayList<NetworkState> result = new ArrayList<>();
- for (NetworkStateSnapshot snapshot : getAllNetworkStateSnapshot()) {
+ for (NetworkStateSnapshot snapshot : getAllNetworkStateSnapshots()) {
// NetworkStateSnapshot doesn't contain NetworkInfo, so need to fetch it from the
// NetworkAgentInfo.
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(snapshot.network);
@@ -2162,7 +2164,7 @@
@Override
@NonNull
- public List<NetworkStateSnapshot> getAllNetworkStateSnapshot() {
+ public List<NetworkStateSnapshot> getAllNetworkStateSnapshots() {
// This contains IMSI details, so make sure the caller is privileged.
PermissionUtils.enforceNetworkStackPermission(mContext);
@@ -2808,6 +2810,8 @@
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
@Nullable String[] args) {
+ if (!checkDumpPermission(mContext, TAG, writer)) return;
+
mPriorityDumper.dump(fd, writer, args);
}
@@ -2825,7 +2829,6 @@
private void doDump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- if (!checkDumpPermission(mContext, TAG, pw)) return;
if (CollectionUtils.contains(args, DIAG_ARG)) {
dumpNetworkDiagnostics(pw);
@@ -3167,6 +3170,11 @@
} else {
logwtf(nai.toShortString() + " set invalid teardown delay " + msg.arg1);
}
+ break;
+ }
+ case NetworkAgent.EVENT_LINGER_DURATION_CHANGED: {
+ nai.setLingerDuration((int) arg.second);
+ break;
}
}
}
@@ -3964,17 +3972,16 @@
// multilayer requests, returning as soon as a NetworkAgentInfo satisfies a request
// is important so as to not evaluate lower priority requests further in
// nri.mRequests.
- final boolean isNetworkNeeded = candidate.isSatisfyingRequest(req.requestId)
- // Note that this catches two important cases:
- // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
- // is currently satisfying the request. This is desirable when
- // cellular ends up validating but WiFi does not.
- // 2. Unvalidated WiFi will not be reaped when validated cellular
- // is currently satisfying the request. This is desirable when
- // WiFi ends up validating and out scoring cellular.
- || nri.getSatisfier().getCurrentScore()
- < candidate.getCurrentScoreAsValidated();
- return isNetworkNeeded;
+ final NetworkAgentInfo champion = req.equals(nri.getActiveRequest())
+ ? nri.getSatisfier() : null;
+ // Note that this catches two important cases:
+ // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
+ // is currently satisfying the request. This is desirable when
+ // cellular ends up validating but WiFi does not.
+ // 2. Unvalidated WiFi will not be reaped when validated cellular
+ // is currently satisfying the request. This is desirable when
+ // WiFi ends up validating and out scoring cellular.
+ return mNetworkRanker.mightBeat(req, champion, candidate.getValidatedScoreable());
}
}
@@ -5721,7 +5728,7 @@
}
mAppOpsManager.checkPackage(callerUid, callerPackageName);
- if (!nc.getSubIds().isEmpty()) {
+ if (!nc.getSubscriptionIds().isEmpty()) {
enforceNetworkFactoryPermission();
}
}
@@ -6143,6 +6150,7 @@
@Override
public int registerNetworkProvider(Messenger messenger, String name) {
enforceNetworkFactoryOrSettingsPermission();
+ Objects.requireNonNull(messenger, "messenger must be non-null");
NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger,
nextNetworkProviderId(), () -> unregisterNetworkProvider(messenger));
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
@@ -6516,7 +6524,8 @@
final NetworkAgentInfo nai = new NetworkAgentInfo(na,
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
- this, mNetd, mDnsResolver, providerId, uid, mQosCallbackTracker, mDeps);
+ this, mNetd, mDnsResolver, providerId, uid, mLingerDelayMs,
+ mQosCallbackTracker, mDeps);
// Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
processCapabilitiesFromAgent(nai, nc);
@@ -7759,7 +7768,7 @@
log(" accepting network in place of " + previousSatisfier.toShortString());
}
previousSatisfier.removeRequest(previousRequest.requestId);
- previousSatisfier.lingerRequest(previousRequest.requestId, now, mLingerDelayMs);
+ previousSatisfier.lingerRequest(previousRequest.requestId, now);
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
@@ -7805,7 +7814,7 @@
@NonNull final Collection<NetworkRequestInfo> networkRequests) {
final NetworkReassignment changes = new NetworkReassignment();
- // Gather the list of all relevant agents and sort them by score.
+ // Gather the list of all relevant agents.
final ArrayList<NetworkAgentInfo> nais = new ArrayList<>();
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
if (!nai.everConnected) {
@@ -9180,6 +9189,7 @@
@Override
public void unregisterConnectivityDiagnosticsCallback(
@NonNull IConnectivityDiagnosticsCallback callback) {
+ Objects.requireNonNull(callback, "callback must be non-null");
mConnectivityDiagnosticsHandler.sendMessage(
mConnectivityDiagnosticsHandler.obtainMessage(
ConnectivityDiagnosticsHandler
@@ -9550,6 +9560,7 @@
*/
@Override
public void unregisterQosCallback(@NonNull final IQosCallback callback) {
+ Objects.requireNonNull(callback, "callback must be non-null");
mQosCallbackTracker.unregisterCallback(callback);
}
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 9f91dd6..483250a 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -174,9 +174,9 @@
# Disk usage stats for verifying quota correctness
3121 pm_package_stats (manual_time|2|3),(quota_time|2|3),(manual_data|2|2),(quota_data|2|2),(manual_cache|2|2),(quota_cache|2|2)
# Snapshot statistics
-3130 pm_snapshot_stats (build_count|1|1),(reuse_count|1|1),(big_builds|1|1),(quick_rebuilds|1|1),(max_build_time|1|3),(cumm_build_time|1|3)
+3130 pm_snapshot_stats (build_count|1|1),(reuse_count|1|1),(big_builds|1|1),(short_lived|1|1),(max_build_time|1|3),(cumm_build_time|2|3)
# Snapshot rebuild instance
-3131 pm_snapshot_rebuild (build_time|1|3),(elapsed|1|3)
+3131 pm_snapshot_rebuild (build_time|1|3),(lifetime|1|3)
# ---------------------------
# InputMethodManagerService.java
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index df6ab5d..3ba4c34 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -19,9 +19,10 @@
import static android.Manifest.permission.MANAGE_SENSOR_PRIVACY;
import static android.app.ActivityManager.RunningServiceInfo;
import static android.app.ActivityManager.RunningTaskInfo;
-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_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD;
import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR;
@@ -140,11 +141,15 @@
private final UserManagerInternal mUserManagerInternal;
private final ActivityManager mActivityManager;
private final ActivityTaskManager mActivityTaskManager;
+ private final AppOpsManager mAppOpsManager;
+
+ private final IBinder mAppOpsRestrictionToken = new Binder();
private SensorPrivacyManagerInternalImpl mSensorPrivacyManagerInternal;
public SensorPrivacyService(Context context) {
super(context);
+ mAppOpsManager = context.getSystemService(AppOpsManager.class);
mUserManagerInternal = getLocalService(UserManagerInternal.class);
mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl(context);
mActivityManager = context.getSystemService(ActivityManager.class);
@@ -194,10 +199,20 @@
}
}
+ for (int i = 0; i < mIndividualEnabled.size(); i++) {
+ int userId = mIndividualEnabled.keyAt(i);
+ SparseBooleanArray userIndividualEnabled =
+ mIndividualEnabled.get(i);
+ for (int j = 0; j < userIndividualEnabled.size(); j++) {
+ int sensor = userIndividualEnabled.keyAt(i);
+ boolean enabled = userIndividualEnabled.valueAt(j);
+ setUserRestriction(userId, sensor, enabled);
+ }
+ }
+
int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_CAMERA};
- AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
- appOpsManager.startWatchingNoted(micAndCameraOps, this);
- appOpsManager.startWatchingStarted(micAndCameraOps, this);
+ mAppOpsManager.startWatchingNoted(micAndCameraOps, this);
+ mAppOpsManager.startWatchingStarted(micAndCameraOps, this);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
@@ -221,7 +236,7 @@
public void onOpNoted(int code, int uid, String packageName,
String attributionTag, @AppOpsManager.OpFlags int flags,
@AppOpsManager.Mode int result) {
- if (result != MODE_ALLOWED || (flags & AppOpsManager.OP_FLAGS_ALL_TRUSTED) == 0) {
+ if (result != MODE_IGNORED || (flags & AppOpsManager.OP_FLAGS_ALL_TRUSTED) == 0) {
return;
}
@@ -1125,6 +1140,9 @@
mSensorPrivacyManagerInternal.dispatch(userId, sensor, enabled);
SparseArray<RemoteCallbackList<ISensorPrivacyListener>> listenersForUser =
mIndividualSensorListeners.get(userId);
+
+ setUserRestriction(userId, sensor, enabled);
+
if (listenersForUser == null) {
return;
}
@@ -1152,6 +1170,18 @@
}
}
+ private void setUserRestriction(int userId, int sensor, boolean enabled) {
+ if (sensor == CAMERA) {
+ mAppOpsManager.setUserRestrictionForUser(OP_CAMERA, enabled,
+ mAppOpsRestrictionToken, new String[]{}, userId);
+ } else if (sensor == MICROPHONE) {
+ mAppOpsManager.setUserRestrictionForUser(OP_RECORD_AUDIO, enabled,
+ mAppOpsRestrictionToken, new String[]{}, userId);
+ mAppOpsManager.setUserRestrictionForUser(OP_RECORD_AUDIO_HOTWORD, enabled,
+ mAppOpsRestrictionToken, new String[]{}, userId);
+ }
+ }
+
private final class DeathRecipient implements IBinder.DeathRecipient {
private ISensorPrivacyListener mListener;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 611fe7a..ee3530a 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -376,7 +376,7 @@
private final LocalLog mListenLog = new LocalLog(200);
- private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
+ private List<List<PhysicalChannelConfig>> mPhysicalChannelConfigs;
private boolean[] mIsDataEnabled;
@@ -716,7 +716,7 @@
mTelephonyDisplayInfos[i] = null;
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
- mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
+ mPhysicalChannelConfigs.add(i, new ArrayList<>());
mAllowedNetworkTypeReason[i] = -1;
mAllowedNetworkTypeValue[i] = -1;
mLinkCapacityEstimateLists.add(i, new ArrayList<>());
@@ -816,7 +816,7 @@
mTelephonyDisplayInfos[i] = null;
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
- mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
+ mPhysicalChannelConfigs.add(i, new ArrayList<>());
mAllowedNetworkTypeReason[i] = -1;
mAllowedNetworkTypeValue[i] = -1;
mLinkCapacityEstimateLists.add(i, new ArrayList<>());
@@ -1314,8 +1314,9 @@
try {
r.callback.onPhysicalChannelConfigChanged(
shouldSanitizeLocationForPhysicalChannelConfig(r)
- ? getLocationSanitizedConfigs(mPhysicalChannelConfigs)
- : mPhysicalChannelConfigs);
+ ? getLocationSanitizedConfigs(
+ mPhysicalChannelConfigs.get(phoneId))
+ : mPhysicalChannelConfigs.get(phoneId));
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -2550,11 +2551,12 @@
* Send a notification to registrants that the configs of physical channel has changed for
* a particular subscription.
*
+ * @param phoneId the phone id.
* @param subId the subId
* @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel.
*/
- public void notifyPhysicalChannelConfigForSubscriber(
- int subId, List<PhysicalChannelConfig> configs) {
+ public void notifyPhysicalChannelConfigForSubscriber(int phoneId, int subId,
+ List<PhysicalChannelConfig> configs) {
if (!checkNotifyPermission("notifyPhysicalChannelConfig()")) {
return;
}
@@ -2566,9 +2568,8 @@
}
synchronized (mRecords) {
- int phoneId = SubscriptionManager.getPhoneId(subId);
if (validatePhoneId(phoneId)) {
- mPhysicalChannelConfigs.set(phoneId, configs.get(phoneId));
+ mPhysicalChannelConfigs.set(phoneId, configs);
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)
@@ -2775,6 +2776,7 @@
pw.println("mDataEnabledReason=" + mDataEnabledReason);
pw.println("mAllowedNetworkTypeReason=" + mAllowedNetworkTypeReason[i]);
pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]);
+ pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs.get(i));
pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i));
pw.decreaseIndent();
}
@@ -2785,7 +2787,6 @@
pw.println("mEmergencyNumberList=" + mEmergencyNumberList);
pw.println("mDefaultPhoneId=" + mDefaultPhoneId);
pw.println("mDefaultSubId=" + mDefaultSubId);
- pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs);
pw.decreaseIndent();
@@ -2860,7 +2861,14 @@
intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
+ // Send the broadcast twice -- once for all apps with READ_PHONE_STATE, then again
+ // for all apps with READ_PRIV but not READ_PHONE_STATE. This ensures that any app holding
+ // either READ_PRIV or READ_PHONE get this broadcast exactly once.
mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.READ_PHONE_STATE);
+ mContext.createContextAsUser(UserHandle.ALL, 0)
+ .sendBroadcastMultiplePermissions(intent,
+ new String[] { Manifest.permission.READ_PRIVILEGED_PHONE_STATE },
+ new String[] { Manifest.permission.READ_PHONE_STATE });
}
private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId,
@@ -2987,7 +2995,14 @@
getApnTypesStringFromBitmask(pdcs.getApnSetting().getApnTypeBitmask()));
intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, slotIndex);
intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
+ // Send the broadcast twice -- once for all apps with READ_PHONE_STATE, then again
+ // for all apps with READ_PRIV but not READ_PHONE_STATE. This ensures that any app holding
+ // either READ_PRIV or READ_PHONE get this broadcast exactly once.
mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.READ_PHONE_STATE);
+ mContext.createContextAsUser(UserHandle.ALL, 0)
+ .sendBroadcastMultiplePermissions(intent,
+ new String[] { Manifest.permission.READ_PRIVILEGED_PHONE_STATE },
+ new String[] { Manifest.permission.READ_PHONE_STATE });
}
/**
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 039f4d9..4b52057 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -729,7 +729,7 @@
// If multiple subscription IDs exist, they MUST all point to the same subscription
// group. Otherwise undefined behavior may occur.
- for (int subId : networkCapabilities.getSubIds()) {
+ for (int subId : networkCapabilities.getSubscriptionIds()) {
// Verify that all subscriptions point to the same group
if (subGrp != null && !subGrp.equals(snapshot.getGroupForSubId(subId))) {
Slog.wtf(TAG, "Got multiple subscription groups for a single network");
@@ -1003,14 +1003,14 @@
}
private boolean requiresRestartForCarrierWifi(NetworkCapabilities caps) {
- if (!caps.hasTransport(TRANSPORT_WIFI) || caps.getSubIds() == null) {
+ if (!caps.hasTransport(TRANSPORT_WIFI) || caps.getSubscriptionIds() == null) {
return false;
}
synchronized (mCaps) {
for (NetworkCapabilities existing : mCaps.values()) {
if (existing.hasTransport(TRANSPORT_WIFI)
- && caps.getSubIds().equals(existing.getSubIds())) {
+ && caps.getSubscriptionIds().equals(existing.getSubscriptionIds())) {
// Restart if any immutable capabilities have changed
return existing.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
!= caps.hasCapability(NET_CAPABILITY_NOT_RESTRICTED);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4324717..5a1a505 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3039,7 +3039,7 @@
try {
r.mRecentCallerApplicationInfo =
mAm.mContext.getPackageManager().getApplicationInfoAsUser(callingPackage,
- 0, userId);
+ 0, UserHandle.getUserId(callingUid));
} catch (PackageManager.NameNotFoundException e) {
}
if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid, r.packageName,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2a1a897..e3b06d6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2500,7 +2500,7 @@
@Override
public void batterySendBroadcast(Intent intent) {
synchronized (this) {
- broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
+ broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, null,
OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(),
Binder.getCallingPid(), UserHandle.USER_ALL);
}
@@ -3940,7 +3940,7 @@
intent.putExtra(Intent.EXTRA_UID, uid);
intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid));
broadcastIntentLocked(null, null, null, intent,
- null, null, 0, null, null, null, OP_NONE,
+ null, null, 0, null, null, null, null, OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
Binder.getCallingPid(), UserHandle.getUserId(uid));
}
@@ -7531,7 +7531,7 @@
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
broadcastIntentLocked(null, null, null, intent,
- null, null, 0, null, null, null, OP_NONE,
+ null, null, 0, null, null, null, null, OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
currentUserId);
intent = new Intent(Intent.ACTION_USER_STARTING);
@@ -7543,8 +7543,8 @@
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered, boolean sticky,
int sendingUser) {}
- }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, OP_NONE, null,
- true, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
+ }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, null, OP_NONE,
+ null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
UserHandle.USER_ALL);
} catch (Throwable e) {
Slog.wtf(TAG, "Failed sending first user broadcasts", e);
@@ -12302,7 +12302,7 @@
Intent intent = allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
- null, null, -1, -1, false, null, null, OP_NONE, null, receivers,
+ null, null, -1, -1, false, null, null, null, OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1, false, null,
false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
queue.enqueueParallelBroadcastLocked(r);
@@ -12544,13 +12544,13 @@
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, String callerFeatureId, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
- Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
- boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
- int realCallingPid, int userId) {
+ Bundle resultExtras, String[] requiredPermissions, String[] excludedPermissions,
+ int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid,
+ int callingUid, int realCallingUid, int realCallingPid, int userId) {
return broadcastIntentLocked(callerApp, callerPackage, callerFeatureId, intent,
resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
- appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
- realCallingPid, userId, false /* allowBackgroundActivityStarts */,
+ excludedPermissions, appOp, bOptions, ordered, sticky, callingPid, callingUid,
+ realCallingUid, realCallingPid, userId, false /* allowBackgroundActivityStarts */,
null /* tokenNeededForBackgroundActivityStarts */, null /* broadcastAllowList */);
}
@@ -12558,9 +12558,11 @@
final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
@Nullable String callerFeatureId, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
- Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
- boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
- int realCallingPid, int userId, boolean allowBackgroundActivityStarts,
+ Bundle resultExtras, String[] requiredPermissions,
+ String[] excludedPermissions, int appOp, Bundle bOptions,
+ boolean ordered, boolean sticky, int callingPid, int callingUid,
+ int realCallingUid, int realCallingPid, int userId,
+ boolean allowBackgroundActivityStarts,
@Nullable IBinder backgroundActivityStartsToken,
@Nullable int[] broadcastAllowList) {
intent = new Intent(intent);
@@ -13139,8 +13141,8 @@
final BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
- requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
- resultCode, resultData, resultExtras, ordered, sticky, false, userId,
+ requiredPermissions, excludedPermissions, appOp, brOptions, registeredReceivers,
+ resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId,
allowBackgroundActivityStarts, backgroundActivityStartsToken,
timeoutExempt);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
@@ -13237,10 +13239,10 @@
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
- requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
- resultData, resultExtras, ordered, sticky, false, userId,
- allowBackgroundActivityStarts, backgroundActivityStartsToken,
- timeoutExempt);
+ requiredPermissions, excludedPermissions, appOp, brOptions,
+ receivers, resultTo, resultCode, resultData, resultExtras,
+ ordered, sticky, false, userId, allowBackgroundActivityStarts,
+ backgroundActivityStartsToken, timeoutExempt);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
@@ -13366,14 +13368,14 @@
String[] requiredPermissions, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
return broadcastIntentWithFeature(caller, null, intent, resolvedType, resultTo, resultCode,
- resultData, resultExtras, requiredPermissions, appOp, bOptions, serialized, sticky,
- userId);
+ resultData, resultExtras, requiredPermissions, null, appOp, bOptions, serialized,
+ sticky, userId);
}
public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
- String[] requiredPermissions, int appOp, Bundle bOptions,
+ String[] requiredPermissions, String[] excludedPermissions, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
@@ -13388,8 +13390,8 @@
return broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
- requiredPermissions, appOp, bOptions, serialized, sticky,
- callingPid, callingUid, callingUid, callingPid, userId);
+ requiredPermissions, excludedPermissions, appOp, bOptions, serialized,
+ sticky, callingPid, callingUid, callingUid, callingPid, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -13410,7 +13412,7 @@
: new String[] {requiredPermission};
try {
return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
- resultTo, resultCode, resultData, resultExtras, requiredPermissions,
+ resultTo, resultCode, resultData, resultExtras, requiredPermissions, null,
OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
realCallingPid, userId, allowBackgroundActivityStarts,
backgroundActivityStartsToken,
@@ -15615,7 +15617,7 @@
return ActivityManagerService.this.broadcastIntentLocked(null /*callerApp*/,
null /*callerPackage*/, null /*callingFeatureId*/, intent,
null /*resolvedType*/, resultTo, 0 /*resultCode*/, null /*resultData*/,
- null /*resultExtras*/, requiredPermissions, AppOpsManager.OP_NONE,
+ null /*resultExtras*/, requiredPermissions, null, AppOpsManager.OP_NONE,
bOptions /*options*/, serialized, false /*sticky*/, callingPid,
callingUid, callingUid, callingPid, userId,
false /*allowBackgroundStarts*/,
@@ -15740,8 +15742,8 @@
| Intent.FLAG_RECEIVER_FOREGROUND
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
- OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
- Binder.getCallingPid(), UserHandle.USER_ALL);
+ null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL);
if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
@@ -15751,8 +15753,9 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
- OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
- Binder.getCallingPid(), UserHandle.USER_ALL);
+ null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ UserHandle.USER_ALL);
}
// Send a broadcast to PackageInstallers if the configuration change is interesting
@@ -15766,7 +15769,7 @@
String[] permissions =
new String[] { android.Manifest.permission.INSTALL_PACKAGES };
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null,
- permissions, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ permissions, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL);
}
}
@@ -15791,7 +15794,7 @@
}
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
- OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(),
+ null, OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(),
Binder.getCallingPid(), UserHandle.USER_ALL);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 42aac29..6fa8ecd4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -760,7 +760,7 @@
pw.flush();
Bundle bundle = mBroadcastOptions == null ? null : mBroadcastOptions.toBundle();
mInterface.broadcastIntentWithFeature(null, null, intent, null, receiver, 0, null, null,
- requiredPermissions, android.app.AppOpsManager.OP_NONE, bundle, true, false,
+ requiredPermissions, null, android.app.AppOpsManager.OP_NONE, bundle, true, false,
mUserId);
if (!mAsync) {
receiver.waitForFinish();
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index a5474d0b..f0b116c 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1419,6 +1419,7 @@
skip = true;
}
}
+
boolean isSingleton = false;
try {
isSingleton = mService.isSingleton(info.activityInfo.processName,
@@ -1553,6 +1554,37 @@
+ info.activityInfo.applicationInfo.uid + " : user is not running");
}
+ if (!skip && r.excludedPermissions != null && r.excludedPermissions.length > 0) {
+ for (int i = 0; i < r.excludedPermissions.length; i++) {
+ String excludedPermission = r.excludedPermissions[i];
+ try {
+ perm = AppGlobals.getPackageManager()
+ .checkPermission(excludedPermission,
+ info.activityInfo.applicationInfo.packageName,
+ UserHandle
+ .getUserId(info.activityInfo.applicationInfo.uid));
+ } catch (RemoteException e) {
+ perm = PackageManager.PERMISSION_DENIED;
+ }
+
+ if (perm == PackageManager.PERMISSION_GRANTED) {
+ skip = true;
+ break;
+ }
+
+ int appOp = AppOpsManager.permissionToOpCode(excludedPermission);
+ if (appOp != AppOpsManager.OP_NONE) {
+ if (mService.getAppOpsManager().checkOpNoThrow(appOp,
+ info.activityInfo.applicationInfo.uid,
+ info.activityInfo.packageName)
+ == AppOpsManager.MODE_ALLOWED) {
+ skip = true;
+ break;
+ }
+ }
+ }
+ }
+
if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
r.requiredPermissions != null && r.requiredPermissions.length > 0) {
for (int i = 0; i < r.requiredPermissions.length; i++) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 198ba34..8015596 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -62,6 +62,7 @@
final int userId; // user id this broadcast was for
final String resolvedType; // the resolved data type
final String[] requiredPermissions; // permissions the caller has required
+ final String[] excludedPermissions; // permissions to exclude
final int appOp; // an app op that is associated with this broadcast
final BroadcastOptions options; // BroadcastOptions supplied by caller
final List receivers; // contains BroadcastFilter and ResolveInfo
@@ -142,6 +143,10 @@
pw.print(Arrays.toString(requiredPermissions));
pw.print(" appOp="); pw.println(appOp);
}
+ if (excludedPermissions != null && excludedPermissions.length > 0) {
+ pw.print(prefix); pw.print("excludedPermissions=");
+ pw.print(Arrays.toString(excludedPermissions));
+ }
if (options != null) {
pw.print(prefix); pw.print("options="); pw.println(options.toBundle());
}
@@ -240,11 +245,11 @@
Intent _intent, ProcessRecord _callerApp, String _callerPackage,
@Nullable String _callerFeatureId, int _callingPid, int _callingUid,
boolean _callerInstantApp, String _resolvedType,
- String[] _requiredPermissions, int _appOp, BroadcastOptions _options, List _receivers,
- IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras,
- boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId,
- boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken,
- boolean timeoutExempt) {
+ String[] _requiredPermissions, String[] _excludedPermissions, int _appOp,
+ BroadcastOptions _options, List _receivers, IIntentReceiver _resultTo, int _resultCode,
+ String _resultData, Bundle _resultExtras, boolean _serialized, boolean _sticky,
+ boolean _initialSticky, int _userId, boolean allowBackgroundActivityStarts,
+ @Nullable IBinder backgroundActivityStartsToken, boolean timeoutExempt) {
if (_intent == null) {
throw new NullPointerException("Can't construct with a null intent");
}
@@ -259,6 +264,7 @@
callerInstantApp = _callerInstantApp;
resolvedType = _resolvedType;
requiredPermissions = _requiredPermissions;
+ excludedPermissions = _excludedPermissions;
appOp = _appOp;
options = _options;
receivers = _receivers;
@@ -299,6 +305,7 @@
userId = from.userId;
resolvedType = from.resolvedType;
requiredPermissions = from.requiredPermissions;
+ excludedPermissions = from.excludedPermissions;
appOp = from.appOp;
options = from.options;
receivers = from.receivers;
@@ -356,8 +363,8 @@
// build a new BroadcastRecord around that single-target list
BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp, callerPackage,
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
- requiredPermissions, appOp, options, splitReceivers, resultTo, resultCode,
- resultData, resultExtras, ordered, sticky, initialSticky, userId,
+ requiredPermissions, excludedPermissions, appOp, options, splitReceivers, resultTo,
+ resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId,
allowBackgroundActivityStarts, mBackgroundActivityStartsToken, timeoutExempt);
split.splitToken = this.splitToken;
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index 984fe40..7562098 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -124,7 +124,7 @@
REASON_PRE_BOOT_COMPLETED, "");
synchronized (mService) {
mService.broadcastIntentLocked(null, null, null, mIntent, null, this, 0, null, null,
- null, AppOpsManager.OP_NONE, bOptions.toBundle(), true,
+ null, null, AppOpsManager.OP_NONE, bOptions.toBundle(), true,
false, ActivityManagerService.MY_PID,
Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId);
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a5d0e72..ba3e1fb 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2936,8 +2936,8 @@
// TODO b/64165549 Verify that mLock is not held before calling AMS methods
synchronized (mService) {
return mService.broadcastIntentLocked(null, null, null, intent, resolvedType,
- resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp,
- bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
+ resultTo, resultCode, resultData, resultExtras, requiredPermissions, null,
+ appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
realCallingPid, userId);
}
}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index b103def..b7ef10a 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -199,13 +199,12 @@
@Override
public void onPropertiesChanged(Properties properties) {
synchronized (mDeviceConfigLock) {
- for (String key : properties.getKeyset()) {
+ for (final String packageName : properties.getKeyset()) {
try {
// Check if the package is installed before caching it.
- final String packageName = keyToPackageName(key);
mPackageManager.getPackageInfo(packageName, 0);
final GamePackageConfiguration config =
- GamePackageConfiguration.fromProperties(key, properties);
+ GamePackageConfiguration.fromProperties(packageName, properties);
if (config.isValid()) {
putConfig(config);
} else {
@@ -290,8 +289,8 @@
private final String mPackageName;
private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs;
- private GamePackageConfiguration(String keyName) {
- mPackageName = keyToPackageName(keyName);
+ private GamePackageConfiguration(String packageName) {
+ mPackageName = packageName;
mModeConfigs = new ArrayMap<>();
}
@@ -563,9 +562,9 @@
}
}
- private void loadDeviceConfigLocked() {
+ void loadDeviceConfigLocked() {
final List<PackageInfo> packages = mPackageManager.getInstalledPackages(0);
- final String[] packageNames = packages.stream().map(e -> packageNameToKey(e.packageName))
+ final String[] packageNames = packages.stream().map(e -> e.packageName)
.toArray(String[]::new);
synchronized (mDeviceConfigLock) {
final Properties properties = DeviceConfig.getProperties(
@@ -680,8 +679,7 @@
case ACTION_PACKAGE_CHANGED:
synchronized (mDeviceConfigLock) {
Properties properties = DeviceConfig.getProperties(
- DeviceConfig.NAMESPACE_GAME_OVERLAY,
- packageNameToKey(packageName));
+ DeviceConfig.NAMESPACE_GAME_OVERLAY, packageName);
for (String key : properties.getKeyset()) {
GamePackageConfiguration config =
GamePackageConfiguration.fromProperties(key,
@@ -692,7 +690,9 @@
break;
case ACTION_PACKAGE_REMOVED:
disableCompatScale(packageName);
- mConfigs.remove(packageName);
+ synchronized (mDeviceConfigLock) {
+ mConfigs.remove(packageName);
+ }
break;
default:
// do nothing
@@ -710,23 +710,6 @@
mDeviceConfigListener = new DeviceConfigListener();
}
- /**
- * Valid package name characters are [a-zA-Z0-9_] with a '.' delimiter. Policy keys can only use
- * [a-zA-Z0-9_] so we must handle periods. We do this by appending a '_' to any existing
- * sequence of '_', then we replace all '.' chars with '_';
- */
- private static String packageNameToKey(String name) {
- return name.replaceAll("(_+)", "_$1").replaceAll("\\.", "_");
- }
-
- /**
- * Replace the last '_' in a sequence with '.' (this can be one or more chars), then replace the
- * resulting special case '_.' with just '_' to get the original package name.
- */
- private static String keyToPackageName(String key) {
- return key.replaceAll("(_)(?!\\1)", ".").replaceAll("_\\.", "_");
- }
-
private String dumpDeviceConfigs() {
StringBuilder out = new StringBuilder();
for (String key : mConfigs.keySet()) {
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 540589b..fd829fa 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -109,7 +109,7 @@
private final boolean mOatArtifactDeletionEnabled;
@VisibleForTesting
- boolean mIsServiceEnabled;
+ static boolean sIsServiceEnabled;
/**
* Initializes the system service.
@@ -165,7 +165,7 @@
});
}
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
- mIsServiceEnabled = isAppHibernationEnabled();
+ sIsServiceEnabled = isDeviceConfigAppHibernationEnabled();
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE_APP_HIBERNATION,
ActivityThread.currentApplication().getMainExecutor(),
@@ -536,7 +536,7 @@
private void onDeviceConfigChanged(Properties properties) {
for (String key : properties.getKeyset()) {
if (TextUtils.equals(KEY_APP_HIBERNATION_ENABLED, key)) {
- mIsServiceEnabled = isAppHibernationEnabled();
+ sIsServiceEnabled = isDeviceConfigAppHibernationEnabled();
break;
}
}
@@ -574,10 +574,10 @@
}
private boolean checkHibernationEnabled(String methodName) {
- if (!mIsServiceEnabled) {
+ if (!sIsServiceEnabled) {
Slog.w(TAG, String.format("Attempted to call %s on unsupported device.", methodName));
}
- return mIsServiceEnabled;
+ return sIsServiceEnabled;
}
private void dump(PrintWriter pw) {
@@ -725,6 +725,10 @@
* @return true if enabled, false otherwise
*/
public static boolean isAppHibernationEnabled() {
+ return sIsServiceEnabled;
+ }
+
+ private static boolean isDeviceConfigAppHibernationEnabled() {
return DeviceConfig.getBoolean(
NAMESPACE_APP_HIBERNATION,
KEY_APP_HIBERNATION_ENABLED,
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 419e686..3f07572 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1827,7 +1827,8 @@
}
}
- mHistoricalRegistry.clearHistory(uid, packageName);
+ mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::clearHistory,
+ mHistoricalRegistry, uid, packageName));
}
public void uidRemoved(int uid) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 282a12d..96bb73f 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -503,7 +503,7 @@
}
}
- private static final class BtDeviceConnectionInfo {
+ /*package*/ static final class BtDeviceConnectionInfo {
final @NonNull BluetoothDevice mDevice;
final @AudioService.BtProfileConnectionState int mState;
final int mProfile;
@@ -520,6 +520,14 @@
mVolume = vol;
}
+ BtDeviceConnectionInfo(@NonNull BtDeviceConnectionInfo info) {
+ mDevice = info.mDevice;
+ mState = info.mState;
+ mProfile = info.mProfile;
+ mSupprNoisy = info.mSupprNoisy;
+ mVolume = info.mVolume;
+ }
+
// redefine equality op so we can match messages intended for this device
@Override
public boolean equals(Object o) {
@@ -541,18 +549,19 @@
}
}
- /*package*/ void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
- int profile, boolean suppressNoisyIntent, int a2dpVolume) {
- final BtDeviceConnectionInfo info = new BtDeviceConnectionInfo(device, state, profile,
- suppressNoisyIntent, a2dpVolume);
-
- final String name = TextUtils.emptyIfNull(device.getName());
+ /**
+ * will block on mDeviceStateLock, which is held during an A2DP (dis) connection
+ * not just a simple message post
+ * @param info struct with the (dis)connection information
+ */
+ /*package*/ void queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ @NonNull BtDeviceConnectionInfo info) {
+ final String name = TextUtils.emptyIfNull(info.mDevice.getName());
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR
+ "postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent")
- .set(MediaMetrics.Property.STATE, state == BluetoothProfile.STATE_CONNECTED
+ .set(MediaMetrics.Property.STATE, info.mState == BluetoothProfile.STATE_CONNECTED
? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
- .set(MediaMetrics.Property.INDEX, a2dpVolume)
+ .set(MediaMetrics.Property.INDEX, info.mVolume)
.set(MediaMetrics.Property.NAME, name)
.record();
@@ -562,10 +571,10 @@
// when receiving a request to change the connection state of a device, this last
// request is the source of truth, so cancel all previous requests that are already in
// the handler
- removeScheduledA2dpEvents(device);
+ removeScheduledA2dpEvents(info.mDevice);
sendLMsgNoDelay(
- state == BluetoothProfile.STATE_CONNECTED
+ info.mState == BluetoothProfile.STATE_CONNECTED
? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION
: MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
SENDMSG_QUEUE, info);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9707ace..edacd40 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -310,6 +310,8 @@
private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35;
private static final int MSG_UPDATE_AUDIO_MODE = 36;
private static final int MSG_RECORDING_CONFIG_CHANGE = 37;
+ private static final int MSG_SET_A2DP_DEV_CONNECTION_STATE = 38;
+ private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
@@ -6114,7 +6116,7 @@
* See AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent()
*/
public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
+ @NonNull BluetoothDevice device, @BtProfileConnectionState int state,
int profile, boolean suppressNoisyIntent, int a2dpVolume) {
if (device == null) {
throw new IllegalArgumentException("Illegal null device");
@@ -6124,8 +6126,13 @@
throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
+ " (dis)connection, got " + state);
}
- mDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device, state,
- profile, suppressNoisyIntent, a2dpVolume);
+
+ AudioDeviceBroker.BtDeviceConnectionInfo info =
+ new AudioDeviceBroker.BtDeviceConnectionInfo(device, state,
+ profile, suppressNoisyIntent, a2dpVolume);
+ sendMsg(mAudioHandler, MSG_SET_A2DP_DEV_CONNECTION_STATE, SENDMSG_QUEUE,
+ 0 /*arg1*/, 0 /*arg2*/,
+ /*obj*/ info, 0 /*delay*/);
}
/** only public for mocking/spying, do not call outside of AudioService */
@@ -6143,7 +6150,8 @@
if (device == null) {
throw new IllegalArgumentException("Illegal null device");
}
- mDeviceBroker.postBluetoothA2dpDeviceConfigChange(device);
+ sendMsg(mAudioHandler, MSG_A2DP_DEV_CONFIG_CHANGE, SENDMSG_QUEUE, 0, 0,
+ /*obj*/ device, /*delay*/ 0);
}
private static final Set<Integer> DEVICE_MEDIA_UNMUTED_ON_PLUG_SET;
@@ -7505,6 +7513,15 @@
onUpdateAudioMode(msg.arg1, msg.arg2, (String) msg.obj, false /*force*/);
}
break;
+
+ case MSG_SET_A2DP_DEV_CONNECTION_STATE:
+ mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ (AudioDeviceBroker.BtDeviceConnectionInfo) msg.obj);
+ break;
+
+ case MSG_A2DP_DEV_CONFIG_CHANGE:
+ mDeviceBroker.postBluetoothA2dpDeviceConfigChange((BluetoothDevice) msg.obj);
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index c57d5af..52e8edf 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -454,8 +454,10 @@
}
final BluetoothDevice btDevice = deviceList.get(0);
// the device is guaranteed CONNECTED
- mDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(btDevice,
- BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK, true, -1);
+ mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ new AudioDeviceBroker.BtDeviceConnectionInfo(btDevice,
+ BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK,
+ true, -1));
}
/*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index cb7c568..a546a60 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -35,7 +35,6 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -51,6 +50,7 @@
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
@@ -338,18 +338,31 @@
private static final boolean DEFAULT_APP_ENABLED = true;
private static final boolean DEFAULT_ALWAYS_REQUIRE_CONFIRMATION = false;
+ // Some devices that shipped before S already have face-specific settings. Instead of
+ // migrating, which is complicated, let's just keep using the existing settings.
+ private final boolean mUseLegacyFaceOnlySettings;
+
+ // Only used for legacy face-only devices
private final Uri FACE_UNLOCK_KEYGUARD_ENABLED =
Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
private final Uri FACE_UNLOCK_APP_ENABLED =
Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_APP_ENABLED);
+
+ // Continues to be used, even though it's face-specific.
private final Uri FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION =
Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION);
+ // Used for all devices other than legacy face-only devices
+ private final Uri BIOMETRIC_KEYGUARD_ENABLED =
+ Settings.Secure.getUriFor(Settings.Secure.BIOMETRIC_KEYGUARD_ENABLED);
+ private final Uri BIOMETRIC_APP_ENABLED =
+ Settings.Secure.getUriFor(Settings.Secure.BIOMETRIC_APP_ENABLED);
+
private final ContentResolver mContentResolver;
private final List<BiometricService.EnabledOnKeyguardCallback> mCallbacks;
- private final Map<Integer, Boolean> mFaceEnabledOnKeyguard = new HashMap<>();
- private final Map<Integer, Boolean> mFaceEnabledForApps = new HashMap<>();
+ private final Map<Integer, Boolean> mBiometricEnabledOnKeyguard = new HashMap<>();
+ private final Map<Integer, Boolean> mBiometricEnabledForApps = new HashMap<>();
private final Map<Integer, Boolean> mFaceAlwaysRequireConfirmation = new HashMap<>();
/**
@@ -362,21 +375,44 @@
super(handler);
mContentResolver = context.getContentResolver();
mCallbacks = callbacks;
+
+ final boolean hasFingerprint = context.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
+ final boolean hasFace = context.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_FACE);
+
+ // Use the legacy setting on face-only devices that shipped on or before Q
+ mUseLegacyFaceOnlySettings =
+ Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.Q
+ && hasFace && !hasFingerprint;
+
updateContentObserver();
}
public void updateContentObserver() {
mContentResolver.unregisterContentObserver(this);
- mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
- false /* notifyForDescendents */,
- this /* observer */,
- UserHandle.USER_ALL);
- mContentResolver.registerContentObserver(FACE_UNLOCK_APP_ENABLED,
- false /* notifyForDescendents */,
- this /* observer */,
- UserHandle.USER_ALL);
+
+ if (mUseLegacyFaceOnlySettings) {
+ mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
+ false /* notifyForDescendants */,
+ this /* observer */,
+ UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(FACE_UNLOCK_APP_ENABLED,
+ false /* notifyForDescendants */,
+ this /* observer */,
+ UserHandle.USER_ALL);
+ } else {
+ mContentResolver.registerContentObserver(BIOMETRIC_KEYGUARD_ENABLED,
+ false /* notifyForDescendants */,
+ this /* observer */,
+ UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(BIOMETRIC_APP_ENABLED,
+ false /* notifyForDescendants */,
+ this /* observer */,
+ UserHandle.USER_ALL);
+ }
mContentResolver.registerContentObserver(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
- false /* notifyForDescendents */,
+ false /* notifyForDescendants */,
this /* observer */,
UserHandle.USER_ALL);
}
@@ -384,7 +420,7 @@
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) {
- mFaceEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser(
+ mBiometricEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser(
mContentResolver,
Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED,
DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */,
@@ -394,7 +430,7 @@
notifyEnabledOnKeyguardCallbacks(userId);
}
} else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) {
- mFaceEnabledForApps.put(userId, Settings.Secure.getIntForUser(
+ mBiometricEnabledForApps.put(userId, Settings.Secure.getIntForUser(
mContentResolver,
Settings.Secure.FACE_UNLOCK_APP_ENABLED,
DEFAULT_APP_ENABLED ? 1 : 0 /* default */,
@@ -405,22 +441,45 @@
Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
DEFAULT_ALWAYS_REQUIRE_CONFIRMATION ? 1 : 0 /* default */,
userId) != 0);
+ } else if (BIOMETRIC_KEYGUARD_ENABLED.equals(uri)) {
+ mBiometricEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.BIOMETRIC_KEYGUARD_ENABLED,
+ DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */,
+ userId) != 0);
+
+ if (userId == ActivityManager.getCurrentUser() && !selfChange) {
+ notifyEnabledOnKeyguardCallbacks(userId);
+ }
+ } else if (BIOMETRIC_APP_ENABLED.equals(uri)) {
+ mBiometricEnabledForApps.put(userId, Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.BIOMETRIC_APP_ENABLED,
+ DEFAULT_APP_ENABLED ? 1 : 0 /* default */,
+ userId) != 0);
}
}
- public boolean getFaceEnabledOnKeyguard() {
- final int user = ActivityManager.getCurrentUser();
- if (!mFaceEnabledOnKeyguard.containsKey(user)) {
- onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, user);
+ public boolean getEnabledOnKeyguard(int userId) {
+ if (!mBiometricEnabledOnKeyguard.containsKey(userId)) {
+ if (mUseLegacyFaceOnlySettings) {
+ onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, userId);
+ } else {
+ onChange(true /* selfChange */, BIOMETRIC_KEYGUARD_ENABLED, userId);
+ }
}
- return mFaceEnabledOnKeyguard.get(user);
+ return mBiometricEnabledOnKeyguard.get(userId);
}
- public boolean getFaceEnabledForApps(int userId) {
- if (!mFaceEnabledForApps.containsKey(userId)) {
- onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId);
+ public boolean getEnabledForApps(int userId) {
+ if (!mBiometricEnabledForApps.containsKey(userId)) {
+ if (mUseLegacyFaceOnlySettings) {
+ onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId);
+ } else {
+ onChange(true /* selfChange */, BIOMETRIC_APP_ENABLED, userId);
+ }
}
- return mFaceEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED);
+ return mBiometricEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED);
}
public boolean getConfirmationAlwaysRequired(@BiometricAuthenticator.Modality int modality,
@@ -442,8 +501,8 @@
void notifyEnabledOnKeyguardCallbacks(int userId) {
List<EnabledOnKeyguardCallback> callbacks = mCallbacks;
for (int i = 0; i < callbacks.size(); i++) {
- callbacks.get(i).notify(BiometricSourceType.FACE,
- mFaceEnabledOnKeyguard.getOrDefault(userId, DEFAULT_KEYGUARD_ENABLED),
+ callbacks.get(i).notify(
+ mBiometricEnabledOnKeyguard.getOrDefault(userId, DEFAULT_KEYGUARD_ENABLED),
userId);
}
}
@@ -462,9 +521,9 @@
}
}
- void notify(BiometricSourceType sourceType, boolean enabled, int userId) {
+ void notify(boolean enabled, int userId) {
try {
- mCallback.onChanged(sourceType, enabled, userId);
+ mCallback.onChanged(enabled, userId);
} catch (DeadObjectException e) {
Slog.w(TAG, "Death while invoking notify", e);
mEnabledOnKeyguardCallbacks.remove(this);
@@ -747,8 +806,8 @@
mEnabledOnKeyguardCallbacks.add(new EnabledOnKeyguardCallback(callback));
try {
- callback.onChanged(BiometricSourceType.FACE,
- mSettingObserver.getFaceEnabledOnKeyguard(), callingUserId);
+ callback.onChanged(mSettingObserver.getEnabledOnKeyguard(callingUserId),
+ callingUserId);
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception", e);
}
@@ -1347,6 +1406,9 @@
}
private void dumpInternal(PrintWriter pw) {
+ pw.println("Legacy Settings: " + mSettingObserver.mUseLegacyFaceOnlySettings);
+ pw.println();
+
pw.println("Sensors:");
for (BiometricSensor sensor : mSensors) {
pw.println(" " + sensor);
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 262cb36..c4bd18b 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -197,17 +197,7 @@
private static boolean isEnabledForApp(BiometricService.SettingObserver settingObserver,
@BiometricAuthenticator.Modality int modality, int userId) {
- switch (modality) {
- case TYPE_FINGERPRINT:
- return true;
- case TYPE_IRIS:
- return true;
- case TYPE_FACE:
- return settingObserver.getFaceEnabledForApps(userId);
- default:
- Slog.w(TAG, "Unsupported modality: " + modality);
- return false;
- }
+ return settingObserver.getEnabledForApps(userId);
}
private static boolean isBiometricDisabledByDevicePolicy(
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 d56fd12..b795b54 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
@@ -375,12 +375,12 @@
}
@Override
- public void onFeaturesRetrieved(byte[] features, int enrollmentId) {
+ public void onFeaturesRetrieved(byte[] features) {
}
@Override
- public void onFeatureSet(int enrollmentId, byte feature) {
+ public void onFeatureSet(byte feature) {
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
index c63af7e..f1bfd53 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java
@@ -32,6 +32,7 @@
*/
public class TestHal extends IFace.Stub {
private static final String TAG = "face.aidl.TestHal";
+
@Override
public SensorProps[] getSensorProps() {
Slog.w(TAG, "getSensorProps");
@@ -102,16 +103,16 @@
}
@Override
- public void getFeatures(int enrollmentId) throws RemoteException {
+ public void getFeatures() throws RemoteException {
Slog.w(TAG, "getFeatures");
- cb.onFeaturesRetrieved(new byte[0], enrollmentId);
+ cb.onFeaturesRetrieved(new byte[0]);
}
@Override
- public void setFeature(HardwareAuthToken hat, int enrollmentId,
- byte feature, boolean enabled) throws RemoteException {
+ public void setFeature(HardwareAuthToken hat, byte feature, boolean enabled)
+ throws RemoteException {
Slog.w(TAG, "setFeature");
- cb.onFeatureSet(enrollmentId, feature);
+ cb.onFeatureSet(feature);
}
@Override
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 1bbcede..b2d35f4 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -15,9 +15,6 @@
*/
package com.android.server.camera;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.os.Build.VERSION_CODES.M;
import android.annotation.IntDef;
@@ -27,15 +24,12 @@
import android.app.ActivityTaskManager;
import android.app.TaskStackListener;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.hardware.CameraSessionStats;
import android.hardware.CameraStreamStats;
import android.hardware.ICameraService;
@@ -43,7 +37,6 @@
import android.hardware.camera2.CameraMetadata;
import android.hardware.display.DisplayManager;
import android.media.AudioManager;
-import android.metrics.LogMaker;
import android.nfc.INfcAdapter;
import android.os.Binder;
import android.os.Handler;
@@ -60,17 +53,16 @@
import android.util.Log;
import android.util.Slog;
import android.view.Display;
+import android.view.IDisplayWindowListener;
import android.view.Surface;
+import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
import com.android.framework.protobuf.nano.MessageNano;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import com.android.server.wm.WindowManagerInternal;
import java.lang.annotation.Retention;
@@ -223,6 +215,37 @@
}
}
+ private final class DisplayWindowListener extends IDisplayWindowListener.Stub {
+
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ ICameraService cs = getCameraServiceRawLocked();
+ if (cs == null) return;
+
+ try {
+ cs.notifyDisplayConfigurationChange();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e);
+ // Not much we can do if camera service is dead.
+ }
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) { }
+
+ @Override
+ public void onDisplayRemoved(int displayId) { }
+
+ @Override
+ public void onFixedRotationStarted(int displayId, int newRotation) { }
+
+ @Override
+ public void onFixedRotationFinished(int displayId) { }
+ }
+
+
+ private final DisplayWindowListener mDisplayWindowListener = new DisplayWindowListener();
+
private final TaskStateHandler mTaskStackListener = new TaskStateHandler();
private final class TaskInfo {
@@ -236,13 +259,12 @@
private final class TaskStateHandler extends TaskStackListener {
private final Object mMapLock = new Object();
- // maps the current top level task id to its corresponding package name
+ // maps the package name to its corresponding current top level task id
@GuardedBy("mMapLock")
private final ArrayMap<String, TaskInfo> mTaskInfoMap = new ArrayMap<>();
@Override
- public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
- throws RemoteException {
+ public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
synchronized (mMapLock) {
TaskInfo info = new TaskInfo();
info.frontTaskId = taskInfo.taskId;
@@ -257,7 +279,7 @@
}
@Override
- public void onTaskRemoved(int taskId) throws RemoteException {
+ public void onTaskRemoved(int taskId) {
synchronized (mMapLock) {
for (Map.Entry<String, TaskInfo> entry : mTaskInfoMap.entrySet()){
if (entry.getValue().frontTaskId == taskId) {
@@ -319,7 +341,7 @@
/**
* Gets whether crop-rotate-scale is needed.
*/
- private boolean getNeedCropRotateScale(Context ctx, String packageName,
+ private boolean getNeedCropRotateScale(@NonNull Context ctx, @NonNull String packageName,
@Nullable TaskInfo taskInfo, int sensorOrientation, int lensFacing) {
if (taskInfo == null) {
return false;
@@ -334,7 +356,7 @@
// Only enable the crop-rotate-scale workaround if the app targets M or below and is not
// resizeable.
- if ((ctx != null) && !isMOrBelow(ctx, packageName) && taskInfo.isResizeable) {
+ if (!isMOrBelow(ctx, packageName) && taskInfo.isResizeable) {
Slog.v(TAG,
"The activity is N or above and claims to support resizeable-activity. "
+ "Crop-rotate-scale is disabled.");
@@ -372,12 +394,8 @@
taskInfo.isFixedOrientationLandscape);
// We need to do crop-rotate-scale when camera is landscape and activity is portrait or
// vice versa.
- if ((taskInfo.isFixedOrientationPortrait && landscapeCamera)
- || (taskInfo.isFixedOrientationLandscape && !landscapeCamera)) {
- return true;
- } else {
- return false;
- }
+ return (taskInfo.isFixedOrientationPortrait && landscapeCamera)
+ || (taskInfo.isFixedOrientationLandscape && !landscapeCamera);
}
@Override
@@ -389,14 +407,10 @@
return false;
}
- // A few remaining todos:
- // 1) Do the same check when working in WM compatible mode. The sequence needs
- // to be adjusted and use orientation events as triggers for all active camera
- // clients.
- // 2) Modify the sensor orientation in camera characteristics along with any 3A regions
- // in capture requests/results to account for thea physical rotation. The former
- // is somewhat tricky as it assumes that camera clients always check for the current
- // value by retrieving the camera characteristics from the camera device.
+ // TODO: Modify the sensor orientation in camera characteristics along with any 3A
+ // regions in capture requests/results to account for thea physical rotation. The
+ // former is somewhat tricky as it assumes that camera clients always check for the
+ // current value by retrieving the camera characteristics from the camera device.
return getNeedCropRotateScale(mContext, packageName,
mTaskStackListener.getFrontTaskInfo(packageName), sensorOrientation,
lensFacing);
@@ -522,18 +536,25 @@
publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
publishLocalService(CameraServiceProxy.class, this);
-
- try {
- ActivityTaskManager.getService().registerTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to register task stack listener!");
- }
}
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_BOOT_COMPLETED) {
CameraStatsJobService.schedule(mContext);
+
+ try {
+ ActivityTaskManager.getService().registerTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register task stack listener!");
+ }
+
+ try {
+ WindowManagerGlobal.getWindowManagerService().registerDisplayWindowListener(
+ mDisplayWindowListener);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register display window listener!");
+ }
}
}
diff --git a/services/core/java/com/android/server/connectivity/FullScore.java b/services/core/java/com/android/server/connectivity/FullScore.java
index 52da566..14cec09 100644
--- a/services/core/java/com/android/server/connectivity/FullScore.java
+++ b/services/core/java/com/android/server/connectivity/FullScore.java
@@ -259,6 +259,14 @@
}
/**
+ * Returns this score but validated.
+ */
+ public FullScore asValidated() {
+ return new FullScore(mLegacyInt, mPolicies | (1L << POLICY_IS_VALIDATED),
+ mKeepConnectedReason);
+ }
+
+ /**
* For backward compatibility, get the legacy int.
* This will be removed before S is published.
*/
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 4d310cb..18becd4 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -59,6 +59,7 @@
import com.android.server.ConnectivityService;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
@@ -281,6 +282,9 @@
*/
public static final int ARG_AGENT_SUCCESS = 1;
+ // How long this network should linger for.
+ private int mLingerDurationMs;
+
// All inactivity timers for this network, sorted by expiry time. A timer is added whenever
// a request is moved to a network with a better score, regardless of whether the network is or
// was lingering or not. An inactivity timer is also added when a network connects
@@ -349,7 +353,8 @@
@NonNull NetworkScore score, Context context,
Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid,
- QosCallbackTracker qosCallbackTracker, ConnectivityService.Dependencies deps) {
+ int lingerDurationMs, QosCallbackTracker qosCallbackTracker,
+ ConnectivityService.Dependencies deps) {
Objects.requireNonNull(net);
Objects.requireNonNull(info);
Objects.requireNonNull(lp);
@@ -370,6 +375,7 @@
mHandler = handler;
this.factorySerialNumber = factorySerialNumber;
this.creatorUid = creatorUid;
+ mLingerDurationMs = lingerDurationMs;
mQosCallbackTracker = qosCallbackTracker;
}
@@ -685,6 +691,12 @@
mHandler.obtainMessage(NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED,
teardownDelayMs, 0, new Pair<>(NetworkAgentInfo.this, null)).sendToTarget();
}
+
+ @Override
+ public void sendLingerDuration(final int durationMs) {
+ mHandler.obtainMessage(NetworkAgent.EVENT_LINGER_DURATION_CHANGED,
+ new Pair<>(NetworkAgentInfo.this, durationMs)).sendToTarget();
+ }
}
/**
@@ -938,6 +950,26 @@
}
/**
+ * Returns a Scoreable identical to this NAI, but validated.
+ *
+ * This is useful to probe what scoring would be if this network validated, to know
+ * whether to provisionally keep a network that may or may not validate.
+ *
+ * @return a Scoreable identical to this NAI, but validated.
+ */
+ public NetworkRanker.Scoreable getValidatedScoreable() {
+ return new NetworkRanker.Scoreable() {
+ @Override public FullScore getScore() {
+ return mScore.asValidated();
+ }
+
+ @Override public NetworkCapabilities getCapsNoCopy() {
+ return networkCapabilities;
+ }
+ };
+ }
+
+ /**
* Return a {@link NetworkStateSnapshot} for this network.
*/
@NonNull
@@ -954,13 +986,14 @@
/**
* Sets the specified requestId to linger on this network for the specified time. Called by
- * ConnectivityService when the request is moved to another network with a higher score, or
+ * ConnectivityService when any request is moved to another network with a higher score, or
* when a network is newly created.
*
* @param requestId The requestId of the request that no longer need to be served by this
* network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the
- * {@code LingerTimer} for a newly created network.
+ * {@code InactivityTimer} for a newly created network.
*/
+ // TODO: Consider creating a dedicated function for nascent network, e.g. start/stopNascent.
public void lingerRequest(int requestId, long now, long duration) {
if (mInactivityTimerForRequest.get(requestId) != null) {
// Cannot happen. Once a request is lingering on a particular network, we cannot
@@ -976,6 +1009,19 @@
}
/**
+ * Sets the specified requestId to linger on this network for the timeout set when
+ * initializing or modified by {@link #setLingerDuration(int)}. Called by
+ * ConnectivityService when any request is moved to another network with a higher score.
+ *
+ * @param requestId The requestId of the request that no longer need to be served by this
+ * network.
+ * @param now current system timestamp obtained by {@code SystemClock.elapsedRealtime}.
+ */
+ public void lingerRequest(int requestId, long now) {
+ lingerRequest(requestId, now, mLingerDurationMs);
+ }
+
+ /**
* Cancel lingering. Called by ConnectivityService when a request is added to this network.
* Returns true if the given requestId was lingering on this network, false otherwise.
*/
@@ -1012,6 +1058,7 @@
}
if (newExpiry > 0) {
+ // If the newExpiry timestamp is in the past, the wakeup message will fire immediately.
mInactivityMessage = new WakeupMessage(
mContext, mHandler,
"NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */,
@@ -1041,8 +1088,33 @@
}
/**
- * Return whether the network is just connected and about to be torn down because of not
- * satisfying any request.
+ * Set the linger duration for this NAI.
+ * @param durationMs The new linger duration, in milliseconds.
+ */
+ public void setLingerDuration(final int durationMs) {
+ final long diff = durationMs - mLingerDurationMs;
+ final ArrayList<InactivityTimer> newTimers = new ArrayList<>();
+ for (final InactivityTimer timer : mInactivityTimers) {
+ if (timer.requestId == NetworkRequest.REQUEST_ID_NONE) {
+ // Don't touch nascent timer, re-add as is.
+ newTimers.add(timer);
+ } else {
+ newTimers.add(new InactivityTimer(timer.requestId, timer.expiryMs + diff));
+ }
+ }
+ mInactivityTimers.clear();
+ mInactivityTimers.addAll(newTimers);
+ updateInactivityTimer();
+ mLingerDurationMs = durationMs;
+ }
+
+ /**
+ * Return whether the network satisfies no request, but is still being kept up
+ * because it has just connected less than
+ * {@code ConnectivityService#DEFAULT_NASCENT_DELAY_MS}ms ago and is thus still considered
+ * nascent. Note that nascent mechanism uses inactivity timer which isn't
+ * associated with a request. Thus, use {@link NetworkRequest#REQUEST_ID_NONE} to identify it.
+ *
*/
public boolean isNascent() {
return mInactive && mInactivityTimers.size() == 1
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
index 2b345e5..346af44 100644
--- a/services/core/java/com/android/server/connectivity/NetworkRanker.java
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -234,16 +234,17 @@
NetworkAgentInfo bestNetwork = null;
int bestScore = Integer.MIN_VALUE;
for (final NetworkAgentInfo nai : nais) {
- if (nai.getCurrentScore() > bestScore) {
+ final int naiScore = nai.getCurrentScore();
+ if (naiScore > bestScore) {
bestNetwork = nai;
- bestScore = nai.getCurrentScore();
+ bestScore = naiScore;
}
}
return bestNetwork;
}
/**
- * Returns whether an offer has a chance to beat a champion network for a request.
+ * Returns whether a {@link Scoreable} has a chance to beat a champion network for a request.
*
* Offers are sent by network providers when they think they might be able to make a network
* with the characteristics contained in the offer. If the offer has no chance to beat
@@ -257,15 +258,15 @@
*
* @param request The request to evaluate against.
* @param champion The currently best network for this request.
- * @param offer The offer.
+ * @param contestant The offer.
* @return Whether the offer stands a chance to beat the champion.
*/
public boolean mightBeat(@NonNull final NetworkRequest request,
@Nullable final NetworkAgentInfo champion,
- @NonNull final NetworkOffer offer) {
+ @NonNull final Scoreable contestant) {
// If this network can't even satisfy the request then it can't beat anything, not
// even an absence of network. It can't satisfy it anyway.
- if (!request.canBeSatisfiedBy(offer.caps)) return false;
+ if (!request.canBeSatisfiedBy(contestant.getCapsNoCopy())) return false;
// If there is no satisfying network, then this network can beat, because some network
// is always better than no network.
if (null == champion) return true;
@@ -274,25 +275,24 @@
// Otherwise rank them.
final ArrayList<Scoreable> candidates = new ArrayList<>();
candidates.add(champion);
- candidates.add(offer);
- return offer == getBestNetworkByPolicy(candidates, champion);
+ candidates.add(contestant);
+ return contestant == getBestNetworkByPolicy(candidates, champion);
} else {
- return mightBeatByLegacyInt(request, champion.getScore(), offer);
+ return mightBeatByLegacyInt(champion.getScore(), contestant);
}
}
/**
- * Returns whether an offer might beat a champion according to the legacy int.
+ * Returns whether a contestant might beat a champion according to the legacy int.
*/
- public boolean mightBeatByLegacyInt(@NonNull final NetworkRequest request,
- @Nullable final FullScore championScore,
- @NonNull final NetworkOffer offer) {
+ private boolean mightBeatByLegacyInt(@Nullable final FullScore championScore,
+ @NonNull final Scoreable contestant) {
final int offerIntScore;
- if (offer.caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ if (contestant.getCapsNoCopy().hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
// If the offer might have Internet access, then it might validate.
- offerIntScore = offer.score.getLegacyIntAsValidated();
+ offerIntScore = contestant.getScore().getLegacyIntAsValidated();
} else {
- offerIntScore = offer.score.getLegacyInt();
+ offerIntScore = contestant.getScore().getLegacyInt();
}
return championScore.getLegacyInt() < offerIntScore;
}
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 fb14fbd..c0ab093 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -502,8 +502,8 @@
Slog.e(TAG, "Failed to lookup font file that has " + font.getPostScriptName());
return null;
}
- resolvedFonts.add(new FontConfig.Font(info.mFile, null, font.getFontStyle(),
- font.getIndex(), font.getFontVariationSettings(), null));
+ resolvedFonts.add(new FontConfig.Font(info.mFile, null, info.getPostScriptName(),
+ font.getFontStyle(), font.getIndex(), font.getFontVariationSettings(), null));
}
return new FontConfig.FontFamily(resolvedFonts, fontFamily.getName(),
null, FontConfig.FontFamily.VARIANT_DEFAULT);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 3678b19..6114094 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -222,7 +222,6 @@
super.disableDevice(initiatedByCec, callback);
assertRunOnServiceThread();
mService.unregisterTvInputCallback(mTvInputCallback);
- // TODO(b/129088603): check disableDevice and onStandby behaviors per spec
}
@Override
@@ -847,7 +846,6 @@
}
protected void switchToAudioInput() {
- // TODO(b/111396634): switch input according to PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT
}
protected boolean isDirectConnectToTv() {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 7994fcc..94a5099 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3177,7 +3177,7 @@
@BinderThread
@Override
- public void reportPerceptible(IBinder windowToken, boolean perceptible) {
+ public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) {
Objects.requireNonNull(windowToken, "windowToken must not be null");
int uid = Binder.getCallingUid();
synchronized (mMethodMap) {
@@ -5992,9 +5992,8 @@
@BinderThread
@Override
- public void reportStartInput(IBinder startInputToken, IVoidResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback,
- () -> mImms.reportStartInput(mToken, startInputToken));
+ public void reportStartInputAsync(IBinder startInputToken) {
+ mImms.reportStartInput(mToken, startInputToken);
}
@BinderThread
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 6244743..885093d 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1840,7 +1840,7 @@
@BinderThread
@Override
- public void reportPerceptible(IBinder windowClient, boolean perceptible) {
+ public void reportPerceptibleAsync(IBinder windowClient, boolean perceptible) {
reportNotSupported();
}
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index 045e06d0..2ffc62a 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -178,8 +178,9 @@
}
/** Logs that a provider has entered or exited stationary throttling. */
- public void logProviderStationaryThrottled(String provider, boolean throttled) {
- addLogEvent(EVENT_PROVIDER_STATIONARY_THROTTLED, provider, throttled);
+ public void logProviderStationaryThrottled(String provider, boolean throttled,
+ ProviderRequest request) {
+ addLogEvent(EVENT_PROVIDER_STATIONARY_THROTTLED, provider, throttled, request);
}
/** Logs that the location power save mode has changed. */
@@ -217,7 +218,7 @@
(Integer) args[1], (CallerIdentity) args[2]);
case EVENT_PROVIDER_STATIONARY_THROTTLED:
return new ProviderStationaryThrottledEvent(timeDelta, (String) args[0],
- (Boolean) args[1]);
+ (Boolean) args[1], (ProviderRequest) args[2]);
case EVENT_LOCATION_POWER_SAVE_MODE_CHANGE:
return new LocationPowerSaveModeEvent(timeDelta, (Integer) args[0]);
default:
@@ -355,17 +356,19 @@
private static final class ProviderStationaryThrottledEvent extends ProviderEvent {
private final boolean mStationaryThrottled;
+ private final ProviderRequest mRequest;
ProviderStationaryThrottledEvent(long timeDelta, String provider,
- boolean stationaryThrottled) {
+ boolean stationaryThrottled, ProviderRequest request) {
super(timeDelta, provider);
mStationaryThrottled = stationaryThrottled;
+ mRequest = request;
}
@Override
public String getLogString() {
return mProvider + " provider stationary/idle " + (mStationaryThrottled ? "throttled"
- : "unthrottled");
+ : "unthrottled") + ", request = " + mRequest;
}
}
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 102263b..8a2dfa8 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -25,12 +25,12 @@
import static android.location.LocationManager.KEY_PROVIDER_ENABLED;
import static android.location.LocationManager.PASSIVE_PROVIDER;
import static android.os.IPowerManager.LOCATION_MODE_NO_CHANGE;
+import static android.os.PowerExemptionManager.REASON_LOCATION_PROVIDER;
+import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
-import static android.os.PowerWhitelistManager.REASON_LOCATION_PROVIDER;
-import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static com.android.server.location.LocationManagerService.D;
import static com.android.server.location.LocationManagerService.TAG;
@@ -148,10 +148,10 @@
private static final long MAX_HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000;
// max age of a location before it is no longer considered "current"
- private static final long MAX_CURRENT_LOCATION_AGE_MS = 10 * 1000;
+ private static final long MAX_CURRENT_LOCATION_AGE_MS = 30 * 1000;
// max timeout allowed for getting the current location
- private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30 * 1000;
+ private static final long MAX_GET_CURRENT_LOCATION_TIMEOUT_MS = 30 * 1000;
// max jitter allowed for min update interval as a percentage of the interval
private static final float FASTEST_INTERVAL_JITTER_PERCENTAGE = .10f;
@@ -230,7 +230,7 @@
options.setDontSendToRestrictedApps(true);
// allows apps to start a fg service in response to a location PI
options.setTemporaryAppAllowlist(TEMPORARY_APP_ALLOWLIST_DURATION_MS,
- TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
REASON_LOCATION_PROVIDER,
"");
@@ -1655,9 +1655,9 @@
public @Nullable ICancellationSignal getCurrentLocation(LocationRequest request,
CallerIdentity identity, int permissionLevel, ILocationCallback callback) {
- if (request.getDurationMillis() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
+ if (request.getDurationMillis() > MAX_GET_CURRENT_LOCATION_TIMEOUT_MS) {
request = new LocationRequest.Builder(request)
- .setDurationMillis(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS)
+ .setDurationMillis(MAX_GET_CURRENT_LOCATION_TIMEOUT_MS)
.build();
}
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index ab7e526..22a675a 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -206,7 +206,7 @@
if (D) {
Log.d(TAG, mName + " provider stationary throttled");
}
- EVENT_LOG.logProviderStationaryThrottled(mName, true);
+ EVENT_LOG.logProviderStationaryThrottled(mName, true, mOutgoingRequest);
}
if (mDeliverLastLocationCallback != null) {
@@ -224,7 +224,7 @@
}
} else {
if (oldThrottlingIntervalMs != INTERVAL_DISABLED) {
- EVENT_LOG.logProviderStationaryThrottled(mName, false);
+ EVENT_LOG.logProviderStationaryThrottled(mName, false, mOutgoingRequest);
if (D) {
Log.d(TAG, mName + " provider stationary unthrottled");
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 59f00a2..6cded50 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1261,7 +1261,8 @@
return getCredentialTypeInternal(userId) != CREDENTIAL_TYPE_NONE;
}
- private void setKeystorePassword(byte[] password, int userHandle) {
+ @VisibleForTesting /** Note: this method is overridden in unit tests */
+ void setKeystorePassword(byte[] password, int userHandle) {
AndroidKeyStoreMaintenance.onUserPasswordChanged(userHandle, password);
}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index c01523a..90694d0 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -205,6 +205,7 @@
Slog.i(TAG, "Using server based resume on reboot");
rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage);
} else {
+ Slog.i(TAG, "Using HAL based resume on reboot");
rebootEscrowProvider = new RebootEscrowProviderHalImpl();
}
@@ -239,7 +240,7 @@
return mKeyStoreManager;
}
- public RebootEscrowProviderInterface getRebootEscrowProvider() {
+ public RebootEscrowProviderInterface createRebootEscrowProviderIfNeeded() {
// Initialize for the provider lazily. Because the device_config and service
// implementation apps may change when system server is running.
if (mRebootEscrowProvider == null) {
@@ -249,6 +250,14 @@
return mRebootEscrowProvider;
}
+ public RebootEscrowProviderInterface getRebootEscrowProvider() {
+ return mRebootEscrowProvider;
+ }
+
+ public void clearRebootEscrowProvider() {
+ mRebootEscrowProvider = null;
+ }
+
public int getBootCount() {
return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT,
0);
@@ -308,8 +317,6 @@
mStorage.removeRebootEscrow(user.id);
}
- // Clear the old key in keystore.
- mKeyStoreManager.clearKeyStoreEncryptionKey();
onEscrowRestoreComplete(false, attemptCount);
}
@@ -395,9 +402,6 @@
allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey, kk);
}
- // Clear the old key in keystore. A new key will be generated by new RoR requests.
- mKeyStoreManager.clearKeyStoreEncryptionKey();
-
if (!allUsersUnlocked && mLoadEscrowDataErrorCode == ERROR_NONE) {
mLoadEscrowDataErrorCode = ERROR_UNLOCK_ALL_USERS;
}
@@ -473,11 +477,17 @@
if (success || (previousBootCount != -1 && bootCountDelta <= BOOT_COUNT_TOLERANCE)) {
reportMetricOnRestoreComplete(success, attemptCount);
}
+
+ // Clear the old key in keystore. A new key will be generated by new RoR requests.
+ mKeyStoreManager.clearKeyStoreEncryptionKey();
+ // Clear the saved reboot escrow provider
+ mInjector.clearRebootEscrowProvider();
clearMetricsStorage();
}
private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) throws IOException {
- RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
+ RebootEscrowProviderInterface rebootEscrowProvider =
+ mInjector.createRebootEscrowProviderIfNeeded();
if (rebootEscrowProvider == null) {
Slog.w(TAG,
"Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
@@ -529,9 +539,8 @@
return;
}
- if (mInjector.getRebootEscrowProvider() == null) {
- Slog.w(TAG,
- "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
+ if (mInjector.createRebootEscrowProviderIfNeeded() == null) {
+ Slog.w(TAG, "Not storing escrow data, RebootEscrowProvider is unavailable");
return;
}
@@ -586,13 +595,17 @@
mRebootEscrowWanted = false;
setRebootEscrowReady(false);
- RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
+ // We want to clear the internal data inside the provider, so always try to create the
+ // provider.
+ RebootEscrowProviderInterface rebootEscrowProvider =
+ mInjector.createRebootEscrowProviderIfNeeded();
if (rebootEscrowProvider == null) {
Slog.w(TAG, "RebootEscrowProvider is unavailable for clear request");
} else {
rebootEscrowProvider.clearRebootEscrowKey();
}
+ mInjector.clearRebootEscrowProvider();
clearMetricsStorage();
List<UserInfo> users = mUserManager.getUsers();
@@ -610,8 +623,7 @@
RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
if (rebootEscrowProvider == null) {
- Slog.w(TAG,
- "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
+ Slog.w(TAG, "Not storing escrow key, RebootEscrowProvider is unavailable");
clearRebootEscrowIfNeeded();
return ARM_REBOOT_ERROR_NO_PROVIDER;
}
@@ -677,11 +689,12 @@
}
boolean prepareRebootEscrow() {
- if (mInjector.getRebootEscrowProvider() == null) {
+ clearRebootEscrowIfNeeded();
+ if (mInjector.createRebootEscrowProviderIfNeeded() == null) {
+ Slog.w(TAG, "No reboot escrow provider, skipping resume on reboot preparation.");
return false;
}
- clearRebootEscrowIfNeeded();
mRebootEscrowWanted = true;
mEventLog.addEntry(RebootEscrowEvent.REQUESTED_LSKF);
return true;
@@ -807,6 +820,10 @@
pw.print("mPendingRebootEscrowKey is ");
pw.println(keySet ? "set" : "not set");
+ RebootEscrowProviderInterface provider = mInjector.getRebootEscrowProvider();
+ String providerType = provider == null ? "null" : String.valueOf(provider.getType());
+ pw.print("RebootEscrowProvider type is " + providerType);
+
pw.println();
pw.println("Event log:");
pw.increaseIndent();
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index abcf4fb..b10d56b 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -384,7 +384,7 @@
if (mPlaybackState == null) {
return false;
}
- return mPlaybackState.isActive() == expected;
+ return mPlaybackState.isActiveState() == expected;
}
/**
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 05922b3..7e4e29e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2016,7 +2016,7 @@
if (LOGV) Slog.v(TAG, "updateNetworkRulesNL()");
Trace.traceBegin(TRACE_TAG_NETWORK, "updateNetworkRulesNL");
- final List<NetworkStateSnapshot> snapshots = mConnManager.getAllNetworkStateSnapshot();
+ final List<NetworkStateSnapshot> snapshots = mConnManager.getAllNetworkStateSnapshots();
// First, generate identities of all connected networks so we can
// quickly compare them against all defined policies below.
diff --git a/services/core/java/com/android/server/net/TEST_MAPPING b/services/core/java/com/android/server/net/TEST_MAPPING
index f707597..02095eb 100644
--- a/services/core/java/com/android/server/net/TEST_MAPPING
+++ b/services/core/java/com/android/server/net/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-large": [
{
"name": "CtsHostsideNetworkTests",
"file_patterns": ["(/|^)NetworkPolicy[^/]*\\.java"],
@@ -14,7 +14,9 @@
"exclude-annotation": "android.platform.test.annotations.FlakyTest"
}
]
- },
+ }
+ ],
+ "presubmit": [
{
"name": "FrameworksServicesTests",
"file_patterns": ["(/|^)NetworkPolicy[^/]*\\.java"],
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index 8bd3b1e..42b7c9d3 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -100,7 +100,7 @@
IntentFilter deletionFilter = new IntentFilter(ACTION_HISTORY_DELETION);
deletionFilter.addDataScheme(SCHEME_DELETION);
- mContext.registerReceiver(mFileCleaupReceiver, deletionFilter);
+ mContext.registerReceiver(mFileCleanupReceiver, deletionFilter);
}
public void init() {
@@ -273,13 +273,36 @@
}
}
+ /**
+ * Remove the first entry from the list of history files whose file matches the given file path.
+ *
+ * This method is necessary for anything that only has an absolute file path rather than an
+ * AtomicFile object from the list of history files.
+ *
+ * filePath should be an absolute path.
+ */
+ void removeFilePathFromHistory(String filePath) {
+ if (filePath == null) {
+ return;
+ }
+
+ Iterator<AtomicFile> historyFileItr = mHistoryFiles.iterator();
+ while (historyFileItr.hasNext()) {
+ final AtomicFile af = historyFileItr.next();
+ if (af != null && filePath.equals(af.getBaseFile().getAbsolutePath())) {
+ historyFileItr.remove();
+ return;
+ }
+ }
+ }
+
private void deleteFile(AtomicFile file) {
if (DEBUG) {
Slog.d(TAG, "Removed " + file.getBaseFile().getName());
}
file.delete();
// TODO: delete all relevant bitmaps, once they exist
- mHistoryFiles.remove(file);
+ removeFilePathFromHistory(file.getBaseFile().getAbsolutePath());
}
private void scheduleDeletion(File file, long creationTime, int retentionDays) {
@@ -342,7 +365,7 @@
}
}
- private final BroadcastReceiver mFileCleaupReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver mFileCleanupReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -358,7 +381,7 @@
Slog.d(TAG, "Removed " + fileToDelete.getBaseFile().getName());
}
fileToDelete.delete();
- mHistoryFiles.remove(fileToDelete);
+ removeFilePathFromHistory(filePath);
}
} catch (Exception e) {
Slog.e(TAG, "Failed to delete notification history file", e);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 3ddfe6c..52a5dc1 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -380,8 +380,6 @@
static final int INVALID_UID = -1;
static final String ROOT_PKG = "root";
- static final boolean ENABLE_BLOCKED_TOASTS = true;
-
static final String[] DEFAULT_ALLOWED_ADJUSTMENTS = new String[] {
Adjustment.KEY_CONTEXTUAL_ACTIONS,
Adjustment.KEY_TEXT_REPLIES,
@@ -737,19 +735,22 @@
final List<UserInfo> activeUsers = mUm.getUsers();
for (UserInfo userInfo : activeUsers) {
int userId = userInfo.getUserHandle().getIdentifier();
- if (isNASMigrationDone(userId)) {
+ if (isNASMigrationDone(userId) || mUm.isManagedProfile(userId)) {
continue;
}
if (mAssistants.hasUserSet(userId)) {
- mAssistants.loadDefaultsFromConfig(false);
ComponentName defaultFromConfig = mAssistants.getDefaultFromConfig();
List<ComponentName> allowedComponents = mAssistants.getAllowedComponents(userId);
- if (allowedComponents.contains(defaultFromConfig)) {
+ if (allowedComponents.size() == 0) {
+ setNASMigrationDone(userId);
+ mAssistants.clearDefaults();
+ continue;
+ } else if (allowedComponents.contains(defaultFromConfig)) {
setNASMigrationDone(userId);
mAssistants.resetDefaultFromConfig();
continue;
}
- //User selected different NAS or none, need onboarding
+ //User selected different NAS, need onboarding
enqueueNotificationInternal(getContext().getPackageName(),
getContext().getOpPackageName(), Binder.getCallingUid(),
Binder.getCallingPid(), TAG,
@@ -821,9 +822,11 @@
}
@VisibleForTesting
- void setNASMigrationDone(int userId) {
- Settings.Secure.putIntForUser(getContext().getContentResolver(),
- Settings.Secure.NAS_SETTINGS_UPDATED, 1, userId);
+ void setNASMigrationDone(int baseUserId) {
+ for (int profileId : mUm.getProfileIds(baseUserId, false)) {
+ Settings.Secure.putIntForUser(getContext().getContentResolver(),
+ Settings.Secure.NAS_SETTINGS_UPDATED, 1, profileId);
+ }
}
@VisibleForTesting
@@ -3165,34 +3168,11 @@
}
final int callingUid = Binder.getCallingUid();
- final UserHandle callingUser = Binder.getCallingUserHandle();
+ checkCallerIsSameApp(pkg);
final boolean isSystemToast = isCallerSystemOrPhone()
|| PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg);
- final boolean isPackageSuspended = isPackagePaused(pkg);
- final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg,
- callingUid);
-
- final boolean appIsForeground;
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- appIsForeground = mActivityManager.getUidImportance(callingUid)
- == IMPORTANCE_FOREGROUND;
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
-
- if (ENABLE_BLOCKED_TOASTS && !isSystemToast && ((notificationsDisabledForPackage
- && !appIsForeground) || isPackageSuspended)) {
- Slog.e(TAG, "Suppressing toast from package " + pkg
- + (isPackageSuspended ? " due to package suspended."
- : " by user request."));
- return;
- }
-
boolean isAppRenderedToast = (callback != null);
- if (blockToast(callingUid, isSystemToast, isAppRenderedToast)) {
- Slog.w(TAG, "Blocking custom toast from package " + pkg
- + " due to package not in the foreground at time the toast was posted");
+ if (!checkCanEnqueueToast(pkg, callingUid, isAppRenderedToast, isSystemToast)) {
return;
}
@@ -3246,6 +3226,39 @@
}
}
+ private boolean checkCanEnqueueToast(String pkg, int callingUid,
+ boolean isAppRenderedToast, boolean isSystemToast) {
+ final boolean isPackageSuspended = isPackagePaused(pkg);
+ final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg,
+ callingUid);
+
+ final boolean appIsForeground;
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ appIsForeground = mActivityManager.getUidImportance(callingUid)
+ == IMPORTANCE_FOREGROUND;
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+
+ if (!isSystemToast && ((notificationsDisabledForPackage && !appIsForeground)
+ || isPackageSuspended)) {
+ Slog.e(TAG, "Suppressing toast from package " + pkg
+ + (isPackageSuspended ? " due to package suspended."
+ : " by user request."));
+ return false;
+ }
+
+ if (blockToast(callingUid, isSystemToast, isAppRenderedToast,
+ isPackageInForegroundForToast(callingUid))) {
+ Slog.w(TAG, "Blocking custom toast from package " + pkg
+ + " due to package not in the foreground at time the toast was posted");
+ return false;
+ }
+
+ return true;
+ }
+
@Override
public void cancelToast(String pkg, IBinder token) {
Slog.i(TAG, "cancelToast pkg=" + pkg + " token=" + token);
@@ -5163,12 +5176,7 @@
@Override
public ComponentName getDefaultNotificationAssistant() {
checkCallerIsSystem();
- ArraySet<ComponentName> defaultComponents = mAssistants.getDefaultComponents();
- if (defaultComponents.size() > 1) {
- Slog.w(TAG, "More than one default NotificationAssistant: "
- + defaultComponents.size());
- }
- return CollectionUtils.firstOrNull(defaultComponents);
+ return mAssistants.getDefaultFromConfig();
}
@Override
@@ -7692,12 +7700,13 @@
boolean isWithinQuota =
mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG)
|| isExemptFromRateLimiting(record.pkg, userId);
+ boolean isPackageInForeground = isPackageInForegroundForToast(record.uid);
if (tryShowToast(
- record, rateLimitingEnabled, isWithinQuota)) {
+ record, rateLimitingEnabled, isWithinQuota, isPackageInForeground)) {
scheduleDurationReachedLocked(record, lastToastWasTextRecord);
mIsCurrentToastShown = true;
- if (rateLimitingEnabled) {
+ if (rateLimitingEnabled && !isPackageInForeground) {
mToastRateLimiter.noteEvent(userId, record.pkg, TOAST_QUOTA_TAG);
}
return;
@@ -7713,14 +7722,15 @@
/** Returns true if it successfully showed the toast. */
private boolean tryShowToast(ToastRecord record, boolean rateLimitingEnabled,
- boolean isWithinQuota) {
- if (rateLimitingEnabled && !isWithinQuota) {
+ boolean isWithinQuota, boolean isPackageInForeground) {
+ if (rateLimitingEnabled && !isWithinQuota && !isPackageInForeground) {
reportCompatRateLimitingToastsChange(record.uid);
Slog.w(TAG, "Package " + record.pkg + " is above allowed toast quota, the "
+ "following toast was blocked and discarded: " + record);
return false;
}
- if (blockToast(record.uid, record.isSystemToast, record.isAppRendered())) {
+ if (blockToast(record.uid, record.isSystemToast, record.isAppRendered(),
+ isPackageInForeground)) {
Slog.w(TAG, "Blocking custom toast from package " + record.pkg
+ " due to package not in the foreground at the time of showing the toast");
return false;
@@ -7911,10 +7921,11 @@
* with targetSdk < R. For apps with targetSdk R+, text toasts are not app-rendered, so
* isAppRenderedToast == true means it's a custom toast.
*/
- private boolean blockToast(int uid, boolean isSystemToast, boolean isAppRenderedToast) {
+ private boolean blockToast(int uid, boolean isSystemToast, boolean isAppRenderedToast,
+ boolean isPackageInForeground) {
return isAppRenderedToast
&& !isSystemToast
- && !isPackageInForegroundForToast(uid)
+ && !isPackageInForeground
&& CompatChanges.isChangeEnabled(CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK, uid);
}
@@ -9392,6 +9403,9 @@
}
ComponentName getDefaultFromConfig() {
+ if (mDefaultFromConfig == null) {
+ loadDefaultsFromConfig(false);
+ }
return mDefaultFromConfig;
}
diff --git a/services/core/java/com/android/server/om/OverlayReferenceMapper.java b/services/core/java/com/android/server/om/OverlayReferenceMapper.java
index a9dbb2f..94a1f3b 100644
--- a/services/core/java/com/android/server/om/OverlayReferenceMapper.java
+++ b/services/core/java/com/android/server/om/OverlayReferenceMapper.java
@@ -19,6 +19,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
@@ -28,10 +30,10 @@
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.pkg.AndroidPackage;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@@ -84,14 +86,15 @@
* See class comment for specific types.
*/
@GuardedBy("mLock")
- private final Map<String, Map<String, Set<String>>> mActorToTargetToOverlays = new HashMap<>();
+ private final ArrayMap<String, ArrayMap<String, ArraySet<String>>> mActorToTargetToOverlays =
+ new ArrayMap<>();
/**
* Keys are actor package names, values are generic package names the actor should be able
* to see.
*/
@GuardedBy("mLock")
- private final Map<String, Set<String>> mActorPkgToPkgs = new HashMap<>();
+ private final ArrayMap<String, Set<String>> mActorPkgToPkgs = new ArrayMap<>();
@GuardedBy("mLock")
private boolean mDeferRebuild;
@@ -160,21 +163,26 @@
*
* @param pkg the package to add
* @param otherPkgs map of other packages to consider, excluding {@param pkg}
+ * @return Set of packages that may have changed visibility
*/
- public void addPkg(AndroidPackage pkg, Map<String, AndroidPackage> otherPkgs) {
+ public ArraySet<String> addPkg(AndroidPackage pkg, Map<String, AndroidPackage> otherPkgs) {
synchronized (mLock) {
+ ArraySet<String> changed = new ArraySet<>();
+
if (!pkg.getOverlayables().isEmpty()) {
- addTarget(pkg, otherPkgs);
+ addTarget(pkg, otherPkgs, changed);
}
// TODO(b/135203078): Replace with isOverlay boolean flag check; fix test mocks
if (!mProvider.getTargetToOverlayables(pkg).isEmpty()) {
- addOverlay(pkg, otherPkgs);
+ addOverlay(pkg, otherPkgs, changed);
}
if (!mDeferRebuild) {
rebuild();
}
+
+ return changed;
}
}
@@ -184,27 +192,40 @@
* of {@link SystemConfig#getNamedActors()}.
*
* @param pkgName name to remove, as was added through {@link #addPkg(AndroidPackage, Map)}
+ * @return Set of packages that may have changed visibility
*/
- public void removePkg(String pkgName) {
+ public ArraySet<String> removePkg(String pkgName) {
synchronized (mLock) {
- removeTarget(pkgName);
- removeOverlay(pkgName);
+ ArraySet<String> changedPackages = new ArraySet<>();
+ removeTarget(pkgName, changedPackages);
+ removeOverlay(pkgName, changedPackages);
if (!mDeferRebuild) {
rebuild();
}
+
+ return changedPackages;
}
}
- private void removeTarget(String target) {
+ /**
+ * @param changedPackages Ongoing collection of packages that may have changed visibility
+ */
+ private void removeTarget(String target, @NonNull Collection<String> changedPackages) {
synchronized (mLock) {
- Iterator<Map<String, Set<String>>> iterator =
- mActorToTargetToOverlays.values().iterator();
- while (iterator.hasNext()) {
- Map<String, Set<String>> next = iterator.next();
- next.remove(target);
- if (next.isEmpty()) {
- iterator.remove();
+ int size = mActorToTargetToOverlays.size();
+ for (int index = size - 1; index >= 0; index--) {
+ ArrayMap<String, ArraySet<String>> targetToOverlays =
+ mActorToTargetToOverlays.valueAt(index);
+ if (targetToOverlays.containsKey(target)) {
+ targetToOverlays.remove(target);
+
+ String actor = mActorToTargetToOverlays.keyAt(index);
+ changedPackages.add(mProvider.getActorPkg(actor));
+
+ if (targetToOverlays.isEmpty()) {
+ mActorToTargetToOverlays.removeAt(index);
+ }
}
}
}
@@ -215,16 +236,19 @@
*
* If a target overlays itself, it will not be associated with itself, as only one half of the
* relationship needs to exist for visibility purposes.
+ *
+ * @param changedPackages Ongoing collection of packages that may have changed visibility
*/
- private void addTarget(AndroidPackage targetPkg, Map<String, AndroidPackage> otherPkgs) {
+ private void addTarget(AndroidPackage targetPkg, Map<String, AndroidPackage> otherPkgs,
+ @NonNull Collection<String> changedPackages) {
synchronized (mLock) {
String target = targetPkg.getPackageName();
- removeTarget(target);
+ removeTarget(target, changedPackages);
Map<String, String> overlayablesToActors = targetPkg.getOverlayables();
for (String overlayable : overlayablesToActors.keySet()) {
String actor = overlayablesToActors.get(overlayable);
- addTargetToMap(actor, target);
+ addTargetToMap(actor, target, changedPackages);
for (AndroidPackage overlayPkg : otherPkgs.values()) {
Map<String, Set<String>> targetToOverlayables =
@@ -235,18 +259,38 @@
}
if (overlayables.contains(overlayable)) {
- addOverlayToMap(actor, target, overlayPkg.getPackageName());
+ String overlay = overlayPkg.getPackageName();
+ addOverlayToMap(actor, target, overlay, changedPackages);
}
}
}
}
}
- private void removeOverlay(String overlay) {
+ /**
+ * @param changedPackages Ongoing collection of packages that may have changed visibility
+ */
+ private void removeOverlay(String overlay, @NonNull Collection<String> changedPackages) {
synchronized (mLock) {
- for (Map<String, Set<String>> targetToOverlays : mActorToTargetToOverlays.values()) {
- for (Set<String> overlays : targetToOverlays.values()) {
- overlays.remove(overlay);
+ int actorsSize = mActorToTargetToOverlays.size();
+ for (int actorIndex = actorsSize - 1; actorIndex >= 0; actorIndex--) {
+ ArrayMap<String, ArraySet<String>> targetToOverlays =
+ mActorToTargetToOverlays.valueAt(actorIndex);
+ int targetsSize = targetToOverlays.size();
+ for (int targetIndex = targetsSize - 1; targetIndex >= 0; targetIndex--) {
+ final Set<String> overlays = targetToOverlays.valueAt(targetIndex);
+
+ if (overlays.remove(overlay)) {
+ String actor = mActorToTargetToOverlays.keyAt(actorIndex);
+ changedPackages.add(mProvider.getActorPkg(actor));
+
+ // targetToOverlays should not be removed here even if empty as the actor
+ // will still have visibility to the target even if no overlays exist
+ }
+ }
+
+ if (targetToOverlays.isEmpty()) {
+ mActorToTargetToOverlays.removeAt(actorIndex);
}
}
}
@@ -257,11 +301,14 @@
*
* If an overlay targets itself, it will not be associated with itself, as only one half of the
* relationship needs to exist for visibility purposes.
+ *
+ * @param changedPackages Ongoing collection of packages that may have changed visibility
*/
- private void addOverlay(AndroidPackage overlayPkg, Map<String, AndroidPackage> otherPkgs) {
+ private void addOverlay(AndroidPackage overlayPkg, Map<String, AndroidPackage> otherPkgs,
+ @NonNull Collection<String> changedPackages) {
synchronized (mLock) {
String overlay = overlayPkg.getPackageName();
- removeOverlay(overlay);
+ removeOverlay(overlay, changedPackages);
Map<String, Set<String>> targetToOverlayables =
mProvider.getTargetToOverlayables(overlayPkg);
@@ -280,7 +327,7 @@
if (TextUtils.isEmpty(actor)) {
continue;
}
- addOverlayToMap(actor, targetPkgName, overlay);
+ addOverlayToMap(actor, targetPkgName, overlay, changedPackages);
}
}
}
@@ -312,7 +359,8 @@
continue;
}
- Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
+ ArrayMap<String, ArraySet<String>> targetToOverlays =
+ mActorToTargetToOverlays.get(actor);
Set<String> pkgs = new HashSet<>();
for (String target : targetToOverlays.keySet()) {
@@ -326,36 +374,51 @@
}
}
- private void addTargetToMap(String actor, String target) {
- Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
+ /**
+ * @param changedPackages Ongoing collection of packages that may have changed visibility
+ */
+ private void addTargetToMap(String actor, String target,
+ @NonNull Collection<String> changedPackages) {
+ ArrayMap<String, ArraySet<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
if (targetToOverlays == null) {
- targetToOverlays = new HashMap<>();
+ targetToOverlays = new ArrayMap<>();
mActorToTargetToOverlays.put(actor, targetToOverlays);
}
- Set<String> overlays = targetToOverlays.get(target);
+ ArraySet<String> overlays = targetToOverlays.get(target);
if (overlays == null) {
- overlays = new HashSet<>();
+ overlays = new ArraySet<>();
targetToOverlays.put(target, overlays);
}
+
+ // For now, only actors themselves can gain or lose visibility through package changes
+ changedPackages.add(mProvider.getActorPkg(actor));
}
- private void addOverlayToMap(String actor, String target, String overlay) {
+ /**
+ * @param changedPackages Ongoing collection of packages that may have changed visibility
+ */
+ private void addOverlayToMap(String actor, String target, String overlay,
+ @NonNull Collection<String> changedPackages) {
synchronized (mLock) {
- Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
+ ArrayMap<String, ArraySet<String>> targetToOverlays =
+ mActorToTargetToOverlays.get(actor);
if (targetToOverlays == null) {
- targetToOverlays = new HashMap<>();
+ targetToOverlays = new ArrayMap<>();
mActorToTargetToOverlays.put(actor, targetToOverlays);
}
- Set<String> overlays = targetToOverlays.get(target);
+ ArraySet<String> overlays = targetToOverlays.get(target);
if (overlays == null) {
- overlays = new HashSet<>();
+ overlays = new ArraySet<>();
targetToOverlays.put(target, overlays);
}
overlays.add(overlay);
}
+
+ // For now, only actors themselves can gain or lose visibility through package changes
+ changedPackages.add(mProvider.getActorPkg(actor));
}
public interface Provider {
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index ca8202f..4f527f2 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -662,11 +662,27 @@
removePackage(newPkgSetting);
}
mStateProvider.runWithState((settings, users) -> {
- addPackageInternal(newPkgSetting, settings);
+ ArraySet<String> additionalChangedPackages =
+ addPackageInternal(newPkgSetting, settings);
synchronized (mCacheLock) {
if (mShouldFilterCache != null) {
updateShouldFilterCacheForPackage(mShouldFilterCache, null, newPkgSetting,
settings, users, settings.size());
+ if (additionalChangedPackages != null) {
+ for (int index = 0; index < additionalChangedPackages.size(); index++) {
+ String changedPackage = additionalChangedPackages.valueAt(index);
+ PackageSetting changedPkgSetting = settings.get(changedPackage);
+ if (changedPkgSetting == null) {
+ // It's possible for the overlay mapper to know that an actor
+ // package changed via an explicit reference, even if the actor
+ // isn't installed, so skip if that's the case.
+ continue;
+ }
+
+ updateShouldFilterCacheForPackage(mShouldFilterCache, null,
+ changedPkgSetting, settings, users, settings.size());
+ }
+ }
} // else, rebuild entire cache when system is ready
}
});
@@ -676,7 +692,12 @@
}
}
- private void addPackageInternal(PackageSetting newPkgSetting,
+ /**
+ * @return Additional packages that may have had their viewing visibility changed and may need
+ * to be updated in the cache. Returns null if there are no additional packages.
+ */
+ @Nullable
+ private ArraySet<String> addPackageInternal(PackageSetting newPkgSetting,
ArrayMap<String, PackageSetting> existingSettings) {
if (Objects.equals("android", newPkgSetting.name)) {
// let's set aside the framework signatures
@@ -692,8 +713,7 @@
final AndroidPackage newPkg = newPkgSetting.pkg;
if (newPkg == null) {
- // nothing to add
- return;
+ return null;
}
if (mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts())) {
@@ -765,8 +785,13 @@
existingPkgs.put(pkgSetting.name, pkgSetting.pkg);
}
}
- mOverlayReferenceMapper.addPkg(newPkgSetting.pkg, existingPkgs);
+
+ ArraySet<String> changedPackages =
+ mOverlayReferenceMapper.addPkg(newPkgSetting.pkg, existingPkgs);
+
mFeatureConfig.updatePackageState(newPkgSetting, false /*removed*/);
+
+ return changedPackages;
}
@GuardedBy("mCacheLock")
@@ -1080,7 +1105,9 @@
}
}
- mOverlayReferenceMapper.removePkg(setting.name);
+ ArraySet<String> additionalChangedPackages =
+ mOverlayReferenceMapper.removePkg(setting.name);
+
mFeatureConfig.updatePackageState(setting, true /*removed*/);
// After removing all traces of the package, if it's part of a shared user, re-add other
@@ -1109,6 +1136,25 @@
siblingSetting, settings, users, settings.size());
}
}
+
+ if (mShouldFilterCache != null) {
+ if (additionalChangedPackages != null) {
+ for (int index = 0; index < additionalChangedPackages.size(); index++) {
+ String changedPackage = additionalChangedPackages.valueAt(index);
+ PackageSetting changedPkgSetting = settings.get(changedPackage);
+ if (changedPkgSetting == null) {
+ // It's possible for the overlay mapper to know that an actor
+ // package changed via an explicit reference, even if the actor
+ // isn't installed, so skip if that's the case.
+ continue;
+ }
+
+ updateShouldFilterCacheForPackage(mShouldFilterCache, null,
+ changedPkgSetting, settings, users, settings.size());
+ }
+ }
+ }
+
onChanged();
}
});
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index 6875b8a..ec79483 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -45,6 +45,7 @@
public static final int DUMP_QUERIES = 1 << 26;
public static final int DUMP_KNOWN_PACKAGES = 1 << 27;
public static final int DUMP_PER_UID_READ_TIMEOUTS = 1 << 28;
+ public static final int DUMP_SNAPSHOT_STATISTICS = 1 << 29;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
public static final int OPTION_DUMP_ALL_COMPONENTS = 1 << 1;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 91a66ac..edd43af 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -636,9 +636,25 @@
Objects.requireNonNull(component);
// All right, create the sender.
- Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(component);
+ final int callingUid = injectBinderCallingUid();
final long identity = Binder.clearCallingIdentity();
try {
+ final PackageManagerInternal pmInt =
+ LocalServices.getService(PackageManagerInternal.class);
+ Intent packageIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT)
+ .setPackage(component.getPackageName());
+ List<ResolveInfo> apps = pmInt.queryIntentActivities(packageIntent,
+ packageIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ callingUid, user.getIdentifier());
+ // ensure that the component is present in the list
+ if (!apps.stream().anyMatch(
+ ri -> component.getClassName().equals(ri.activityInfo.name))) {
+ return null;
+ }
+
+ Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(component);
final PendingIntent pi = PendingIntent.getActivityAsUser(
mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
| PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT,
@@ -1027,22 +1043,24 @@
return false;
}
+ final PackageManagerInternal pmInt =
+ LocalServices.getService(PackageManagerInternal.class);
final int callingUid = injectBinderCallingUid();
+ final int state = pmInt.getComponentEnabledSetting(component, callingUid,
+ user.getIdentifier());
+ switch (state) {
+ case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+ break; // Need to check the manifest's enabled state.
+ case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+ return true;
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+ return false;
+ }
+
final long ident = Binder.clearCallingIdentity();
try {
- final int state = mIPM.getComponentEnabledSetting(component, user.getIdentifier());
- switch (state) {
- case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
- break; // Need to check the manifest's enabled state.
- case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
- return true;
- case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
- case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
- case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
- return false;
- }
- final PackageManagerInternal pmInt =
- LocalServices.getService(PackageManagerInternal.class);
ActivityInfo info = pmInt.getActivityInfo(component,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0a484e2..27077b6 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -527,6 +527,14 @@
throw new SecurityException("User restriction prevents installing");
}
+ if (params.dataLoaderParams != null
+ && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("You need the "
+ + "com.android.permission.USE_INSTALLER_V2 permission "
+ + "to use a data loader");
+ }
+
// INSTALL_REASON_ROLLBACK allows an app to be rolled back without requiring the ROLLBACK
// capability; ensure if this is set as the install reason the app has one of the necessary
// signature permissions to perform the rollback.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index e532790..b6a65dd 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3636,12 +3636,14 @@
@Override
public DataLoaderParamsParcel getDataLoaderParams() {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null;
}
@Override
public void addFile(int location, String name, long lengthBytes, byte[] metadata,
byte[] signature) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
if (!isDataLoaderInstallation()) {
throw new IllegalStateException(
"Cannot add files to non-data loader installation session.");
@@ -3674,6 +3676,7 @@
@Override
public void removeFile(int location, String name) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
if (!isDataLoaderInstallation()) {
throw new IllegalStateException(
"Cannot add files to non-data loader installation session.");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2849982..3c4a304 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1495,6 +1495,9 @@
// List of packages names to keep cached, even if they are uninstalled for all users
private List<String> mKeepUninstalledPackages;
+ // Cached reference to IDevicePolicyManager.
+ private IDevicePolicyManager mDevicePolicyManager = null;
+
private File mCacheDir;
private Future<?> mPrepareAppDataFuture;
@@ -1914,6 +1917,18 @@
*/
private interface Computer {
+ /**
+ * Administrative statistics: record that the snapshot has been used. Every call
+ * to use() increments the usage counter.
+ */
+ void use();
+
+ /**
+ * Fetch the snapshot usage counter.
+ * @return The number of times this snapshot was used.
+ */
+ int getUsed();
+
@NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
int flags, @PrivateResolveFlags int privateResolveFlags, int filterCallingUid,
int userId, boolean resolveForStart, boolean allowDynamicSplits);
@@ -2065,6 +2080,9 @@
*/
private static class ComputerEngine implements Computer {
+ // The administrative use counter.
+ private int mUsed = 0;
+
// Cached attributes. The names in this class are the same as the
// names in PackageManagerService; see that class for documentation.
protected final Settings mSettings;
@@ -2157,6 +2175,20 @@
mService = args.service;
}
+ /**
+ * Record that the snapshot was used.
+ */
+ public void use() {
+ mUsed++;
+ }
+
+ /**
+ * Return the usage counter.
+ */
+ public int getUsed() {
+ return mUsed;
+ }
+
public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart,
@@ -4885,121 +4917,11 @@
*/
private final Object mSnapshotLock = new Object();
- // A counter of all queries that hit the current snapshot.
- @GuardedBy("mSnapshotLock")
- private int mSnapshotHits = 0;
-
- // A class to record snapshot statistics.
- private static class SnapshotStatistics {
- // A build time is "big" if it takes longer than 5ms.
- private static final long SNAPSHOT_BIG_BUILD_TIME_NS = TimeUnit.MILLISECONDS.toNanos(5);
-
- // A snapshot is in quick succession to the previous snapshot if it less than
- // 100ms since the previous snapshot.
- private static final long SNAPSHOT_QUICK_REBUILD_INTERVAL_NS =
- TimeUnit.MILLISECONDS.toNanos(100);
-
- // The interval between snapshot statistics logging, in ns.
- private static final long SNAPSHOT_LOG_INTERVAL_NS = TimeUnit.MINUTES.toNanos(10);
-
- // The throttle parameters for big build reporting. Do not report more than this
- // many events in a single log interval.
- private static final int SNAPSHOT_BUILD_REPORT_LIMIT = 10;
-
- // The time the snapshot statistics were last logged.
- private long mStatisticsSent = 0;
-
- // The number of build events logged since the last periodic log.
- private int mLoggedBuilds = 0;
-
- // The time of the last build.
- private long mLastBuildTime = 0;
-
- // The number of times the snapshot has been rebuilt since the statistics were
- // last logged.
- private int mRebuilds = 0;
-
- // The number of times the snapshot has been used since it was rebuilt.
- private int mReused = 0;
-
- // The number of "big" build times since the last log. "Big" is defined by
- // SNAPSHOT_BIG_BUILD_TIME.
- private int mBigBuilds = 0;
-
- // The number of quick rebuilds. "Quick" is defined by
- // SNAPSHOT_QUICK_REBUILD_INTERVAL_NS.
- private int mQuickRebuilds = 0;
-
- // The time take to build a snapshot. This is cumulative over the rebuilds recorded
- // in mRebuilds, so the average time to build a snapshot is given by
- // mBuildTimeNs/mRebuilds.
- private int mBuildTimeNs = 0;
-
- // The maximum build time since the last log.
- private long mMaxBuildTimeNs = 0;
-
- // The constant that converts ns to ms. This is the divisor.
- private final long NS_TO_MS = TimeUnit.MILLISECONDS.toNanos(1);
-
- // Convert ns to an int ms. The maximum range of this method is about 24 days.
- // There is no expectation that an event will take longer than that.
- private int nsToMs(long ns) {
- return (int) (ns / NS_TO_MS);
- }
-
- // The single method records a rebuild. The "now" parameter is passed in because
- // the caller needed it to computer the duration, so pass it in to avoid
- // recomputing it.
- private void rebuild(long now, long done, int hits) {
- if (mStatisticsSent == 0) {
- mStatisticsSent = now;
- }
- final long elapsed = now - mLastBuildTime;
- final long duration = done - now;
- mLastBuildTime = now;
-
- if (mMaxBuildTimeNs < duration) {
- mMaxBuildTimeNs = duration;
- }
- mRebuilds++;
- mReused += hits;
- mBuildTimeNs += duration;
-
- boolean log_build = false;
- if (duration > SNAPSHOT_BIG_BUILD_TIME_NS) {
- log_build = true;
- mBigBuilds++;
- }
- if (elapsed < SNAPSHOT_QUICK_REBUILD_INTERVAL_NS) {
- log_build = true;
- mQuickRebuilds++;
- }
- if (log_build && mLoggedBuilds < SNAPSHOT_BUILD_REPORT_LIMIT) {
- EventLogTags.writePmSnapshotRebuild(nsToMs(duration), nsToMs(elapsed));
- mLoggedBuilds++;
- }
-
- final long log_interval = now - mStatisticsSent;
- if (log_interval >= SNAPSHOT_LOG_INTERVAL_NS) {
- EventLogTags.writePmSnapshotStats(mRebuilds, mReused,
- mBigBuilds, mQuickRebuilds,
- nsToMs(mMaxBuildTimeNs),
- nsToMs(mBuildTimeNs));
- mStatisticsSent = now;
- mRebuilds = 0;
- mReused = 0;
- mBuildTimeNs = 0;
- mMaxBuildTimeNs = 0;
- mBigBuilds = 0;
- mQuickRebuilds = 0;
- mLoggedBuilds = 0;
- }
- }
- }
-
- // Snapshot statistics.
- @GuardedBy("mLock")
- private final SnapshotStatistics mSnapshotStatistics = new SnapshotStatistics();
+ /**
+ * The snapshot statistics. These are collected to track performance and to identify
+ * situations in which the snapshots are misbehaving.
+ */
+ private final SnapshotStatistics mSnapshotStatistics;
// 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.
@@ -5033,10 +4955,9 @@
Computer c = mSnapshotComputer;
if (sSnapshotCorked && (c != null)) {
// Snapshots are corked, which means new ones should not be built right now.
+ c.use();
return c;
}
- // Deliberately capture the value pre-increment
- final int hits = mSnapshotHits++;
if (sSnapshotInvalid || (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().
@@ -5046,7 +4967,7 @@
// self-consistent (the lock is being held) and is current as of the time
// this function is entered.
if (sSnapshotInvalid) {
- rebuildSnapshot(hits);
+ rebuildSnapshot();
}
// Guaranteed to be non-null. mSnapshotComputer is only be set to null
@@ -5056,6 +4977,7 @@
c = mSnapshotComputer;
}
}
+ c.use();
return c;
}
}
@@ -5065,16 +4987,16 @@
* threads from using the invalid computer until it is rebuilt.
*/
@GuardedBy("mLock")
- private void rebuildSnapshot(int hits) {
- final long now = System.nanoTime();
+ 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 = System.nanoTime();
+ final long done = SystemClock.currentTimeMicro();
mSnapshotStatistics.rebuild(now, done, hits);
- mSnapshotHits = 0;
}
/**
@@ -6327,6 +6249,7 @@
mSnapshotEnabled = false;
mLiveComputer = createLiveComputer();
mSnapshotComputer = null;
+ mSnapshotStatistics = null;
mPackages.putAll(testParams.packages);
mEnableFreeCacheV2 = testParams.enableFreeCacheV2;
@@ -6479,17 +6402,20 @@
mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
mDomainVerificationManager.setConnection(mDomainVerificationConnection);
- // Create the computer as soon as the state objects have been installed. The
- // cached computer is the same as the live computer until the end of the
- // 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;
- mLiveComputer = createLiveComputer();
- mSnapshotComputer = null;
- registerObserver();
+ synchronized (mLock) {
+ // Create the computer as soon as the state objects have been installed. The
+ // cached computer is the same as the live computer until the end of the
+ // 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();
+ mLiveComputer = createLiveComputer();
+ mSnapshotComputer = null;
+ registerObserver();
+ }
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
@@ -15315,9 +15241,8 @@
final BroadcastOptions bOptions = getTemporaryAppAllowlistBroadcastOptions(
REASON_LOCKED_BOOT_COMPLETED);
am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null,
- requiredPermissions, android.app.AppOpsManager.OP_NONE, bOptions.toBundle(),
- false, false,
- userId);
+ requiredPermissions, null, android.app.AppOpsManager.OP_NONE,
+ bOptions.toBundle(), false, false, userId);
// Deliver BOOT_COMPLETED only if user is unlocked
final UserManagerInternal umInternal = mInjector.getUserManagerInternal();
@@ -15327,9 +15252,8 @@
bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
}
am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null,
- requiredPermissions, android.app.AppOpsManager.OP_NONE, bOptions.toBundle(),
- false, false,
- userId);
+ requiredPermissions, null, android.app.AppOpsManager.OP_NONE,
+ bOptions.toBundle(), false, false, userId);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -20886,8 +20810,7 @@
}
private boolean isPackageDeviceAdmin(String packageName, int userId) {
- IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+ final IDevicePolicyManager dpm = getDevicePolicyManager();
try {
if (dpm != null) {
final ComponentName deviceOwnerComponentName = dpm.getDeviceOwnerComponent(
@@ -20919,6 +20842,16 @@
return false;
}
+ /** Returns the device policy manager interface. */
+ private IDevicePolicyManager getDevicePolicyManager() {
+ if (mDevicePolicyManager == null) {
+ // No need to synchronize; worst-case scenario it will be fetched twice.
+ mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+ }
+ return mDevicePolicyManager;
+ }
+
private boolean shouldKeepUninstalledPackageLPr(String packageName) {
return mKeepUninstalledPackages != null && mKeepUninstalledPackages.contains(packageName);
}
@@ -22197,7 +22130,7 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
try {
am.broadcastIntentWithFeature(null, null, intent, null, null,
- 0, null, null, null, android.app.AppOpsManager.OP_NONE,
+ 0, null, null, null, null, android.app.AppOpsManager.OP_NONE,
null, false, false, userId);
} catch (RemoteException e) {
}
@@ -23695,11 +23628,17 @@
@Override
public int getComponentEnabledSetting(@NonNull ComponentName component, int userId) {
- if (component == null) return COMPONENT_ENABLED_STATE_DEFAULT;
- if (!mUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
false /*checkShell*/, "getComponentEnabled");
+ return getComponentEnabledSettingInternal(component, callingUid, userId);
+ }
+
+ private int getComponentEnabledSettingInternal(ComponentName component, int callingUid,
+ int userId) {
+ if (component == null) return COMPONENT_ENABLED_STATE_DEFAULT;
+ if (!mUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
+
synchronized (mLock) {
try {
if (shouldFilterApplicationLocked(
@@ -23959,6 +23898,7 @@
pw.println(" dexopt: dump dexopt state");
pw.println(" compiler-stats: dump compiler statistics");
pw.println(" service-permissions: dump permissions required by services");
+ pw.println(" snapshot: dump snapshot statistics");
pw.println(" known-packages: dump known packages");
pw.println(" <package.name>: info about given package");
return;
@@ -24107,6 +24047,8 @@
dumpState.setDump(DumpState.DUMP_KNOWN_PACKAGES);
} else if ("t".equals(cmd) || "timeouts".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_PER_UID_READ_TIMEOUTS);
+ } else if ("snapshot".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_SNAPSHOT_STATISTICS);
} else if ("write".equals(cmd)) {
synchronized (mLock) {
writeSettingsLPrTEMP();
@@ -24435,6 +24377,22 @@
pw.println(")");
}
}
+
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)) {
+ pw.println("Snapshot statistics");
+ if (!mSnapshotEnabled) {
+ pw.println(" Snapshots disabled");
+ } else {
+ int hits = 0;
+ synchronized (mSnapshotLock) {
+ if (mSnapshotComputer != null) {
+ hits = mSnapshotComputer.getUsed();
+ }
+ }
+ final long now = SystemClock.currentTimeMicro();
+ mSnapshotStatistics.dump(pw, " ", now, hits, true);
+ }
+ }
}
/**
@@ -27126,6 +27084,13 @@
}
@Override
+ public @PackageManager.EnabledState int getComponentEnabledSetting(
+ @NonNull ComponentName componentName, int callingUid, int userId) {
+ return PackageManagerService.this.getComponentEnabledSettingInternal(componentName,
+ callingUid, userId);
+ }
+
+ @Override
public void setEnableRollbackCode(int token, int enableRollbackCode) {
PackageManagerService.this.setEnableRollbackCode(token, enableRollbackCode);
}
@@ -27810,8 +27775,8 @@
};
try {
am.broadcastIntentWithFeature(null, null, intent, null, null, 0, null, null,
- requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
- UserHandle.USER_ALL);
+ requiredPermissions, null, android.app.AppOpsManager.OP_NONE, null, false,
+ false, UserHandle.USER_ALL);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index c462a6c..464477d 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -92,6 +92,7 @@
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* Package information used by {@link ShortcutService}.
@@ -663,12 +664,14 @@
});
// Then, update the pinned state if necessary.
final List<ShortcutInfo> pinned = getShortcutById(pinnedShortcuts);
- pinned.forEach(si -> {
- if (!si.isPinned()) {
- si.addFlags(ShortcutInfo.FLAG_PINNED);
- }
- });
- saveShortcut(pinned);
+ if (pinned != null) {
+ pinned.forEach(si -> {
+ if (!si.isPinned()) {
+ si.addFlags(ShortcutInfo.FLAG_PINNED);
+ }
+ });
+ saveShortcut(pinned);
+ }
forEachShortcutMutateIf(AppSearchShortcutInfo.QUERY_IS_PINNED, si -> {
if (!pinnedShortcuts.contains(si.getId()) && si.isPinned()) {
si.clearFlags(ShortcutInfo.FLAG_PINNED);
@@ -719,7 +722,7 @@
// If not reset yet, then reset.
if (mLastResetTime < last) {
- if (ShortcutService.DEBUG) {
+ if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) {
Slog.d(TAG, String.format("%s: last reset=%d, now=%d, last=%d: resetting",
getPackageName(), mLastResetTime, now, last));
}
@@ -826,9 +829,11 @@
: s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
.getPinnedShortcutIds(getPackageName(), getPackageUserId());
final List<ShortcutInfo> shortcuts = getShortcutById(ids);
- for (ShortcutInfo si : shortcuts) {
- filter(result, query, cloneFlag, callingLauncher, pinnedByCallerSet,
- getPinnedByAnyLauncher, si);
+ if (shortcuts != null) {
+ for (ShortcutInfo si : shortcuts) {
+ filter(result, query, cloneFlag, callingLauncher, pinnedByCallerSet,
+ getPinnedByAnyLauncher, si);
+ }
}
}
@@ -1097,7 +1102,7 @@
}
final int manifestShortcutSize = newManifestShortcutList == null ? 0
: newManifestShortcutList.size();
- if (ShortcutService.DEBUG) {
+ if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) {
Slog.d(TAG,
String.format("Package %s has %d manifest shortcut(s), and %d share target(s)",
getPackageName(), manifestShortcutSize, mShareTargets.size()));
@@ -1109,7 +1114,7 @@
// disabled.
return false;
}
- if (ShortcutService.DEBUG) {
+ if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) {
Slog.d(TAG, String.format("Package %s %s, version %d -> %d", getPackageName(),
(isNewApp ? "added" : "updated"),
getPackageInfo().getVersionCode(), pi.getLongVersionCode()));
@@ -1198,7 +1203,7 @@
}
private boolean publishManifestShortcuts(List<ShortcutInfo> newManifestShortcutList) {
- if (ShortcutService.DEBUG) {
+ if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) {
Slog.d(TAG, String.format(
"Package %s: publishing manifest shortcuts", getPackageName()));
}
@@ -1875,7 +1880,7 @@
final int depth = parser.getDepth();
final String tag = parser.getName();
- if (ShortcutService.DEBUG_LOAD) {
+ if (ShortcutService.DEBUG_LOAD || ShortcutService.DEBUG_REBOOT) {
Slog.d(TAG, String.format("depth=%d type=%d name=%s", depth, type, tag));
}
if ((depth == 1) && TAG_ROOT.equals(tag)) {
@@ -1903,7 +1908,8 @@
final ShortcutPackage ret = new ShortcutPackage(shortcutUser,
shortcutUser.getUserId(), packageName);
- ret.mIsInitilized = ShortcutService.parseIntAttribute(parser, ATTR_SCHEMA_VERSON, 0) > 0;
+ ret.mIsInitilized = ShortcutService.parseIntAttribute(parser, ATTR_SCHEMA_VERSON, 0)
+ == AppSearchShortcutInfo.SCHEMA_VERSION;
ret.mApiCallCount =
ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
ret.mLastResetTime =
@@ -2010,7 +2016,7 @@
}
final int depth = parser.getDepth();
final String tag = parser.getName();
- if (ShortcutService.DEBUG_LOAD) {
+ if (ShortcutService.DEBUG_LOAD || ShortcutService.DEBUG_REBOOT) {
Slog.d(TAG, String.format(" depth=%d type=%d name=%s",
depth, type, tag));
}
@@ -2094,7 +2100,7 @@
}
final int depth = parser.getDepth();
final String tag = parser.getName();
- if (ShortcutService.DEBUG_LOAD) {
+ if (ShortcutService.DEBUG_LOAD || ShortcutService.DEBUG_REBOOT) {
Slog.d(TAG, String.format(" depth=%d type=%d name=%s",
depth, type, tag));
}
@@ -2297,6 +2303,12 @@
// No need to invoke AppSearch when there's nothing to save.
return;
}
+ if (ShortcutService.DEBUG_REBOOT) {
+ Slog.d(TAG, "Saving shortcuts for user=" + mShortcutUser.getUserId()
+ + " pkg=" + getPackageName() + " ids=["
+ + shortcuts.stream().map(ShortcutInfo::getId)
+ .collect(Collectors.joining(",")) + "]");
+ }
awaitInAppSearch("Saving shortcuts", session -> {
final AndroidFuture<Boolean> future = new AndroidFuture<>();
session.put(new PutDocumentsRequest.Builder()
@@ -2369,6 +2381,10 @@
shortcutIds.add(id);
}
}
+ if (ShortcutService.DEBUG_REBOOT) {
+ Slog.d(TAG, "Getting shortcuts for user=" + mShortcutUser.getUserId()
+ + " pkg=" + getPackageName() + " ids: [" + String.join(",", ids) + "]");
+ }
return awaitInAppSearch("Getting shortcut by id", session -> {
final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
session.getByUri(
@@ -2413,6 +2429,10 @@
private void forEachShortcutMutateIf(@NonNull final String query,
@NonNull final Function<ShortcutInfo, Boolean> cb) {
+ if (ShortcutService.DEBUG_REBOOT) {
+ Slog.d(TAG, "Changing shortcuts for user=" + mShortcutUser.getUserId()
+ + " pkg=" + getPackageName());
+ }
final SearchResults res = awaitInAppSearch("Mutating shortcuts", session ->
AndroidFuture.completedFuture(session.search(query, getSearchSpec())));
if (res == null) return;
@@ -2434,6 +2454,10 @@
private void forEachShortcutStopWhen(
@NonNull final String query, @NonNull final Function<ShortcutInfo, Boolean> cb) {
+ if (ShortcutService.DEBUG_REBOOT) {
+ Slog.d(TAG, "Iterating shortcuts for user=" + mShortcutUser.getUserId()
+ + " pkg=" + getPackageName());
+ }
final SearchResults res = awaitInAppSearch("Iterating shortcuts", session ->
AndroidFuture.completedFuture(session.search(query, getSearchSpec())));
if (res == null) return;
@@ -2447,6 +2471,10 @@
}
private List<ShortcutInfo> getNextPage(@NonNull final SearchResults res) {
+ if (ShortcutService.DEBUG_REBOOT) {
+ Slog.d(TAG, "Get next page for search result for user=" + mShortcutUser.getUserId()
+ + " pkg=" + getPackageName());
+ }
final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
final List<ShortcutInfo> ret = new ArrayList<>();
final long callingIdentity = Binder.clearCallingIdentity();
@@ -2524,6 +2552,10 @@
@NonNull
private AndroidFuture<AppSearchSession> setupSchema(
@NonNull final AppSearchSession session) {
+ if (ShortcutService.DEBUG_REBOOT) {
+ Slog.d(TAG, "Setup Schema for user=" + mShortcutUser.getUserId()
+ + " pkg=" + getPackageName());
+ }
SetSchemaRequest.Builder schemaBuilder = new SetSchemaRequest.Builder()
.addSchemas(AppSearchPerson.SCHEMA, AppSearchShortcutInfo.SCHEMA)
.setForceOverride(true);
@@ -2564,6 +2596,15 @@
}
private void restoreParsedShortcuts(final boolean replace) {
+ if (ShortcutService.DEBUG_REBOOT) {
+ if (replace) {
+ Slog.d(TAG, "Replacing all shortcuts with the ones parsed from xml for user="
+ + mShortcutUser.getUserId() + " pkg=" + getPackageName());
+ } else {
+ Slog.d(TAG, "Restoring pinned shortcuts from xml for user="
+ + mShortcutUser.getUserId() + " pkg=" + getPackageName());
+ }
+ }
if (replace) {
removeShortcuts();
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index dcf730d..8d03fce 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -164,6 +164,7 @@
static final boolean DEBUG = false; // STOPSHIP if true
static final boolean DEBUG_LOAD = false; // STOPSHIP if true
static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
+ static final boolean DEBUG_REBOOT = true;
@VisibleForTesting
static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
@@ -659,7 +660,7 @@
/** lifecycle event */
void onBootPhase(int phase) {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "onBootPhase: " + phase);
}
switch (phase) {
@@ -674,7 +675,7 @@
/** lifecycle event */
void handleUnlockUser(int userId) {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "handleUnlockUser: user=" + userId);
}
synchronized (mUnlockedUsers) {
@@ -699,7 +700,7 @@
/** lifecycle event */
void handleStopUser(int userId) {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "handleStopUser: user=" + userId);
}
synchronized (mLock) {
@@ -713,7 +714,7 @@
@GuardedBy("mLock")
private void unloadUserLocked(int userId) {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "unloadUserLocked: user=" + userId);
}
// Save all dirty information.
@@ -945,7 +946,7 @@
@VisibleForTesting
void saveBaseStateLocked() {
final AtomicFile file = getBaseStateFile();
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Saving to " + file.getBaseFile());
}
@@ -978,7 +979,7 @@
mRawLastResetTime = 0;
final AtomicFile file = getBaseStateFile();
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Loading from " + file.getBaseFile());
}
try (FileInputStream in = file.openRead()) {
@@ -1028,7 +1029,7 @@
@GuardedBy("mLock")
private void saveUserLocked(@UserIdInt int userId) {
final File path = getUserFile(userId);
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Saving to " + path);
}
@@ -1086,7 +1087,7 @@
@Nullable
private ShortcutUser loadUserLocked(@UserIdInt int userId) {
final File path = getUserFile(userId);
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Loading from " + path);
}
final AtomicFile file = new AtomicFile(path);
@@ -1095,7 +1096,7 @@
try {
in = file.openRead();
} catch (FileNotFoundException e) {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Not found " + path);
}
return null;
@@ -1132,7 +1133,7 @@
final int depth = parser.getDepth();
final String tag = parser.getName();
- if (DEBUG_LOAD) {
+ if (DEBUG_LOAD || DEBUG_REBOOT) {
Slog.d(TAG, String.format("depth=%d type=%d name=%s",
depth, type, tag));
}
@@ -1157,7 +1158,7 @@
private final Runnable mSaveDirtyInfoRunner = this::saveDirtyInfo;
private void scheduleSaveInner(@UserIdInt int userId) {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Scheduling to save for " + userId);
}
synchronized (mLock) {
@@ -1172,7 +1173,7 @@
@VisibleForTesting
void saveDirtyInfo() {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "saveDirtyInfo");
}
if (mShutdown.get()) {
@@ -2942,6 +2943,10 @@
@Nullable String packageName, @Nullable List<String> shortcutIds,
@Nullable List<LocusId> locusIds, @Nullable ComponentName componentName,
int queryFlags, int userId, int callingPid, int callingUid) {
+ if (DEBUG_REBOOT) {
+ Slog.d(TAG, "Getting shortcuts for launcher= " + callingPackage
+ + "user=" + userId + " pkg=" + packageName);
+ }
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
int flags = ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER;
@@ -3639,7 +3644,7 @@
*/
@VisibleForTesting
void checkPackageChanges(@UserIdInt int ownerUserId) {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "checkPackageChanges() ownerUserId=" + ownerUserId);
}
if (injectIsSafeModeEnabled()) {
@@ -3685,6 +3690,9 @@
@GuardedBy("mLock")
private void rescanUpdatedPackagesLocked(@UserIdInt int userId, long lastScanTime) {
+ if (DEBUG_REBOOT) {
+ Slog.d(TAG, "rescan updated package user=" + userId + " last scanned=" + lastScanTime);
+ }
final ShortcutUser user = getUserShortcutsLocked(userId);
// Note after each OTA, we'll need to rescan all system apps, as their lastUpdateTime
@@ -3708,7 +3716,7 @@
}
private void handlePackageAdded(String packageName, @UserIdInt int userId) {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
}
synchronized (mLock) {
@@ -3720,7 +3728,7 @@
}
private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d",
packageName, userId));
}
@@ -3736,7 +3744,7 @@
}
private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
packageUserId));
}
@@ -3746,7 +3754,7 @@
}
private void handlePackageDataCleared(String packageName, int packageUserId) {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName,
packageUserId));
}
@@ -3761,7 +3769,7 @@
handlePackageRemoved(packageName, packageUserId);
return;
}
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, String.format("handlePackageChanged: %s user=%d", packageName,
packageUserId));
}
@@ -3948,7 +3956,7 @@
private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, boolean afterOta,
Consumer<ApplicationInfo> callback) {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime
+ " afterOta=" + afterOta);
}
@@ -3960,7 +3968,7 @@
// Also if it's right after an OTA, always re-scan all apps anyway, since the
// shortcut parser might have changed.
if (afterOta || (pi.lastUpdateTime >= lastScanTime)) {
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Found updated package " + pi.packageName
+ " updateTime=" + pi.lastUpdateTime);
}
@@ -4313,7 +4321,7 @@
@Override
public void applyRestore(byte[] payload, @UserIdInt int userId) {
enforceSystem();
- if (DEBUG) {
+ if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "Restoring user " + userId);
}
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 069944d..e66cb03 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -328,6 +328,10 @@
public void rescanPackageIfNeeded(@NonNull String packageName, boolean forceRescan) {
final boolean isNewApp = !mPackages.containsKey(packageName);
+ if (ShortcutService.DEBUG_REBOOT) {
+ Slog.d(TAG, "rescanPackageIfNeeded " + getUserId() + "@" + packageName
+ + ", forceRescan=" + forceRescan + " , isNewApp=" + isNewApp);
+ }
final ShortcutPackage shortcutPackage = getPackageShortcuts(packageName);
@@ -397,7 +401,7 @@
} else {
// Save each ShortcutPackageItem in a separate Xml file.
final File path = getShortcutPackageItemFile(spi);
- if (ShortcutService.DEBUG) {
+ if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) {
Slog.d(TAG, "Saving package item " + spi.getPackageName() + " to " + path);
}
diff --git a/services/core/java/com/android/server/pm/SnapshotStatistics.java b/services/core/java/com/android/server/pm/SnapshotStatistics.java
new file mode 100644
index 0000000..c425bad5
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SnapshotStatistics.java
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.text.TextUtils;
+
+import com.android.server.EventLogTags;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * This class records statistics about PackageManagerService snapshots. It maintains two sets of
+ * statistics: a periodic set which represents the last 10 minutes, and a cumulative set since
+ * process boot. The key metrics that are recorded are:
+ * <ul>
+ * <li> The time to create a snapshot - this is the performance cost of a snapshot
+ * <li> The lifetime of the snapshot - creation time over lifetime is the amortized cost
+ * <li> The number of times a snapshot is reused - this is the number of times lock
+ * contention was avoided.
+ * </ul>
+
+ * The time conversions in this class are designed to keep arithmetic using ints, rather
+ * than longs. Raw times are supplied as longs in units of us. These are left long.
+ * Rebuild durations however, are converted to ints. An int can express a duration of
+ * approximately 35 minutes. This is longer than any expected snapshot rebuild time, so
+ * an int is satisfactory. The exception is the cumulative rebuild time over the course
+ * of a monitoring cycle: this value is kept long since the cycle time is one week and in
+ * a badly behaved system, the rebuild time might exceed 35 minutes.
+
+ * @hide
+ */
+public class SnapshotStatistics {
+ /**
+ * The interval at which statistics should be ticked. It is 60s. The interval is in
+ * units of milliseconds because that is what's required by Handler.sendMessageDelayed().
+ */
+ public static final int SNAPSHOT_TICK_INTERVAL_MS = 60 * 1000;
+
+ /**
+ * The number of ticks for long statistics. This is one week.
+ */
+ public static final int SNAPSHOT_LONG_TICKS = 7 * 24 * 60;
+
+ /**
+ * The number snapshot event logs that can be generated in a single logging interval.
+ * A small number limits the logging generated by this class. A snapshot event log is
+ * generated for every big snapshot build time, up to the limit, or whenever the
+ * maximum build time is exceeded in the logging interval.
+ */
+ public static final int SNAPSHOT_BUILD_REPORT_LIMIT = 10;
+
+ /**
+ * The number of microseconds in a millisecond.
+ */
+ private static final int US_IN_MS = 1000;
+
+ /**
+ * A snapshot build time is "big" if it takes longer than 10ms.
+ */
+ public static final int SNAPSHOT_BIG_BUILD_TIME_US = 10 * US_IN_MS;
+
+ /**
+ * A snapshot build time is reportable if it takes longer than 30ms. Testing shows
+ * that this is very rare.
+ */
+ public static final int SNAPSHOT_REPORTABLE_BUILD_TIME_US = 30 * US_IN_MS;
+
+ /**
+ * A snapshot is short-lived it used fewer than 5 times.
+ */
+ public static final int SNAPSHOT_SHORT_LIFETIME = 5;
+
+ /**
+ * The lock to control access to this object.
+ */
+ private final Object mLock = new Object();
+
+ /**
+ * The bins for the build time histogram. Values are in us.
+ */
+ private final BinMap mTimeBins;
+
+ /**
+ * The bins for the snapshot use histogram.
+ */
+ private final BinMap mUseBins;
+
+ /**
+ * The number of events reported in the current tick.
+ */
+ private int mEventsReported = 0;
+
+ /**
+ * The tick counter. At the default tick interval, this wraps every 4000 years or so.
+ */
+ private int mTicks = 0;
+
+ /**
+ * The handler used for the periodic ticks.
+ */
+ private Handler mHandler = null;
+
+ /**
+ * Convert ns to an int ms. The maximum range of this method is about 24 days. There
+ * is no expectation that an event will take longer than that.
+ */
+ private int usToMs(int us) {
+ return us / US_IN_MS;
+ }
+
+ /**
+ * This class exists to provide a fast bin lookup for histograms. An instance has an
+ * integer array that maps incoming values to bins. Values larger than the array are
+ * mapped to the top-most bin.
+ */
+ private static class BinMap {
+
+ // The number of bins
+ private int mCount;
+ // The mapping of low integers to bins
+ private int[] mBinMap;
+ // The maximum mapped value. Values at or above this are mapped to the
+ // top bin.
+ private int mMaxBin;
+ // A copy of the original key
+ private int[] mUserKey;
+
+ /**
+ * Create a bin map. The input is an array of integers, which must be
+ * monotonically increasing (this is not checked). The result is an integer array
+ * as long as the largest value in the input.
+ */
+ BinMap(int[] userKey) {
+ mUserKey = Arrays.copyOf(userKey, userKey.length);
+ // The number of bins is the length of the keys, plus 1 (for the max).
+ mCount = mUserKey.length + 1;
+ // The maximum value is one more than the last one in the map.
+ mMaxBin = mUserKey[mUserKey.length - 1] + 1;
+ mBinMap = new int[mMaxBin + 1];
+
+ int j = 0;
+ for (int i = 0; i < mUserKey.length; i++) {
+ while (j <= mUserKey[i]) {
+ mBinMap[j] = i;
+ j++;
+ }
+ }
+ mBinMap[mMaxBin] = mUserKey.length;
+ }
+
+ /**
+ * Map a value to a bin.
+ */
+ public int getBin(int x) {
+ if (x >= 0 && x < mMaxBin) {
+ return mBinMap[x];
+ } else if (x >= mMaxBin) {
+ return mBinMap[mMaxBin];
+ } else {
+ // x is negative. The bin will not be used.
+ return 0;
+ }
+ }
+
+ /**
+ * The number of bins in this map
+ */
+ public int count() {
+ return mCount;
+ }
+
+ /**
+ * For convenience, return the user key.
+ */
+ public int[] userKeys() {
+ return mUserKey;
+ }
+ }
+
+ /**
+ * A complete set of statistics. These are public, making it simpler for a client to
+ * fetch the individual fields.
+ */
+ public class Stats {
+
+ /**
+ * The start time for this set of statistics, in us.
+ */
+ public long mStartTimeUs = 0;
+
+ /**
+ * The completion time for this set of statistics, in ns. A value of zero means
+ * the statistics are still active.
+ */
+ public long mStopTimeUs = 0;
+
+ /**
+ * The build-time histogram. The total number of rebuilds is the sum over the
+ * histogram entries.
+ */
+ public int[] mTimes;
+
+ /**
+ * The reuse histogram. The total number of snapshot uses is the sum over the
+ * histogram entries.
+ */
+ public int[] mUsed;
+
+ /**
+ * The total number of rebuilds. This could be computed by summing over the use
+ * bins, but is maintained separately for convenience.
+ */
+ public int mTotalBuilds = 0;
+
+ /**
+ * The total number of times any snapshot was used.
+ */
+ public int mTotalUsed = 0;
+
+ /**
+ * The total number of builds that count as big, which means they took longer than
+ * SNAPSHOT_BIG_BUILD_TIME_NS.
+ */
+ public int mBigBuilds = 0;
+
+ /**
+ * The total number of short-lived snapshots
+ */
+ public int mShortLived = 0;
+
+ /**
+ * The time taken to build snapshots. This is cumulative over the rebuilds
+ * recorded in mRebuilds, so the average time to build a snapshot is given by
+ * mBuildTimeNs/mRebuilds. Note that this cannot be computed from the histogram.
+ */
+ public long mTotalTimeUs = 0;
+
+ /**
+ * The maximum build time since the last log.
+ */
+ public int mMaxBuildTimeUs = 0;
+
+ /**
+ * Record the rebuild. The parameters are the length of time it took to build the
+ * latest snapshot, and the number of times the _previous_ snapshot was used. A
+ * negative value for used signals an invalid value, which is the case the first
+ * time a snapshot is every built.
+ */
+ private void rebuild(int duration, int used,
+ int buildBin, int useBin, boolean big, boolean quick) {
+ mTotalBuilds++;
+ mTimes[buildBin]++;
+
+ if (used >= 0) {
+ mTotalUsed += used;
+ mUsed[useBin]++;
+ }
+
+ mTotalTimeUs += duration;
+ boolean reportIt = false;
+
+ if (big) {
+ mBigBuilds++;
+ }
+ if (quick) {
+ mShortLived++;
+ }
+ if (mMaxBuildTimeUs < duration) {
+ mMaxBuildTimeUs = duration;
+ }
+ }
+
+ private Stats(long now) {
+ mStartTimeUs = now;
+ mTimes = new int[mTimeBins.count()];
+ mUsed = new int[mUseBins.count()];
+ }
+
+ /**
+ * Create a copy of the argument. The copy is made under lock but can then be
+ * used without holding the lock.
+ */
+ private Stats(Stats orig) {
+ mStartTimeUs = orig.mStartTimeUs;
+ mStopTimeUs = orig.mStopTimeUs;
+ mTimes = Arrays.copyOf(orig.mTimes, orig.mTimes.length);
+ mUsed = Arrays.copyOf(orig.mUsed, orig.mUsed.length);
+ mTotalBuilds = orig.mTotalBuilds;
+ mTotalUsed = orig.mTotalUsed;
+ mBigBuilds = orig.mBigBuilds;
+ mShortLived = orig.mShortLived;
+ mTotalTimeUs = orig.mTotalTimeUs;
+ mMaxBuildTimeUs = orig.mMaxBuildTimeUs;
+ }
+
+ /**
+ * Set the end time for the statistics. The end time is used only for reporting
+ * in the dump() method.
+ */
+ private void complete(long stop) {
+ mStopTimeUs = stop;
+ }
+
+ /**
+ * Format a time span into ddd:HH:MM:SS. The input is in us.
+ */
+ private String durationToString(long us) {
+ // s has a range of several years
+ int s = (int) (us / (1000 * 1000));
+ int m = s / 60;
+ s %= 60;
+ int h = m / 60;
+ m %= 60;
+ int d = h / 24;
+ h %= 24;
+ if (d != 0) {
+ return TextUtils.formatSimple("%2d:%02d:%02d:%02d", d, h, m, s);
+ } else if (h != 0) {
+ return TextUtils.formatSimple("%2s %02d:%02d:%02d", "", h, m, s);
+ } else {
+ return TextUtils.formatSimple("%2s %2s %2d:%02d", "", "", m, s);
+ }
+ }
+
+ /**
+ * Print the prefix for dumping. This does not generate a line to the output.
+ */
+ private void dumpPrefix(PrintWriter pw, String indent, long now, boolean header,
+ String title) {
+ pw.print(indent + " ");
+ if (header) {
+ pw.format(Locale.US, "%-23s", title);
+ } else {
+ pw.format(Locale.US, "%11s", durationToString(now - mStartTimeUs));
+ if (mStopTimeUs != 0) {
+ pw.format(Locale.US, " %11s", durationToString(now - mStopTimeUs));
+ } else {
+ pw.format(Locale.US, " %11s", "now");
+ }
+ }
+ }
+
+ /**
+ * Dump the summary statistics record. Choose the header or the data.
+ * number of builds
+ * number of uses
+ * number of big builds
+ * number of short lifetimes
+ * cumulative build time, in seconds
+ * maximum build time, in ms
+ */
+ 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",
+ "TotTime", "MaxTime");
+ } else {
+ pw.format(Locale.US,
+ " %10d %10d %10d %10d %10d %10d",
+ mTotalBuilds, mTotalUsed, mBigBuilds, mShortLived,
+ mTotalTimeUs / 1000, mMaxBuildTimeUs / 1000);
+ }
+ pw.println();
+ }
+
+ /**
+ * Dump the build time histogram. Choose the header or the data.
+ */
+ private void dumpTimes(PrintWriter pw, String indent, long now, boolean header) {
+ dumpPrefix(pw, indent, now, header, "Build times");
+ if (header) {
+ int[] keys = mTimeBins.userKeys();
+ for (int i = 0; i < keys.length; i++) {
+ pw.format(Locale.US, " %10s",
+ TextUtils.formatSimple("<= %dms", keys[i]));
+ }
+ pw.format(Locale.US, " %10s",
+ TextUtils.formatSimple("> %dms", keys[keys.length - 1]));
+ } else {
+ for (int i = 0; i < mTimes.length; i++) {
+ pw.format(Locale.US, " %10d", mTimes[i]);
+ }
+ }
+ pw.println();
+ }
+
+ /**
+ * Dump the usage histogram. Choose the header or the data.
+ */
+ private void dumpUsage(PrintWriter pw, String indent, long now, boolean header) {
+ dumpPrefix(pw, indent, now, header, "Use counters");
+ if (header) {
+ int[] keys = mUseBins.userKeys();
+ for (int i = 0; i < keys.length; i++) {
+ pw.format(Locale.US, " %10s", TextUtils.formatSimple("<= %d", keys[i]));
+ }
+ pw.format(Locale.US, " %10s",
+ TextUtils.formatSimple("> %d", keys[keys.length - 1]));
+ } else {
+ for (int i = 0; i < mUsed.length; i++) {
+ pw.format(Locale.US, " %10d", mUsed[i]);
+ }
+ }
+ pw.println();
+ }
+
+ /**
+ * Dump something, based on the "what" parameter.
+ */
+ private void dump(PrintWriter pw, String indent, long now, boolean header, String what) {
+ if (what.equals("stats")) {
+ dumpStats(pw, indent, now, header);
+ } else if (what.equals("times")) {
+ dumpTimes(pw, indent, now, header);
+ } else if (what.equals("usage")) {
+ dumpUsage(pw, indent, now, header);
+ } else {
+ throw new IllegalArgumentException("unrecognized choice: " + what);
+ }
+ }
+
+ /**
+ * Report the object via an event. Presumably the record indicates an anomalous
+ * incident.
+ */
+ private void report() {
+ EventLogTags.writePmSnapshotStats(
+ mTotalBuilds, mTotalUsed, mBigBuilds, mShortLived,
+ mMaxBuildTimeUs / US_IN_MS, mTotalTimeUs / US_IN_MS);
+ }
+ }
+
+ /**
+ * Long statistics. These roll over approximately every week.
+ */
+ private Stats[] mLong;
+
+ /**
+ * Short statistics. These roll over approximately every minute;
+ */
+ private Stats[] mShort;
+
+ /**
+ * The time of the last build. This can be used to compute the length of time a
+ * snapshot existed before being replaced.
+ */
+ private long mLastBuildTime = 0;
+
+ /**
+ * Create a snapshot object. Initialize the bin levels. The last bin catches
+ * everything that is not caught earlier, so its value is not really important.
+ */
+ public SnapshotStatistics() {
+ // Create the bin thresholds. The time bins are in units of us.
+ mTimeBins = new BinMap(new int[] { 1, 2, 5, 10, 20, 50, 100 });
+ mUseBins = new BinMap(new int[] { 1, 2, 5, 10, 20, 50, 100 });
+
+ // Create the raw statistics
+ final long now = SystemClock.currentTimeMicro();
+ mLong = new Stats[2];
+ mLong[0] = new Stats(now);
+ mShort = new Stats[10];
+ mShort[0] = new Stats(now);
+
+ // Create the message handler for ticks and start the ticker.
+ mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ SnapshotStatistics.this.handleMessage(msg);
+ }
+ };
+ scheduleTick();
+ }
+
+ /**
+ * Handle a message. The only messages are ticks, so the message parameter is ignored.
+ */
+ private void handleMessage(@Nullable Message msg) {
+ tick();
+ scheduleTick();
+ }
+
+ /**
+ * Schedule one tick, a tick interval in the future.
+ */
+ private void scheduleTick() {
+ mHandler.sendEmptyMessageDelayed(0, SNAPSHOT_TICK_INTERVAL_MS);
+ }
+
+ /**
+ * Record a rebuild. Cumulative and current statistics are updated. Events may be
+ * generated.
+ * @param now The time at which the snapshot rebuild began, in ns.
+ * @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) {
+ // The duration has a span of about 2000s
+ final int duration = (int) (done - now);
+ boolean reportEvent = false;
+ synchronized (mLock) {
+ mLastBuildTime = now;
+
+ final int timeBin = mTimeBins.getBin(duration / 1000);
+ final int useBin = mUseBins.getBin(hits);
+ final boolean big = duration >= SNAPSHOT_BIG_BUILD_TIME_US;
+ final boolean quick = hits <= SNAPSHOT_SHORT_LIFETIME;
+
+ mShort[0].rebuild(duration, hits, timeBin, useBin, big, quick);
+ mLong[0].rebuild(duration, hits, timeBin, useBin, big, quick);
+ if (duration >= SNAPSHOT_REPORTABLE_BUILD_TIME_US) {
+ if (mEventsReported++ < SNAPSHOT_BUILD_REPORT_LIMIT) {
+ reportEvent = true;
+ }
+ }
+ }
+ // The IO to the logger is done outside the lock.
+ if (reportEvent) {
+ // Report the first N big builds, and every new maximum after that.
+ EventLogTags.writePmSnapshotRebuild(duration / US_IN_MS, hits);
+ }
+ }
+
+ /**
+ * 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.
+ */
+ private void shift(Stats[] s, long now) {
+ s[0].complete(now);
+ for (int i = s.length - 1; i > 0; i--) {
+ s[i] = s[i - 1];
+ }
+ s[0] = new Stats(now);
+ }
+
+ /**
+ * Roll the statistics.
+ * <ul>
+ * <li> Roll the quick statistics immediately.
+ * <li> Roll the long statistics every SNAPSHOT_LONG_TICKER ticks. The long
+ * statistics hold a week's worth of data.
+ * <li> Roll the logging statistics every SNAPSHOT_LOGGING_TICKER ticks. The logging
+ * statistics hold 10 minutes worth of data.
+ * </ul>
+ */
+ private void tick() {
+ synchronized (mLock) {
+ long now = SystemClock.currentTimeMicro();
+ mTicks++;
+ if (mTicks % SNAPSHOT_LONG_TICKS == 0) {
+ shift(mLong, now);
+ }
+ shift(mShort, now);
+ mEventsReported = 0;
+ }
+ }
+
+ /**
+ * Dump the statistics. The header is dumped from l[0], so that must not be null.
+ */
+ private void dump(PrintWriter pw, String indent, long now, Stats[] l, Stats[] s, String what) {
+ l[0].dump(pw, indent, now, true, what);
+ for (int i = 0; i < s.length; i++) {
+ if (s[i] != null) {
+ s[i].dump(pw, indent, now, false, what);
+ }
+ }
+ for (int i = 0; i < l.length; i++) {
+ if (l[i] != null) {
+ l[i].dump(pw, indent, now, false, what);
+ }
+ }
+ }
+
+ /**
+ * 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) {
+ // Grab the raw statistics under lock, but print them outside of the lock.
+ Stats[] l;
+ Stats[] s;
+ synchronized (mLock) {
+ l = Arrays.copyOf(mLong, mLong.length);
+ l[0] = new Stats(l[0]);
+ s = Arrays.copyOf(mShort, mShort.length);
+ s[0] = new Stats(s[0]);
+ }
+ pw.format(Locale.US, "%s Unrecorded hits %d", indent, unrecorded);
+ pw.println();
+ dump(pw, indent, now, l, s, "stats");
+ if (!full) {
+ return;
+ }
+ pw.println();
+ dump(pw, indent, now, l, s, "times");
+ pw.println();
+ dump(pw, indent, now, l, s, "usage");
+ }
+}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index e913829..81cfbf7 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -85,6 +85,15 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
}
]
+ },
+ {
+ "name": "PackageManagerServiceHostTests",
+ "file_patterns": ["AppsFilter\\.java"],
+ "options": [
+ {
+ "include-filter": "com.android.server.pm.test.OverlayActorVisibilityTest"
+ }
+ ]
}
],
"postsubmit": [
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
index bf2b3c7..a8a6a72 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
@@ -24,6 +24,7 @@
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.os.Build;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Patterns;
@@ -32,7 +33,6 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.util.List;
-import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -251,6 +251,10 @@
* improve the reliability of any legacy verifiers.
*/
private boolean isValidHost(String host) {
+ if (TextUtils.isEmpty(host)) {
+ return false;
+ }
+
mDomainMatcher.reset(host);
return mDomainMatcher.matches();
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
index 2a17c6d..3f00a9d 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
@@ -29,6 +29,7 @@
import android.os.ServiceSpecificException;
import java.util.List;
+import java.util.Objects;
import java.util.UUID;
public class DomainVerificationManagerStub extends IDomainVerificationManager.Stub {
@@ -110,6 +111,7 @@
public List<DomainOwner> getOwnersForDomain(@NonNull String domain,
@UserIdInt int userId) {
try {
+ Objects.requireNonNull(domain);
return mService.getOwnersForDomain(domain, userId);
} catch (Exception e) {
throw rethrow(e);
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 4ae79a2..a3e1a9c 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -742,6 +742,7 @@
}
public List<DomainOwner> getOwnersForDomain(@NonNull String domain, @UserIdInt int userId) {
+ Objects.requireNonNull(domain);
mEnforcer.assertOwnerQuerent(mConnection.getCallingUid(), mConnection.getCallingUserId(),
userId);
@@ -891,7 +892,7 @@
boolean hasAutoVerifyDomains = newDomainsSize > 0;
boolean needsBroadcast =
- applyImmutableState(pkgName, newStateMap, newAutoVerifyDomains);
+ applyImmutableState(newPkgSetting, newStateMap, newAutoVerifyDomains);
sendBroadcast = hasAutoVerifyDomains && needsBroadcast;
@@ -942,7 +943,8 @@
pkgState = new DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains);
}
- boolean needsBroadcast = applyImmutableState(pkgState, domains);
+ boolean needsBroadcast =
+ applyImmutableState(newPkgSetting, pkgState.getStateMap(), domains);
if (needsBroadcast && !isPendingOrRestored) {
// TODO(b/159952358): Test this behavior
// Attempt to preserve user experience by automatically verifying all domains from
@@ -989,22 +991,17 @@
}
}
- private boolean applyImmutableState(@NonNull DomainVerificationPkgState pkgState,
- @NonNull ArraySet<String> autoVerifyDomains) {
- return applyImmutableState(pkgState.getPackageName(), pkgState.getStateMap(),
- autoVerifyDomains);
- }
-
/**
* Applies any immutable state as the final step when adding or migrating state. Currently only
- * applies {@link SystemConfig#getLinkedApps()}, which approves all domains for a package.
+ * applies {@link SystemConfig#getLinkedApps()}, which approves all domains for a system app.
*
* @return whether or not a broadcast is necessary for this package
*/
- private boolean applyImmutableState(@NonNull String packageName,
+ private boolean applyImmutableState(@NonNull PackageSetting pkgSetting,
@NonNull ArrayMap<String, Integer> stateMap,
@NonNull ArraySet<String> autoVerifyDomains) {
- if (mSystemConfig.getLinkedApps().contains(packageName)) {
+ if (pkgSetting.isSystem()
+ && mSystemConfig.getLinkedApps().contains(pkgSetting.getName())) {
int domainsSize = autoVerifyDomains.size();
for (int index = 0; index < domainsSize; index++) {
stateMap.put(autoVerifyDomains.valueAt(index),
@@ -1317,7 +1314,7 @@
if (pkgSetting == null || pkgSetting.getPkg() == null) {
continue;
}
- resetDomainState(pkgState, pkgSetting.getPkg());
+ resetDomainState(pkgState.getStateMap(), pkgSetting);
}
} else {
int size = packageNames.size();
@@ -1328,7 +1325,7 @@
if (pkgSetting == null || pkgSetting.getPkg() == null) {
continue;
}
- resetDomainState(pkgState, pkgSetting.getPkg());
+ resetDomainState(pkgState.getStateMap(), pkgSetting);
}
}
}
@@ -1340,9 +1337,8 @@
/**
* Reset states that are mutable by the domain verification agent.
*/
- private void resetDomainState(@NonNull DomainVerificationPkgState pkgState,
- @NonNull AndroidPackage pkg) {
- ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
+ private void resetDomainState(@NonNull ArrayMap<String, Integer> stateMap,
+ @NonNull PackageSetting pkgSetting) {
int size = stateMap.size();
for (int index = size - 1; index >= 0; index--) {
Integer state = stateMap.valueAt(index);
@@ -1362,7 +1358,8 @@
}
}
- applyImmutableState(pkgState, mCollector.collectValidAutoVerifyDomains(pkg));
+ applyImmutableState(pkgSetting, stateMap,
+ mCollector.collectValidAutoVerifyDomains(pkgSetting.getPkg()));
}
@Override
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
index 44ff3eb..246810f 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
@@ -22,6 +22,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.text.TextUtils;
+import android.util.Patterns;
import com.android.internal.util.CollectionUtils;
import com.android.server.compat.PlatformCompat;
@@ -29,9 +31,13 @@
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.util.Set;
+import java.util.regex.Matcher;
public final class DomainVerificationUtils {
+ private static final ThreadLocal<Matcher> sCachedMatcher = ThreadLocal.withInitial(
+ () -> Patterns.DOMAIN_NAME.matcher(""));
+
/**
* Consolidates package exception messages. A generic unavailable message is included since the
* caller doesn't bother to check why the package isn't available.
@@ -48,6 +54,15 @@
return false;
}
+ String host = intent.getData().getHost();
+ if (TextUtils.isEmpty(host)) {
+ return false;
+ }
+
+ if (!sCachedMatcher.get().reset(host).matches()) {
+ return false;
+ }
+
Set<String> categories = intent.getCategories();
int categoriesSize = CollectionUtils.size(categories);
if (categoriesSize > 2) {
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index cd7f685..6f6bdac 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -394,13 +394,13 @@
throw new IllegalStateException("Have not received sensor event.");
}
- if (latestEvent.values.length != mExpectedValues.size()) {
+ if (latestEvent.values.length < mExpectedValues.size()) {
throw new RuntimeException("Number of supplied numeric range(s) does not "
+ "match the number of values in the latest sensor event for sensor: "
+ mSensor);
}
- for (int i = 0; i < latestEvent.values.length; i++) {
+ for (int i = 0; i < mExpectedValues.size(); i++) {
if (!adheresToRange(latestEvent.values[i], mExpectedValues.get(i))) {
return false;
}
diff --git a/services/core/java/com/android/server/power/FaceDownDetector.java b/services/core/java/com/android/server/power/FaceDownDetector.java
index 474ce59..816c81d 100644
--- a/services/core/java/com/android/server/power/FaceDownDetector.java
+++ b/services/core/java/com/android/server/power/FaceDownDetector.java
@@ -330,7 +330,9 @@
private boolean isEnabled() {
return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, KEY_FEATURE_ENABLED,
- DEFAULT_FEATURE_ENABLED);
+ DEFAULT_FEATURE_ENABLED)
+ && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_flipToScreenOffEnabled);
}
private float getAccelerationThreshold() {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 4adcfb6..9e19f57 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -613,9 +613,11 @@
PackageInstaller.SessionInfo session = mContext.getPackageManager()
.getPackageInstaller().getSessionInfo(rollback.getStagedSessionId());
if (session == null || session.isStagedSessionFailed()) {
- iter.remove();
- deleteRollback(rollback,
- "Session " + rollback.getStagedSessionId() + " not existed or failed");
+ if (rollback.isEnabling()) {
+ iter.remove();
+ deleteRollback(rollback, "Session " + rollback.getStagedSessionId()
+ + " not existed or failed");
+ }
continue;
}
diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
index 189f47f..35aff8d 100644
--- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
+++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
@@ -21,8 +21,8 @@
import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_CANCELLED;
import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_TIMED_OUT;
-import static com.android.server.rotationresolver.RotationResolverManagerService.RESOLUTION_FAILURE;
-import static com.android.server.rotationresolver.RotationResolverManagerService.logRotationStats;
+import static com.android.server.rotationresolver.RotationResolverManagerService.errorCodeToProto;
+import static com.android.server.rotationresolver.RotationResolverManagerService.surfaceRotationToProto;
import android.annotation.NonNull;
import android.content.ComponentName;
@@ -173,8 +173,10 @@
request.mCallbackInternal.onSuccess(rotation);
final long timeToCalculate =
SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
- logRotationStats(request.mRemoteRequest.getProposedRotation(),
- request.mRemoteRequest.getCurrentRotation(), rotation, timeToCalculate);
+ RotationResolverManagerService.logRotationStatsWithTimeToCalculate(
+ request.mRemoteRequest.getProposedRotation(),
+ request.mRemoteRequest.getCurrentRotation(),
+ surfaceRotationToProto(rotation), timeToCalculate);
Slog.d(TAG, "onSuccess:" + rotation);
Slog.d(TAG, "timeToCalculate:" + timeToCalculate);
}
@@ -192,8 +194,9 @@
request.mCallbackInternal.onFailure(error);
final long timeToCalculate =
SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
- logRotationStats(request.mRemoteRequest.getProposedRotation(),
- request.mRemoteRequest.getCurrentRotation(), RESOLUTION_FAILURE,
+ RotationResolverManagerService.logRotationStatsWithTimeToCalculate(
+ request.mRemoteRequest.getProposedRotation(),
+ request.mRemoteRequest.getCurrentRotation(), errorCodeToProto(error),
timeToCalculate);
Slog.d(TAG, "onFailure:" + error);
Slog.d(TAG, "timeToCalculate:" + timeToCalculate);
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index a749d18..19a246e 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
@@ -18,6 +18,9 @@
import static android.provider.DeviceConfig.NAMESPACE_ROTATION_RESOLVER;
import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_CANCELLED;
+import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_NOT_SUPPORTED;
+import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_PREEMPTED;
+import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_TIMED_OUT;
import static com.android.internal.util.FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_0;
import static com.android.internal.util.FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_180;
@@ -37,6 +40,7 @@
import android.provider.DeviceConfig;
import android.rotationresolver.RotationResolverInternal;
import android.service.rotationresolver.RotationResolutionRequest;
+import android.service.rotationresolver.RotationResolverService;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -217,24 +221,37 @@
}
}
- static void logRotationStats(int proposedRotation, int currentRotation,
- int resolvedRotation, long timeToCalculate) {
+ static void logRotationStatsWithTimeToCalculate(int proposedRotation, int currentRotation,
+ int result, long timeToCalculate) {
FrameworkStatsLog.write(FrameworkStatsLog.AUTO_ROTATE_REPORTED,
/* previous_orientation= */ surfaceRotationToProto(currentRotation),
/* proposed_orientation= */ surfaceRotationToProto(proposedRotation),
- /* resolved_orientation= */ surfaceRotationToProto(resolvedRotation),
+ result,
/* process_duration_millis= */ timeToCalculate);
}
static void logRotationStats(int proposedRotation, int currentRotation,
- int resolvedRotation) {
+ int result) {
FrameworkStatsLog.write(FrameworkStatsLog.AUTO_ROTATE_REPORTED,
/* previous_orientation= */ surfaceRotationToProto(currentRotation),
/* proposed_orientation= */ surfaceRotationToProto(proposedRotation),
- /* resolved_orientation= */ surfaceRotationToProto(resolvedRotation));
+ result);
}
- private static int surfaceRotationToProto(@Surface.Rotation int rotationPoseResult) {
+ static int errorCodeToProto(@RotationResolverService.FailureCodes int error) {
+ switch (error) {
+ case ROTATION_RESULT_FAILURE_NOT_SUPPORTED:
+ return RESOLUTION_UNAVAILABLE;
+ case ROTATION_RESULT_FAILURE_TIMED_OUT:
+ case ROTATION_RESULT_FAILURE_PREEMPTED:
+ case ROTATION_RESULT_FAILURE_CANCELLED:
+ return ORIENTATION_UNKNOWN;
+ default:
+ return RESOLUTION_FAILURE;
+ }
+ }
+
+ static int surfaceRotationToProto(@Surface.Rotation int rotationPoseResult) {
switch (rotationPoseResult) {
case Surface.ROTATION_0:
return AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_0;
@@ -245,7 +262,8 @@
case Surface.ROTATION_270:
return AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_270;
default:
- return ORIENTATION_UNKNOWN;
+ // Should not reach here.
+ return RESOLUTION_FAILURE;
}
}
}
diff --git a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
index 072cc16f..7649958 100644
--- a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
+++ b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java
@@ -82,6 +82,9 @@
handleAutoTimeDetectionChangedOnHandlerThread();
}
});
+ mServiceConfigAccessor.addListener(
+ () -> mHandler.post(
+ EnvironmentImpl.this::handleAutoTimeDetectionChangedOnHandlerThread));
}
/** Internal method for handling the auto time setting being changed. */
diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java
index d91e9c2..7145f5e 100644
--- a/services/core/java/com/android/server/timedetector/ServerFlags.java
+++ b/services/core/java/com/android/server/timedetector/ServerFlags.java
@@ -30,7 +30,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.DateTimeException;
import java.time.Duration;
+import java.time.Instant;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -62,6 +64,8 @@
KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
+ KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE,
+ KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE,
})
@Retention(RetentionPolicy.SOURCE)
@interface DeviceConfigKey {}
@@ -135,6 +139,23 @@
public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT =
"location_time_zone_detection_setting_enabled_default";
+ /**
+ * The key to override the time detector origin priorities configuration. A comma-separated list
+ * of strings that will be passed to {@link TimeDetectorStrategy#stringToOrigin(String)}.
+ * All values must be recognized or the override value will be ignored.
+ */
+ @DeviceConfigKey
+ public static final String KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE =
+ "time_detector_origin_priorities_override";
+
+ /**
+ * The key to override the time detector lower bound configuration. The values is the number of
+ * milliseconds since the beginning of the Unix epoch.
+ */
+ @DeviceConfigKey
+ public static final String KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE =
+ "time_detector_lower_bound_millis_override";
+
@GuardedBy("mListeners")
private final ArrayMap<ConfigurationChangeListener, Set<String>> mListeners = new ArrayMap<>();
@@ -209,6 +230,38 @@
}
/**
+ * Returns an optional string array value from {@link DeviceConfig} from the system_time
+ * namespace, returns {@link Optional#empty()} if there is no explicit value set.
+ */
+ @NonNull
+ public Optional<String[]> getOptionalStringArray(@DeviceConfigKey String key) {
+ Optional<String> string = getOptionalString(key);
+ if (!string.isPresent()) {
+ return Optional.empty();
+ }
+ return Optional.of(string.get().split(","));
+ }
+
+ /**
+ * Returns an {@link Instant} from {@link DeviceConfig} from the system_time
+ * namespace, returns the {@code defaultValue} if the value is missing or invalid.
+ */
+ @NonNull
+ public Optional<Instant> getOptionalInstant(@DeviceConfigKey String key) {
+ String value = DeviceConfig.getProperty(NAMESPACE_SYSTEM_TIME, key);
+ if (value == null) {
+ return Optional.empty();
+ }
+
+ try {
+ long millis = Long.parseLong(value);
+ return Optional.of(Instant.ofEpochMilli(millis));
+ } catch (DateTimeException | NumberFormatException e) {
+ return Optional.empty();
+ }
+ }
+
+ /**
* Returns an optional boolean value from {@link DeviceConfig} from the system_time
* namespace, returns {@link Optional#empty()} if there is no explicit value set.
*/
diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java
index be4432a..dac8a0a 100644
--- a/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java
@@ -15,9 +15,10 @@
*/
package com.android.server.timedetector;
+import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE;
+import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE;
import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK;
import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY;
-import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,12 +29,17 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+import com.android.server.timedetector.TimeDetectorStrategy.Origin;
import com.android.server.timezonedetector.ConfigurationChangeListener;
import java.time.Instant;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
+import java.util.function.Supplier;
/**
* A singleton that provides access to service configuration for time detection. This hides how
@@ -48,7 +54,7 @@
* By default telephony and network only suggestions are accepted and telephony takes
* precedence over network.
*/
- private static final @TimeDetectorStrategy.Origin int[]
+ private static final @Origin int[]
DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES = { ORIGIN_TELEPHONY, ORIGIN_NETWORK };
/**
@@ -60,6 +66,8 @@
private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet(
new ArraySet<>(new String[] {
+ KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE,
+ KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE,
}));
private static final Object SLOCK = new Object();
@@ -70,8 +78,9 @@
private static ServiceConfigAccessor sInstance;
@NonNull private final Context mContext;
+ @NonNull private final ConfigOriginPrioritiesSupplier mConfigOriginPrioritiesSupplier;
+ @NonNull private final ServerFlagsOriginPrioritiesSupplier mServerFlagsOriginPrioritiesSupplier;
@NonNull private final ServerFlags mServerFlags;
- @NonNull private final int[] mOriginPriorities;
/**
* If a newly calculated system clock time and the current system clock time differs by this or
@@ -83,7 +92,9 @@
private ServiceConfigAccessor(@NonNull Context context) {
mContext = Objects.requireNonNull(context);
mServerFlags = ServerFlags.getInstance(mContext);
- mOriginPriorities = getOriginPrioritiesInternal();
+ mConfigOriginPrioritiesSupplier = new ConfigOriginPrioritiesSupplier(context);
+ mServerFlagsOriginPrioritiesSupplier =
+ new ServerFlagsOriginPrioritiesSupplier(mServerFlags);
mSystemClockUpdateThresholdMillis =
SystemProperties.getInt("ro.sys.time_detector_update_diff",
SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT);
@@ -111,31 +122,109 @@
}
@NonNull
- int[] getOriginPriorities() {
- return mOriginPriorities;
+ @Origin int[] getOriginPriorities() {
+ int[] serverFlagsValue = mServerFlagsOriginPrioritiesSupplier.get();
+ if (serverFlagsValue != null) {
+ return serverFlagsValue;
+ }
+
+ int[] configValue = mConfigOriginPrioritiesSupplier.get();
+ if (configValue != null) {
+ return configValue;
+ }
+ return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES;
}
int systemClockUpdateThresholdMillis() {
return mSystemClockUpdateThresholdMillis;
}
+ @NonNull
Instant autoTimeLowerBound() {
- return TIME_LOWER_BOUND_DEFAULT;
+ return mServerFlags.getOptionalInstant(KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE)
+ .orElse(TIME_LOWER_BOUND_DEFAULT);
}
- private int[] getOriginPrioritiesInternal() {
- String[] originStrings =
- mContext.getResources().getStringArray(R.array.config_autoTimeSourcesPriority);
- if (originStrings.length == 0) {
- return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES;
- } else {
- int[] origins = new int[originStrings.length];
- for (int i = 0; i < originStrings.length; i++) {
- int origin = stringToOrigin(originStrings[i]);
- origins[i] = origin;
- }
+ /**
+ * A base supplier of an array of time origin integers in priority order.
+ * It handles memoization of the result to avoid repeated string parsing when nothing has
+ * changed.
+ */
+ private abstract static class BaseOriginPrioritiesSupplier implements Supplier<@Origin int[]> {
+ @GuardedBy("this") @Nullable private String[] mLastPriorityStrings;
+ @GuardedBy("this") @Nullable private int[] mLastPriorityInts;
- return origins;
+ /** Returns an array of {@code ORIGIN_*} values, or {@code null}. */
+ @Override
+ @Nullable
+ public @Origin int[] get() {
+ String[] priorityStrings = lookupPriorityStrings();
+ synchronized (this) {
+ if (Arrays.equals(mLastPriorityStrings, priorityStrings)) {
+ return mLastPriorityInts;
+ }
+
+ int[] priorityInts = null;
+ if (priorityStrings != null && priorityStrings.length > 0) {
+ priorityInts = new int[priorityStrings.length];
+ try {
+ for (int i = 0; i < priorityInts.length; i++) {
+ String priorityString = priorityStrings[i];
+ Preconditions.checkArgument(priorityString != null);
+
+ priorityString = priorityString.trim();
+ priorityInts[i] = TimeDetectorStrategy.stringToOrigin(priorityString);
+ }
+ } catch (IllegalArgumentException e) {
+ // If any strings were bad and they were ignored then the semantics of the
+ // whole list could change, so return null.
+ priorityInts = null;
+ }
+ }
+ mLastPriorityStrings = priorityStrings;
+ mLastPriorityInts = priorityInts;
+ return priorityInts;
+ }
+ }
+
+ @Nullable
+ protected abstract String[] lookupPriorityStrings();
+ }
+
+ /** Supplies origin priorities from config_autoTimeSourcesPriority. */
+ private static class ConfigOriginPrioritiesSupplier extends BaseOriginPrioritiesSupplier {
+
+ @NonNull private final Context mContext;
+
+ private ConfigOriginPrioritiesSupplier(Context context) {
+ mContext = Objects.requireNonNull(context);
+ }
+
+ @Override
+ @Nullable
+ protected String[] lookupPriorityStrings() {
+ return mContext.getResources().getStringArray(R.array.config_autoTimeSourcesPriority);
+ }
+ }
+
+ /**
+ * Supplies origin priorities from device_config (server flags), see
+ * {@link ServerFlags#KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE}.
+ */
+ private static class ServerFlagsOriginPrioritiesSupplier extends BaseOriginPrioritiesSupplier {
+
+ @NonNull private final ServerFlags mServerFlags;
+
+ private ServerFlagsOriginPrioritiesSupplier(ServerFlags serverFlags) {
+ mServerFlags = Objects.requireNonNull(serverFlags);
+ }
+
+ @Override
+ @Nullable
+ protected String[] lookupPriorityStrings() {
+ Optional<String[]> priorityStrings = mServerFlags.getOptionalStringArray(
+ KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE);
+ return priorityStrings.orElse(null);
}
}
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 20c1c3c..0f14af4 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -30,6 +30,8 @@
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.VisibleForTesting;
@@ -100,6 +102,7 @@
}
@Override
+ @NonNull
public TimeCapabilitiesAndConfig getCapabilitiesAndConfig() {
int userId = mCallerIdentityInjector.getCallingUserId();
return getTimeCapabilitiesAndConfig(userId);
@@ -119,7 +122,8 @@
}
@Override
- public boolean updateConfiguration(TimeConfiguration timeConfiguration) {
+ public boolean updateConfiguration(@NonNull TimeConfiguration timeConfiguration) {
+ enforceManageTimeDetectorPermission();
// TODO(b/172891783) Add actual logic
return false;
}
@@ -179,6 +183,13 @@
ipw.flush();
}
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ new TimeDetectorShellCommand(this).exec(
+ this, in, out, err, args, callback, resultReceiver);
+ }
+
private void enforceSuggestTelephonyTimePermission() {
mContext.enforceCallingPermission(
android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE,
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java
new file mode 100644
index 0000000..233cc57
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java
@@ -0,0 +1,86 @@
+/*
+ * 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.timedetector;
+
+import static android.app.timedetector.TimeDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED;
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
+
+import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE;
+import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE;
+
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/** Implements the shell command interface for {@link TimeDetectorService}. */
+class TimeDetectorShellCommand extends ShellCommand {
+
+ private final TimeDetectorService mInterface;
+
+ TimeDetectorShellCommand(TimeDetectorService timeDetectorService) {
+ mInterface = timeDetectorService;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ switch (cmd) {
+ case SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED:
+ return runIsAutoDetectionEnabled();
+ default: {
+ return handleDefaultCommands(cmd);
+ }
+ }
+ }
+
+ private int runIsAutoDetectionEnabled() {
+ final PrintWriter pw = getOutPrintWriter();
+ boolean enabled = mInterface.getCapabilitiesAndConfig()
+ .getTimeConfiguration()
+ .isAutoDetectionEnabled();
+ pw.println(enabled);
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Time Detector (time_detector) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.printf(" %s\n", SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED);
+ pw.println(" Prints true/false according to the automatic time detection setting");
+ pw.println();
+ pw.printf("This service is also affected by the following device_config flags in the"
+ + " %s namespace:\n", NAMESPACE_SYSTEM_TIME);
+ pw.printf(" %s - the lower bound used to validate time suggestions when they are"
+ + " received.\n", KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE);
+ pw.println(" Specified in milliseconds since the start of the Unix epoch.");
+ pw.printf(" %s - [default=null], a comma separated list of origins. See"
+ + " TimeDetectorStrategy for details\n",
+ KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE);
+ pw.println();
+ pw.println("Example:");
+ pw.printf(" $ adb shell cmd device_config put %s %s %s\n",
+ NAMESPACE_SYSTEM_TIME, KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE,
+ "external");
+ pw.println("See adb shell cmd device_config for more information.");
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index be382f0..ff5060e 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -27,10 +27,13 @@
import android.os.TimestampedValue;
import android.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
import com.android.server.timezonedetector.Dumpable;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
/**
* The interface for the class that implements the time detection algorithm used by the
@@ -44,9 +47,9 @@
*/
public interface TimeDetectorStrategy extends Dumpable {
- @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK, ORIGIN_GNSS,
- ORIGIN_EXTERNAL })
+ @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK, ORIGIN_GNSS, ORIGIN_EXTERNAL })
@Retention(RetentionPolicy.SOURCE)
+ @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
@interface Origin {}
/** Used when a time value originated from a telephony signal. */
@@ -126,9 +129,11 @@
/**
* Converts a human readable config string to one of the {@code ORIGIN_} constants.
- * Throws an {@link IllegalArgumentException} if the value is unrecognized.
+ * Throws an {@link IllegalArgumentException} if the value is unrecognized or {@code null}.
*/
static @Origin int stringToOrigin(String originString) {
+ Preconditions.checkArgument(originString != null);
+
switch (originString) {
case "manual":
return ORIGIN_MANUAL;
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index db8a59e..357c232 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -325,9 +325,9 @@
ipw.println("mEnvironment.systemClockMillis()=" + mEnvironment.systemClockMillis());
ipw.println("mEnvironment.systemClockUpdateThresholdMillis()="
+ mEnvironment.systemClockUpdateThresholdMillis());
+ Instant autoTimeLowerBound = mEnvironment.autoTimeLowerBound();
ipw.printf("mEnvironment.autoTimeLowerBound()=%s(%s)\n",
- mEnvironment.autoTimeLowerBound(),
- mEnvironment.autoTimeLowerBound().toEpochMilli());
+ autoTimeLowerBound, autoTimeLowerBound.toEpochMilli());
String priorities =
Arrays.stream(mEnvironment.autoOriginPriorities())
.mapToObj(TimeDetectorStrategy::originToString)
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index c20400a..457dc43 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -321,6 +321,13 @@
ipw.flush();
}
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ new TimeZoneDetectorShellCommand(this).exec(
+ this, in, out, err, args, callback, resultReceiver);
+ }
+
private void enforceManageTimeZoneDetectorPermission() {
mContext.enforceCallingPermission(
android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION,
@@ -346,13 +353,5 @@
android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE,
"suggest manual time and time zone");
}
-
- @Override
- public void onShellCommand(FileDescriptor in, FileDescriptor out,
- FileDescriptor err, String[] args, ShellCallback callback,
- ResultReceiver resultReceiver) {
- new TimeZoneDetectorShellCommand(this).exec(
- this, in, out, err, args, callback, resultReceiver);
- }
}
diff --git a/services/core/java/com/android/server/trust/OWNERS b/services/core/java/com/android/server/trust/OWNERS
index b039c4b..e2c6ce1 100644
--- a/services/core/java/com/android/server/trust/OWNERS
+++ b/services/core/java/com/android/server/trust/OWNERS
@@ -1 +1 @@
-include /core/java/android/app/trust/OWNERS
+include /core/java/android/service/trust/OWNERS
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index a59b368..8818023 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -119,18 +119,18 @@
if (!mIsQuitting) {
mRouteSelectionCallback = new RouteSelectionCallback();
mConnectivityManager.requestBackgroundNetwork(
- getRouteSelectionRequest(), mHandler, mRouteSelectionCallback);
+ getRouteSelectionRequest(), mRouteSelectionCallback, mHandler);
mWifiBringupCallback = new NetworkBringupCallback();
mConnectivityManager.requestBackgroundNetwork(
- getWifiNetworkRequest(), mHandler, mWifiBringupCallback);
+ getWifiNetworkRequest(), mWifiBringupCallback, mHandler);
for (final int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
final NetworkBringupCallback cb = new NetworkBringupCallback();
mCellBringupCallbacks.add(cb);
mConnectivityManager.requestBackgroundNetwork(
- getCellNetworkRequestForSubId(subId), mHandler, cb);
+ getCellNetworkRequestForSubId(subId), cb, mHandler);
}
} else {
mRouteSelectionCallback = null;
@@ -154,14 +154,14 @@
* Builds the Route selection request
*
* <p>This request is guaranteed to select carrier-owned, non-VCN underlying networks by virtue
- * of a populated set of subIds as expressed in NetworkCapabilities#getSubIds(). Only carrier
- * owned networks may be selected, as the request specifies only subIds in the VCN's
+ * of a populated set of subIds as expressed in NetworkCapabilities#getSubscriptionIds(). Only
+ * 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.
*/
private NetworkRequest getRouteSelectionRequest() {
return getBaseNetworkRequestBuilder()
- .setSubIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
+ .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
.build();
}
@@ -177,7 +177,7 @@
private NetworkRequest getWifiNetworkRequest() {
return getBaseNetworkRequestBuilder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .setSubIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
+ .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
.build();
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 23fb95b..38f5dd6 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -2127,7 +2127,7 @@
(VcnControlPlaneIkeConfig) mConnectionConfig.getControlPlaneConfig();
final IkeSessionParams.Builder builder =
new IkeSessionParams.Builder(controlPlaneConfig.getIkeSessionParams());
- builder.setConfiguredNetwork(network);
+ builder.setNetwork(network);
return builder.build();
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 89b7bbd..9acbdcc 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2378,6 +2378,46 @@
}
}
+ /**
+ * Propagate a wake event to the wallpaper engine.
+ */
+ public void notifyWakingUp(int x, int y, @NonNull Bundle extras) {
+ synchronized (mLock) {
+ final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
+ data.connection.forEachDisplayConnector(
+ displayConnector -> {
+ if (displayConnector.mEngine != null) {
+ try {
+ displayConnector.mEngine.dispatchWallpaperCommand(
+ WallpaperManager.COMMAND_WAKING_UP, x, y, -1, extras);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Propagate a sleep event to the wallpaper engine.
+ */
+ public void notifyGoingToSleep(int x, int y, @NonNull Bundle extras) {
+ synchronized (mLock) {
+ final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
+ data.connection.forEachDisplayConnector(
+ displayConnector -> {
+ if (displayConnector.mEngine != null) {
+ try {
+ displayConnector.mEngine.dispatchWallpaperCommand(
+ WallpaperManager.COMMAND_GOING_TO_SLEEP, x, y, -1, extras);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+ }
+
@Override
public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index e4dc8c2..561f1ad 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -96,6 +96,8 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
+import com.android.server.apphibernation.AppHibernationManagerInternal;
+import com.android.server.apphibernation.AppHibernationService;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -163,6 +165,8 @@
*/
private final LaunchObserverRegistryImpl mLaunchObserver;
@VisibleForTesting static final int LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE = 512;
+ private final ArrayMap<String, Boolean> mLastHibernationStates = new ArrayMap<>();
+ private AppHibernationManagerInternal mAppHibernationManagerInternal;
/**
* The information created when an intent is incoming but we do not yet know whether it will be
@@ -789,6 +793,27 @@
}
}
+ @Nullable
+ private AppHibernationManagerInternal getAppHibernationManagerInternal() {
+ if (!AppHibernationService.isAppHibernationEnabled()) return null;
+ if (mAppHibernationManagerInternal == null) {
+ mAppHibernationManagerInternal =
+ LocalServices.getService(AppHibernationManagerInternal.class);
+ }
+ return mAppHibernationManagerInternal;
+ }
+
+ /**
+ * Notifies the tracker before the package is unstopped because of launching activity.
+ * @param packageName The package to be unstopped.
+ */
+ void notifyBeforePackageUnstopped(@NonNull String packageName) {
+ final AppHibernationManagerInternal ahmInternal = getAppHibernationManagerInternal();
+ if (ahmInternal != null) {
+ mLastHibernationStates.put(packageName, ahmInternal.isHibernatingGlobally(packageName));
+ }
+ }
+
/**
* Notifies the tracker that we called immediately before we call bindApplication on the client.
*
@@ -823,6 +848,8 @@
}
stopLaunchTrace(info);
+ final Boolean isHibernating =
+ mLastHibernationStates.remove(info.mLastLaunchedActivity.packageName);
if (abort) {
mSupervisor.stopWaitingForActivityVisible(info.mLastLaunchedActivity);
launchObserverNotifyActivityLaunchCancelled(info);
@@ -830,7 +857,7 @@
if (info.isInterestingToLoggerAndObserver()) {
launchObserverNotifyActivityLaunchFinished(info, timestampNs);
}
- logAppTransitionFinished(info);
+ logAppTransitionFinished(info, isHibernating != null ? isHibernating : false);
}
info.mPendingDrawActivities.clear();
mTransitionInfoList.remove(info);
@@ -859,7 +886,7 @@
}
}
- private void logAppTransitionFinished(@NonNull TransitionInfo info) {
+ private void logAppTransitionFinished(@NonNull TransitionInfo info, boolean isHibernating) {
if (DEBUG_METRICS) Slog.i(TAG, "logging finished transition " + info);
// Take a snapshot of the transition info before sending it to the handler for logging.
@@ -868,7 +895,7 @@
if (info.isInterestingToLoggerAndObserver()) {
BackgroundThread.getHandler().post(() -> logAppTransition(
info.mCurrentTransitionDeviceUptime, info.mCurrentTransitionDelayMs,
- infoSnapshot));
+ infoSnapshot, isHibernating));
}
BackgroundThread.getHandler().post(() -> logAppDisplayed(infoSnapshot));
if (info.mPendingFullyDrawn != null) {
@@ -880,7 +907,7 @@
// This gets called on a background thread without holding the activity manager lock.
private void logAppTransition(int currentTransitionDeviceUptime, int currentTransitionDelayMs,
- TransitionInfoSnapshot info) {
+ TransitionInfoSnapshot info, boolean isHibernating) {
final LogMaker builder = new LogMaker(APP_TRANSITION);
builder.setPackageName(info.packageName);
builder.setType(info.type);
@@ -933,7 +960,8 @@
packageOptimizationInfo.getCompilationReason(),
packageOptimizationInfo.getCompilationFilter(),
info.sourceType,
- info.sourceEventDelayMs);
+ info.sourceEventDelayMs,
+ isHibernating);
if (DEBUG_METRICS) {
Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)",
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c8b7038..c39358e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -189,6 +189,7 @@
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
+import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
@@ -215,14 +216,8 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_WALLPAPER;
-import static com.android.server.wm.WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowManagerService.letterboxBackgroundTypeToString;
import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
@@ -268,7 +263,6 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -344,7 +338,6 @@
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.Task.ActivityState;
import com.android.server.wm.WindowManagerService.H;
-import com.android.server.wm.WindowManagerService.LetterboxBackgroundType;
import com.android.server.wm.utils.InsetUtils;
import com.google.android.collect.Sets;
@@ -587,7 +580,10 @@
AnimatingActivityRegistry mAnimatingActivityRegistry;
- private Task mLastParent;
+ // Set to the previous Task parent of the ActivityRecord when it is reparented to a new Task
+ // due to picture-in-picture. This gets cleared whenever this activity or the Task
+ // it references to gets removed. This should also be cleared when we move out of pip.
+ private Task mLastParentBeforePip;
boolean firstWindowDrawn;
/** Whether the visible window(s) of this activity is drawn. */
@@ -642,7 +638,8 @@
*/
private boolean mWillCloseOrEnterPip;
- private Letterbox mLetterbox;
+ @VisibleForTesting
+ final LetterboxUiController mLetterboxUiController;
/**
* The scale to fit at least one side of the activity to its parent. If the activity uses
@@ -671,8 +668,6 @@
@Nullable
private Rect mLetterboxBoundsForFixedOrientationAndAspectRatio;
- private boolean mShowWallpaperForLetterboxBackground;
-
// activity is not displayed?
// TODO: rename to mNoDisplay
@VisibleForTesting
@@ -1096,60 +1091,11 @@
pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
}
}
-
- dumpLetterboxInfo(pw, prefix);
- }
-
- private void dumpLetterboxInfo(PrintWriter pw, String prefix) {
- final WindowState mainWin = findMainWindow();
- if (mainWin == null) {
- return;
+ if (mLastParentBeforePip != null) {
+ pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId);
}
- boolean areBoundsLetterboxed = mainWin.isLetterboxedAppWindow();
- pw.println(prefix + "areBoundsLetterboxed=" + areBoundsLetterboxed);
- if (!areBoundsLetterboxed) {
- return;
- }
-
- pw.println(prefix + " letterboxReason=" + getLetterboxReasonString(mainWin));
- pw.println(prefix + " letterboxAspectRatio=" + computeAspectRatio(getBounds()));
-
- boolean isLetterboxUiShown = isLetterboxed(mainWin);
- pw.println(prefix + "isLetterboxUiShown=" + isLetterboxUiShown);
-
- if (!isLetterboxUiShown) {
- return;
- }
- pw.println(prefix + " letterboxBackgroundColor=" + Integer.toHexString(
- getLetterboxBackgroundColor().toArgb()));
- pw.println(prefix + " letterboxBackgroundType="
- + letterboxBackgroundTypeToString(mWmService.getLetterboxBackgroundType()));
- if (mWmService.getLetterboxBackgroundType() == LETTERBOX_BACKGROUND_WALLPAPER) {
- pw.println(prefix + " isLetterboxWallpaperBlurSupported="
- + isLetterboxWallpaperBlurSupported());
- pw.println(prefix + " letterboxBackgroundWallpaperDarkScrimAlpha="
- + getLetterboxWallpaperDarkScrimAlpha());
- pw.println(prefix + " letterboxBackgroundWallpaperBlurRadius="
- + getLetterboxWallpaperBlurRadius());
- }
- }
-
- /**
- * Returns a string representing the reason for letterboxing. This method assumes the activity
- * is letterboxed.
- */
- private String getLetterboxReasonString(WindowState mainWin) {
- if (inSizeCompatMode()) {
- return "SIZE_COMPAT_MODE";
- }
- if (isLetterboxedForFixedOrientationAndAspectRatio()) {
- return "FIXED_ORIENTATION";
- }
- if (mainWin.isLetterboxedForDisplayCutout()) {
- return "DISPLAY_CUTOUT";
- }
- return "UNKNOWN_REASON";
+ mLetterboxUiController.dump(pw, prefix);
}
void setAppTimeTracker(AppTimeTracker att) {
@@ -1349,7 +1295,7 @@
if (getDisplayContent() != null) {
getDisplayContent().mClosingApps.remove(this);
}
- } else if (mLastParent != null && mLastParent.getRootTask() != null) {
+ } else if (oldTask != null && oldTask.getRootTask() != null) {
task.getRootTask().mExitingActivities.remove(this);
}
final Task rootTask = getRootTask();
@@ -1362,7 +1308,10 @@
? rootTask.getAnimatingActivityRegistry()
: null;
- mLastParent = task;
+ if (task == mLastParentBeforePip) {
+ // Activity's reparented back from pip, clear the links once established
+ clearLastParentBeforePip();
+ }
updateColorTransform();
@@ -1381,6 +1330,26 @@
}
}
+ /**
+ * Sets {@link #mLastParentBeforePip} to the current parent Task, it's caller's job to ensure
+ * {@link #getTask()} is set before this is called.
+ */
+ void setLastParentBeforePip() {
+ mLastParentBeforePip = getTask();
+ mLastParentBeforePip.mChildPipActivity = this;
+ }
+
+ private void clearLastParentBeforePip() {
+ if (mLastParentBeforePip != null) {
+ mLastParentBeforePip.mChildPipActivity = null;
+ mLastParentBeforePip = null;
+ }
+ }
+
+ @Nullable Task getLastParentBeforePip() {
+ return mLastParentBeforePip;
+ }
+
private void updateColorTransform() {
if (mSurfaceControl != null && mLastAppSaturationInfo != null) {
getPendingTransaction().setColorTransform(mSurfaceControl,
@@ -1415,183 +1384,29 @@
}
}
- if (mLetterbox != null) {
- mLetterbox.onMovedToDisplay(mDisplayContent.getDisplayId());
- }
+ mLetterboxUiController.onMovedToDisplay(mDisplayContent.getDisplayId());
}
- // TODO(b/183754168): Move letterbox UI logic into a separate class.
void layoutLetterbox(WindowState winHint) {
- final WindowState w = findMainWindow();
- if (w == null || winHint != null && w != winHint) {
- return;
- }
- final boolean surfaceReady = w.isDrawn() // Regular case
- || w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface.
- final boolean needsLetterbox = surfaceReady && isLetterboxed(w);
- updateRoundedCorners(w);
- updateWallpaperForLetterbox(w);
- if (needsLetterbox) {
- if (mLetterbox == null) {
- mLetterbox = new Letterbox(() -> makeChildSurface(null),
- mWmService.mTransactionFactory,
- mWmService::isLetterboxActivityCornersRounded,
- this::getLetterboxBackgroundColor,
- this::hasWallpaperBackgroudForLetterbox,
- this::getLetterboxWallpaperBlurRadius,
- this::getLetterboxWallpaperDarkScrimAlpha);
- mLetterbox.attachInput(w);
- }
- getPosition(mTmpPoint);
- // Get the bounds of the "space-to-fill". The transformed bounds have the highest
- // priority because the activity is launched in a rotated environment. In multi-window
- // mode, the task-level represents this. In fullscreen-mode, the task container does
- // (since the orientation letterbox is also applied to the task).
- final Rect transformedBounds = getFixedRotationTransformDisplayBounds();
- final Rect spaceToFill = transformedBounds != null
- ? transformedBounds
- : inMultiWindowMode()
- ? getRootTask().getBounds()
- : getRootTask().getParent().getBounds();
- mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
- } else if (mLetterbox != null) {
- mLetterbox.hide();
- }
- }
-
- private Color getLetterboxBackgroundColor() {
- final WindowState w = findMainWindow();
- if (w == null || w.isLetterboxedForDisplayCutout()) {
- return Color.valueOf(Color.BLACK);
- }
- @LetterboxBackgroundType int letterboxBackgroundType =
- mWmService.getLetterboxBackgroundType();
- switch (letterboxBackgroundType) {
- case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
- if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
- return Color.valueOf(taskDescription.getBackgroundColorFloating());
- }
- break;
- case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
- if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
- return Color.valueOf(taskDescription.getBackgroundColor());
- }
- break;
- case LETTERBOX_BACKGROUND_WALLPAPER:
- if (hasWallpaperBackgroudForLetterbox()) {
- // Color is used for translucent scrim that dims wallpaper.
- return Color.valueOf(Color.BLACK);
- }
- Slog.w(TAG, "Wallpaper option is selected for letterbox background but "
- + "blur is not supported by a device or not supported in the current "
- + "window configuration or both alpha scrim and blur radius aren't "
- + "provided so using solid color background");
- break;
- case LETTERBOX_BACKGROUND_SOLID_COLOR:
- return mWmService.getLetterboxBackgroundColor();
- default:
- throw new AssertionError(
- "Unexpected letterbox background type: " + letterboxBackgroundType);
- }
- // If picked option configured incorrectly or not supported then default to a solid color
- // background.
- return mWmService.getLetterboxBackgroundColor();
- }
-
- /**
- * @return {@code true} when the main window is letterboxed, this activity isn't transparent
- * and doesn't show a wallpaper.
- */
- @VisibleForTesting
- boolean isLetterboxed(WindowState mainWindow) {
- return mainWindow.isLetterboxedAppWindow() && fillsParent()
- // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
- // WindowContainer#showWallpaper because the later will return true when this
- // activity is using blurred wallpaper for letterbox backgroud.
- && (mainWindow.mAttrs.flags & FLAG_SHOW_WALLPAPER) == 0;
- }
-
- private void updateRoundedCorners(WindowState mainWindow) {
- int cornersRadius =
- // Don't round corners if letterboxed only for display cutout.
- isLetterboxed(mainWindow) && !mainWindow.isLetterboxedForDisplayCutout()
- ? Math.max(0, mWmService.getLetterboxActivityCornersRadius()) : 0;
- setCornersRadius(mainWindow, cornersRadius);
- }
-
- private void setCornersRadius(WindowState mainWindow, int cornersRadius) {
- final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
- if (windowSurface != null && windowSurface.isValid()) {
- Transaction transaction = getSyncTransaction();
- transaction.setCornerRadius(windowSurface, cornersRadius);
- }
+ mLetterboxUiController.layoutLetterbox(winHint);
}
boolean hasWallpaperBackgroudForLetterbox() {
- return mShowWallpaperForLetterboxBackground;
- }
-
- private void updateWallpaperForLetterbox(WindowState mainWindow) {
- @LetterboxBackgroundType int letterboxBackgroundType =
- mWmService.getLetterboxBackgroundType();
- boolean wallpaperShouldBeShown =
- letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER
- && isLetterboxed(mainWindow)
- // Don't use wallpaper as a background if letterboxed for display cutout.
- && !mainWindow.isLetterboxedForDisplayCutout()
- // Check that dark scrim alpha or blur radius are provided
- && (getLetterboxWallpaperBlurRadius() > 0
- || getLetterboxWallpaperDarkScrimAlpha() > 0)
- // Check that blur is supported by a device if blur radius is provided.
- && (getLetterboxWallpaperBlurRadius() <= 0
- || isLetterboxWallpaperBlurSupported());
- if (mShowWallpaperForLetterboxBackground != wallpaperShouldBeShown) {
- mShowWallpaperForLetterboxBackground = wallpaperShouldBeShown;
- requestUpdateWallpaperIfNeeded();
- }
- }
-
- private int getLetterboxWallpaperBlurRadius() {
- int blurRadius = mWmService.getLetterboxBackgroundWallpaperBlurRadius();
- return blurRadius < 0 ? 0 : blurRadius;
- }
-
- private float getLetterboxWallpaperDarkScrimAlpha() {
- float alpha = mWmService.getLetterboxBackgroundWallpaperDarkScrimAlpha();
- // No scrim by default.
- return (alpha < 0 || alpha >= 1) ? 0.0f : alpha;
- }
-
- private boolean isLetterboxWallpaperBlurSupported() {
- return mWmService.mContext.getSystemService(WindowManager.class).isCrossWindowBlurEnabled();
+ return mLetterboxUiController.hasWallpaperBackgroudForLetterbox();
}
void updateLetterboxSurface(WindowState winHint) {
- final WindowState w = findMainWindow();
- if (w != winHint && winHint != null && w != null) {
- return;
- }
- layoutLetterbox(winHint);
- if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
- mLetterbox.applySurfaceChanges(getSyncTransaction());
- }
+ mLetterboxUiController.updateLetterboxSurface(winHint);
}
+ /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
Rect getLetterboxInsets() {
- if (mLetterbox != null) {
- return mLetterbox.getInsets();
- } else {
- return new Rect();
- }
+ return mLetterboxUiController.getLetterboxInsets();
}
/** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
void getLetterboxInnerBounds(Rect outBounds) {
- if (mLetterbox != null) {
- outBounds.set(mLetterbox.getInnerFrame());
- } else {
- outBounds.setEmpty();
- }
+ mLetterboxUiController.getLetterboxInnerBounds(outBounds);
}
/**
@@ -1599,7 +1414,7 @@
* when the current activity is displayed.
*/
boolean isFullyTransparentBarAllowed(Rect rect) {
- return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
+ return mLetterboxUiController.isFullyTransparentBarAllowed(rect);
}
/**
@@ -1607,7 +1422,7 @@
* the given {@code rect}.
*/
boolean isLetterboxOverlappingWith(Rect rect) {
- return mLetterbox != null && mLetterbox.isOverlappingWith(rect);
+ return mLetterboxUiController.isLetterboxOverlappingWith(rect);
}
static class Token extends IApplicationToken.Stub {
@@ -1856,6 +1671,9 @@
mPersistentState = persistentState;
taskDescription = _taskDescription;
+
+ mLetterboxUiController = new LetterboxUiController(mWmService, this);
+
if (_createTime > 0) {
createTime = _createTime;
}
@@ -2108,12 +1926,16 @@
}
void scheduleAddStartingWindow() {
- // Note: we really want to do sendMessageAtFrontOfQueue() because we
- // want to process the message ASAP, before any other queued
- // messages.
- if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
- ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Enqueueing ADD_STARTING");
- mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
+ if (StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
+ mAddStartingWindow.run();
+ } else {
+ // Note: we really want to do sendMessageAtFrontOfQueue() because we
+ // want to process the message ASAP, before any other queued
+ // messages.
+ if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Enqueueing ADD_STARTING");
+ mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
+ }
}
}
@@ -2125,7 +1947,9 @@
final StartingData startingData;
synchronized (mWmService.mGlobalLock) {
// There can only be one adding request, silly caller!
- mWmService.mAnimationHandler.removeCallbacks(this);
+ if (!StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
+ mWmService.mAnimationHandler.removeCallbacks(this);
+ }
if (mStartingData == null) {
// Animation has been canceled... do nothing.
@@ -2140,7 +1964,6 @@
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Add starting %s: startingData=%s",
this, startingData);
-
WindowManagerPolicy.StartingSurface surface = null;
try {
surface = startingData.createStartingSurface(ActivityRecord.this);
@@ -2375,17 +2198,22 @@
+ " startingView=%s Callers=%s", this, mStartingWindow, mStartingSurface,
Debug.getCallers(5));
-
- // Use the same thread to remove the window as we used to add it, as otherwise we end up
- // with things in the view hierarchy being called from different threads.
- mWmService.mAnimationHandler.post(() -> {
+ final Runnable removeSurface = () -> {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Removing startingView=%s", surface);
try {
surface.remove(prepareAnimation);
} catch (Exception e) {
Slog.w(TAG_WM, "Exception when removing starting window", e);
}
- });
+ };
+
+ if (StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
+ removeSurface.run();
+ } else {
+ // Use the same thread to remove the window as we used to add it, as otherwise we end up
+ // with things in the view hierarchy being called from different threads.
+ mWmService.mAnimationHandler.post(removeSurface);
+ }
}
private void removeAppTokenFromDisplay() {
@@ -3434,6 +3262,7 @@
*/
void cleanUp(boolean cleanServices, boolean setState) {
task.cleanUpActivityReferences(this);
+ clearLastParentBeforePip();
deferRelaunchUntilPaused = false;
frozenBeforeDestroy = false;
@@ -3687,10 +3516,8 @@
dc.setFocusedApp(null);
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
}
- if (mLetterbox != null) {
- mLetterbox.destroy();
- mLetterbox = null;
- }
+
+ mLetterboxUiController.destroy();
if (!delayed) {
updateReportedVisibilityLocked();
@@ -5863,6 +5690,12 @@
nowVisible = true;
lastVisibleTime = SystemClock.uptimeMillis();
mAtmService.scheduleAppGcsLocked();
+ // The nowVisible may be false in onAnimationFinished because the transition animation
+ // was started by starting window but the main window hasn't drawn so the procedure
+ // didn't schedule. Hence also check when nowVisible becomes true (drawn) to avoid the
+ // closing activity having to wait until idle timeout to be stopped or destroyed if the
+ // next activity won't report idle (e.g. repeated view animation).
+ mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
}
}
@@ -6679,26 +6512,26 @@
// traverse the copy.
final ArrayList<WindowState> children = new ArrayList<>(mChildren);
children.forEach(WindowState::onExitAnimationDone);
+ // The starting window could transfer to another activity after app transition started, in
+ // that case the latest top activity might not receive exit animation done callback if the
+ // starting window didn't applied exit animation success. Notify animation finish to the
+ // starting window if needed.
+ if (task != null && startingMoved) {
+ final WindowState transferredStarting = task.getWindow(w ->
+ w.mAttrs.type == TYPE_APPLICATION_STARTING);
+ if (transferredStarting != null && transferredStarting.mAnimatingExit
+ && !transferredStarting.isSelfAnimating(0 /* flags */,
+ ANIMATION_TYPE_WINDOW_ANIMATION)) {
+ transferredStarting.onExitAnimationDone();
+ }
+ }
getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token);
scheduleAnimation();
- if (!mTaskSupervisor.mStoppingActivities.isEmpty()
- || !mTaskSupervisor.mFinishingActivities.isEmpty()) {
- if (mRootWindowContainer.allResumedActivitiesIdle()) {
- // If all activities are already idle then we now need to make sure we perform
- // the full stop of this activity. This is because we won't do that while they
- // are still waiting for the animation to finish.
- mTaskSupervisor.scheduleIdle();
- } else if (mRootWindowContainer.allResumedActivitiesVisible()) {
- // If all resumed activities are already visible (and should be drawn, see
- // updateReportedVisibility ~ nowVisible) but not idle, we still schedule to
- // process the stopping and finishing activities because the transition is done.
- // This also avoids if the next activity never reports idle (e.g. animating view),
- // the previous will need to wait until idle timeout to be stopped or destroyed.
- mTaskSupervisor.scheduleProcessStoppingAndFinishingActivities();
- }
- }
+ // Schedule to handle the stopping and finishing activities which the animation is done
+ // because the activities which were animating have not been stopped yet.
+ mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -7022,11 +6855,13 @@
// and back which can cause visible issues (see b/184078928).
final int parentWindowingMode =
newParentConfiguration.windowConfiguration.getWindowingMode();
+ final boolean isFixedOrientationLetterboxAllowed =
+ isSplitScreenWindowingMode(parentWindowingMode)
+ || parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
+ || parentWindowingMode == WINDOWING_MODE_FULLSCREEN;
// TODO(b/181207944): Consider removing the if condition and always run
// resolveFixedOrientationConfiguration() since this should be applied for all cases.
- if (isSplitScreenWindowingMode(parentWindowingMode)
- || parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
- || parentWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ if (isFixedOrientationLetterboxAllowed) {
resolveFixedOrientationConfiguration(newParentConfiguration);
}
@@ -7047,12 +6882,16 @@
resolveFullscreenConfiguration(newParentConfiguration);
}
+ if (isFixedOrientationLetterboxAllowed || mCompatDisplayInsets != null
+ // In fullscreen, can be letterboxed for aspect ratio.
+ || !inMultiWindowMode()) {
+ updateResolvedBoundsHorizontalPosition(newParentConfiguration);
+ }
+
if (mVisibleRequested) {
updateCompatDisplayInsets();
}
- // TODO(b/175212232): Consolidate position logic from each "resolve" method above here.
-
// Assign configuration sequence number into hierarchy because there is a different way than
// ensureActivityConfiguration() in this class that uses configuration in WindowState during
// layout traversals.
@@ -7083,6 +6922,48 @@
}
}
+
+ /**
+ * Adjusts horizontal position of resolved bounds if they doesn't fill the parent using gravity
+ * requested in the config or via an ADB command. For more context see {@link
+ * WindowManagerService#getLetterboxHorizontalPositionMultiplier}.
+ */
+ private void updateResolvedBoundsHorizontalPosition(Configuration newParentConfiguration) {
+ final Configuration resolvedConfig = getResolvedOverrideConfiguration();
+ final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
+ final Rect screenResolvedBounds =
+ mSizeCompatBounds != null ? mSizeCompatBounds : resolvedBounds;
+ final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
+ final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
+ if (resolvedBounds.isEmpty() || parentBounds.width() == screenResolvedBounds.width()) {
+ return;
+ }
+
+ int offsetX = 0;
+ if (screenResolvedBounds.width() >= parentAppBounds.width()) {
+ // If resolved bounds overlap with insets, center within app bounds.
+ offsetX = getHorizontalCenterOffset(
+ parentAppBounds.width(), screenResolvedBounds.width());
+ } else {
+ float positionMultiplier =
+ mWmService.mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier();
+ positionMultiplier =
+ (positionMultiplier < 0.0f || positionMultiplier > 1.0f)
+ // Default to central position if invalid value is provided.
+ ? 0.5f : positionMultiplier;
+ offsetX = (int) Math.ceil((parentAppBounds.width() - screenResolvedBounds.width())
+ * positionMultiplier);
+ }
+
+ if (mSizeCompatBounds != null) {
+ mSizeCompatBounds.offset(offsetX, 0 /* offsetY */);
+ final int dx = mSizeCompatBounds.left - resolvedBounds.left;
+ offsetBounds(resolvedConfig, dx, 0 /* offsetY */);
+ } else {
+ offsetBounds(resolvedConfig, offsetX, 0 /* offsetY */);
+ }
+ }
+
/**
* Whether this activity is letterboxed for fixed orientation. If letterboxed due to fixed
* orientation then aspect ratio restrictions are also already respected.
@@ -7138,7 +7019,7 @@
// Override from config_fixedOrientationLetterboxAspectRatio or via ADB with
// set-fixed-orientation-letterbox-aspect-ratio.
final float letterboxAspectRatioOverride =
- mWmService.getFixedOrientationLetterboxAspectRatio();
+ mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
aspect = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
? letterboxAspectRatioOverride : aspect;
@@ -7160,7 +7041,10 @@
resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + height);
} else {
final int width = (int) Math.rint(parentHeight / aspect);
- final int left = parentBounds.centerX() - width / 2;
+ final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
+ final int left = width <= parentAppBounds.width()
+ // Avoid overlapping with the horizontal decor area when possible.
+ ? parentAppBounds.left : parentBounds.centerX() - width / 2;
resolvedBounds.set(left, parentBounds.top, left + width, parentBounds.bottom);
}
@@ -7212,12 +7096,6 @@
task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
getFixedRotationTransformDisplayInfo());
}
- if (needToBeCentered) {
- // Offset to center relative to parent's app bounds.
- final int offsetX = getHorizontalCenterOffset(
- parentAppBounds.width(), resolvedBounds.width());
- offsetBounds(resolvedConfig, offsetX, 0 /* offsetY */);
- }
}
/**
@@ -7315,8 +7193,8 @@
final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
- // Calculates the scale and offset to horizontal center the size compatibility bounds into
- // the region which is available to application.
+ // Calculates the scale the size compatibility bounds into the region which is available
+ // to application.
final int contentW = resolvedAppBounds.width();
final int contentH = resolvedAppBounds.height();
final int viewportW = containerAppBounds.width();
@@ -7324,8 +7202,9 @@
// Only allow to scale down.
mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH)
? 1f : Math.min((float) viewportW / contentW, (float) viewportH / contentH);
- final int screenTopInset = containerAppBounds.top - containerBounds.top;
- final boolean topNotAligned = screenTopInset != resolvedAppBounds.top - resolvedBounds.top;
+ final int containerTopInset = containerAppBounds.top - containerBounds.top;
+ final boolean topNotAligned =
+ containerTopInset != resolvedAppBounds.top - resolvedBounds.top;
if (mSizeCompatScale != 1f || topNotAligned) {
if (mSizeCompatBounds == null) {
mSizeCompatBounds = new Rect();
@@ -7334,18 +7213,15 @@
mSizeCompatBounds.offsetTo(0, 0);
mSizeCompatBounds.scale(mSizeCompatScale);
// The insets are included in height, e.g. the area of real cutout shouldn't be scaled.
- mSizeCompatBounds.bottom += screenTopInset;
+ mSizeCompatBounds.bottom += containerTopInset;
} else {
mSizeCompatBounds = null;
}
- // Center horizontally in parent (app bounds) and align to top of parent (bounds)
- // - this is a UX choice.
- final int offsetX = getHorizontalCenterOffset(
- (int) viewportW, (int) (contentW * mSizeCompatScale));
+ // Align to top of parent (bounds) - this is a UX choice and exclude the horizontal decor
+ // if needed. Horizontal position is adjusted in updateResolvedBoundsHorizontalPosition.
// Above coordinates are in "@" space, now place "*" and "#" to screen space.
- final int screenPosX = (fillContainer
- ? containerBounds.left : containerAppBounds.left) + offsetX;
+ final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
final int screenPosY = containerBounds.top;
if (screenPosX != 0 || screenPosY != 0) {
if (mSizeCompatBounds != null) {
@@ -7661,7 +7537,7 @@
/**
* Returns the aspect ratio of the given {@code rect}.
*/
- private static float computeAspectRatio(Rect rect) {
+ static float computeAspectRatio(Rect rect) {
final int width = rect.width();
final int height = rect.height();
if (width == 0 || height == 0) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 75a188e..1158a9c 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2721,8 +2721,22 @@
launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
break;
case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
- launchFlags &=
- ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
+ if (mLaunchMode == LAUNCH_SINGLE_INSTANCE_PER_TASK) {
+ // Remove MULTIPLE_TASK flag along with NEW_DOCUMENT only if NEW_DOCUMENT
+ // is set, otherwise we still want to keep the MULTIPLE_TASK flag (if
+ // any) for singleInstancePerTask that the multiple tasks can be created,
+ // or a singleInstancePerTask activity is basically the same as a
+ // singleTask activity when documentLaunchMode set to never.
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0) {
+ launchFlags &= ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+ | FLAG_ACTIVITY_MULTIPLE_TASK);
+ }
+ } else {
+ // TODO(b/184903976): Should FLAG_ACTIVITY_MULTIPLE_TASK always be
+ // removed for document-never activity?
+ launchFlags &=
+ ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
+ }
break;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a0beee4..b83945e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4514,11 +4514,9 @@
.setContentTitle(text)
.setContentText(
mContext.getText(R.string.heavy_weight_notification_detail))
- // TODO(b/175194709) Please replace FLAG_MUTABLE_UNAUDITED below
- // with either FLAG_IMMUTABLE (recommended) or FLAG_MUTABLE.
.setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT
- | PendingIntent.FLAG_MUTABLE_UNAUDITED, null,
+ | PendingIntent.FLAG_IMMUTABLE, null,
new UserHandle(userId)))
.build();
try {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index bdde369..2b1cf39 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2027,7 +2027,9 @@
}
final void scheduleIdle() {
- mHandler.sendEmptyMessage(IDLE_NOW_MSG);
+ if (!mHandler.hasMessages(IDLE_NOW_MSG)) {
+ mHandler.sendEmptyMessage(IDLE_NOW_MSG);
+ }
}
/**
@@ -2115,8 +2117,10 @@
}
}
- void scheduleProcessStoppingAndFinishingActivities() {
- if (!mHandler.hasMessages(PROCESS_STOPPING_AND_FINISHING_MSG)) {
+ void scheduleProcessStoppingAndFinishingActivitiesIfNeeded() {
+ if ((!mStoppingActivities.isEmpty() || !mFinishingActivities.isEmpty())
+ && !mHandler.hasMessages(PROCESS_STOPPING_AND_FINISHING_MSG)
+ && mRootWindowContainer.allResumedActivitiesVisible()) {
mHandler.sendEmptyMessage(PROCESS_STOPPING_AND_FINISHING_MSG);
}
}
diff --git a/services/core/java/com/android/server/wm/BlurController.java b/services/core/java/com/android/server/wm/BlurController.java
index 128d452..d920267 100644
--- a/services/core/java/com/android/server/wm/BlurController.java
+++ b/services/core/java/com/android/server/wm/BlurController.java
@@ -18,24 +18,52 @@
import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.view.ICrossWindowBlurEnabledListener;
import com.android.internal.annotations.GuardedBy;
+/**
+ * Keeps track of the different factors that determine whether cross-window blur is enabled
+ * or disabled. Also keeps a list of all interested listeners and notifies them when the
+ * blur enabled state changes.
+ */
final class BlurController {
-
+ private final PowerManager mPowerManager;
private final RemoteCallbackList<ICrossWindowBlurEnabledListener>
mBlurEnabledListeners = new RemoteCallbackList<>();
+ // We don't use the WM global lock, because the BlurController is not involved in window
+ // drawing and only receives binder calls that don't need synchronization with the rest of WM
private final Object mLock = new Object();
@GuardedBy("mLock")
boolean mBlurEnabled;
@GuardedBy("mLock")
boolean mBlurForceDisabled;
+ @GuardedBy("mLock")
+ boolean mInBatterySaverMode;
- BlurController() {
- mBlurEnabled = CROSS_WINDOW_BLUR_SUPPORTED;
+ BlurController(Context context, PowerManager powerManager) {
+ mPowerManager = powerManager;
+ mInBatterySaverMode = mPowerManager.isPowerSaveMode();
+ updateBlurEnabledLocked();
+
+ IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
+ filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+ context.registerReceiverForAllUsers(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) {
+ setBatterySaverEnabled(mPowerManager.isPowerSaveMode());
+ }
+ }
+ }, filter, null, null);
}
boolean registerCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener) {
@@ -59,8 +87,16 @@
}
+ void setBatterySaverEnabled(boolean enabled) {
+ synchronized (mLock) {
+ mInBatterySaverMode = enabled;
+ updateBlurEnabledLocked();
+ }
+ }
+
private void updateBlurEnabledLocked() {
- final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED && !mBlurForceDisabled;
+ final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED && !mBlurForceDisabled
+ && !mInBatterySaverMode;
if (mBlurEnabled == newEnabled) {
return;
}
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index 5a8af45..af0c3e3 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -16,8 +16,8 @@
package com.android.server.wm;
-import static android.service.displayhash.DisplayHasherService.EXTRA_VERIFIED_DISPLAY_HASH;
-import static android.service.displayhash.DisplayHasherService.SERVICE_META_DATA;
+import static android.service.displayhash.DisplayHashingService.EXTRA_VERIFIED_DISPLAY_HASH;
+import static android.service.displayhash.DisplayHashingService.SERVICE_META_DATA;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
@@ -52,8 +52,8 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.displayhash.DisplayHashParams;
-import android.service.displayhash.DisplayHasherService;
-import android.service.displayhash.IDisplayHasherService;
+import android.service.displayhash.DisplayHashingService;
+import android.service.displayhash.IDisplayHashingService;
import android.util.AttributeSet;
import android.util.Size;
import android.util.Slog;
@@ -79,7 +79,7 @@
import java.util.function.BiConsumer;
/**
- * Handles requests into {@link android.service.displayhash.DisplayHasherService}
+ * Handles requests into {@link DisplayHashingService}
*
* Do not hold the {@link WindowManagerService#mGlobalLock} when calling methods since they are
* blocking calls into another service.
@@ -91,7 +91,7 @@
private final Object mServiceConnectionLock = new Object();
@GuardedBy("mServiceConnectionLock")
- private DisplayHasherServiceConnection mServiceConnection;
+ private DisplayHashingServiceConnection mServiceConnection;
private final Context mContext;
@@ -150,7 +150,7 @@
private boolean mDisplayHashThrottlingEnabled = true;
private interface Command {
- void run(IDisplayHasherService service) throws RemoteException;
+ void run(IDisplayHashingService service) throws RemoteException;
}
DisplayHashController(Context context) {
@@ -233,7 +233,7 @@
(float) size.getHeight() / boundsInWindow.height());
}
- args.setGrayscale(displayHashParams.isGrayscaleBuffer());
+ args.setGrayscale(displayHashParams.isUseGrayscale());
SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
SurfaceControl.captureLayers(args.build());
@@ -405,9 +405,9 @@
}
}
- TypedArray sa = res.obtainAttributes(attrs, R.styleable.DisplayHasherService);
+ TypedArray sa = res.obtainAttributes(attrs, R.styleable.DisplayHashingService);
mThrottleDurationMillis = sa.getInt(
- R.styleable.DisplayHasherService_throttleDurationMillis, 0);
+ R.styleable.DisplayHashingService_throttleDurationMillis, 0);
sa.recycle();
mParsedXml = true;
return true;
@@ -424,7 +424,7 @@
if (DEBUG) Slog.v(TAG, "creating connection");
// Create the connection
- mServiceConnection = new DisplayHasherServiceConnection();
+ mServiceConnection = new DisplayHashingServiceConnection();
final ComponentName component = getServiceComponentName();
if (DEBUG) Slog.v(TAG, "binding to: " + component);
@@ -455,7 +455,7 @@
return null;
}
- final Intent intent = new Intent(DisplayHasherService.SERVICE_INTERFACE);
+ final Intent intent = new Intent(DisplayHashingService.SERVICE_INTERFACE);
intent.setPackage(packageName);
final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
@@ -472,10 +472,10 @@
if (serviceInfo == null) return null;
final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
- if (!Manifest.permission.BIND_DISPLAY_HASHER_SERVICE
+ if (!Manifest.permission.BIND_DISPLAY_HASHING_SERVICE
.equals(serviceInfo.permission)) {
Slog.w(TAG, name.flattenToShortString() + " requires permission "
- + Manifest.permission.BIND_DISPLAY_HASHER_SERVICE);
+ + Manifest.permission.BIND_DISPLAY_HASHING_SERVICE);
return null;
}
@@ -488,7 +488,7 @@
private Bundle mResult;
private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
- public Bundle run(BiConsumer<IDisplayHasherService, RemoteCallback> func) {
+ public Bundle run(BiConsumer<IDisplayHashingService, RemoteCallback> func) {
connectAndRun(service -> {
RemoteCallback callback = new RemoteCallback(result -> {
mResult = result;
@@ -507,9 +507,9 @@
}
}
- private class DisplayHasherServiceConnection implements ServiceConnection {
+ private class DisplayHashingServiceConnection implements ServiceConnection {
@GuardedBy("mServiceConnectionLock")
- private IDisplayHasherService mRemoteService;
+ private IDisplayHashingService mRemoteService;
@GuardedBy("mServiceConnectionLock")
private ArrayList<Command> mQueuedCommands;
@@ -518,7 +518,7 @@
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) Slog.v(TAG, "onServiceConnected(): " + name);
synchronized (mServiceConnectionLock) {
- mRemoteService = IDisplayHasherService.Stub.asInterface(service);
+ mRemoteService = IDisplayHashingService.Stub.asInterface(service);
if (mQueuedCommands != null) {
final int size = mQueuedCommands.size();
if (DEBUG) Slog.d(TAG, "running " + size + " queued commands");
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
new file mode 100644
index 0000000..eb7087c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -0,0 +1,328 @@
+/*
+ * 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.wm;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.graphics.Color;
+
+import com.android.internal.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Reads letterbox configs from resources and controls their overrides at runtime. */
+final class LetterboxConfiguration {
+
+ /**
+ * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
+ * set-fixed-orientation-letterbox-aspect-ratio or via {@link
+ * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored
+ * if it is <= this value.
+ */
+ static final float MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.0f;
+
+ /** Enum for Letterbox background type. */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({LETTERBOX_BACKGROUND_SOLID_COLOR, LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND,
+ LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING, LETTERBOX_BACKGROUND_WALLPAPER})
+ @interface LetterboxBackgroundType {};
+ /** Solid background using color specified in R.color.config_letterboxBackgroundColor. */
+ static final int LETTERBOX_BACKGROUND_SOLID_COLOR = 0;
+
+ /** Color specified in R.attr.colorBackground for the letterboxed application. */
+ static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND = 1;
+
+ /** Color specified in R.attr.colorBackgroundFloating for the letterboxed application. */
+ static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING = 2;
+
+ /** Using wallpaper as a background which can be blurred or dimmed with dark scrim. */
+ static final int LETTERBOX_BACKGROUND_WALLPAPER = 3;
+
+ final Context mContext;
+
+ // Aspect ratio of letterbox for fixed orientation, values <=
+ // MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored.
+ private float mFixedOrientationLetterboxAspectRatio;
+
+ // Corners radius for activities presented in the letterbox mode, values < 0 will be ignored.
+ private int mLetterboxActivityCornersRadius;
+
+ // Color for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type.
+ private Color mLetterboxBackgroundColor;
+
+ @LetterboxBackgroundType
+ private int mLetterboxBackgroundType;
+
+ // Blur radius for LETTERBOX_BACKGROUND_WALLPAPER option in mLetterboxBackgroundType.
+ // Values <= 0 are ignored and 0 is used instead.
+ private int mLetterboxBackgroundWallpaperBlurRadius;
+
+ // Alpha of a black scrim shown over wallpaper letterbox background when
+ // LETTERBOX_BACKGROUND_WALLPAPER option is selected for mLetterboxBackgroundType.
+ // Values < 0 or >= 1 are ignored and 0.0 (transparent) is used instead.
+ private float mLetterboxBackgroundWallpaperDarkScrimAlpha;
+
+ // Horizontal position of a center of the letterboxed app window. 0 corresponds to the left
+ // side of the screen and 1.0 to the right side.
+ private float mLetterboxHorizontalPositionMultiplier;
+
+ LetterboxConfiguration(Context context) {
+ mContext = context;
+ mFixedOrientationLetterboxAspectRatio = context.getResources().getFloat(
+ R.dimen.config_fixedOrientationLetterboxAspectRatio);
+ mLetterboxActivityCornersRadius = context.getResources().getInteger(
+ R.integer.config_letterboxActivityCornersRadius);
+ mLetterboxBackgroundColor = Color.valueOf(context.getResources().getColor(
+ R.color.config_letterboxBackgroundColor));
+ mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(context);
+ mLetterboxBackgroundWallpaperBlurRadius = context.getResources().getDimensionPixelSize(
+ R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
+ mLetterboxBackgroundWallpaperDarkScrimAlpha = context.getResources().getFloat(
+ R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
+ mLetterboxHorizontalPositionMultiplier = context.getResources().getFloat(
+ R.dimen.config_letterboxHorizontalPositionMultiplier);
+ }
+
+ /**
+ * Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link
+ * #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
+ * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
+ * the framework implementation will be used to determine the aspect ratio.
+ */
+ void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
+ mFixedOrientationLetterboxAspectRatio = aspectRatio;
+ }
+
+ /**
+ * Resets the aspect ratio of letterbox for fixed orientation to {@link
+ * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}.
+ */
+ void resetFixedOrientationLetterboxAspectRatio() {
+ mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
+ }
+
+ /**
+ * Gets the aspect ratio of letterbox for fixed orientation.
+ */
+ float getFixedOrientationLetterboxAspectRatio() {
+ return mFixedOrientationLetterboxAspectRatio;
+ }
+
+ /**
+ * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
+ * both it and a value of {@link
+ * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
+ * and corners of the activity won't be rounded.
+ */
+ void setLetterboxActivityCornersRadius(int cornersRadius) {
+ mLetterboxActivityCornersRadius = cornersRadius;
+ }
+
+ /**
+ * Resets corners raidus for activities presented in the letterbox mode to {@link
+ * com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
+ */
+ void resetLetterboxActivityCornersRadius() {
+ mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_letterboxActivityCornersRadius);
+ }
+
+ /**
+ * Whether corners of letterboxed activities are rounded.
+ */
+ boolean isLetterboxActivityCornersRounded() {
+ return getLetterboxActivityCornersRadius() > 0;
+ }
+
+ /**
+ * Gets corners raidus for activities presented in the letterbox mode.
+ */
+ int getLetterboxActivityCornersRadius() {
+ return mLetterboxActivityCornersRadius;
+ }
+
+ /**
+ * Gets color of letterbox background which is used when {@link
+ * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+ * fallback for other backfround types.
+ */
+ Color getLetterboxBackgroundColor() {
+ return mLetterboxBackgroundColor;
+ }
+
+
+ /**
+ * Sets color of letterbox background which is used when {@link
+ * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+ * fallback for other backfround types.
+ */
+ void setLetterboxBackgroundColor(Color color) {
+ mLetterboxBackgroundColor = color;
+ }
+
+ /**
+ * Resets color of letterbox background to {@link
+ * com.android.internal.R.color.config_letterboxBackgroundColor}.
+ */
+ void resetLetterboxBackgroundColor() {
+ mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
+ com.android.internal.R.color.config_letterboxBackgroundColor));
+ }
+
+ /**
+ * Gets {@link LetterboxBackgroundType} specified in {@link
+ * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
+ */
+ @LetterboxBackgroundType
+ int getLetterboxBackgroundType() {
+ return mLetterboxBackgroundType;
+ }
+
+ /** Sets letterbox background type. */
+ void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
+ mLetterboxBackgroundType = backgroundType;
+ }
+
+ /**
+ * Resets cletterbox background type to {@link
+ * com.android.internal.R.integer.config_letterboxBackgroundType}.
+ */
+ void resetLetterboxBackgroundType() {
+ mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
+ }
+
+ /** Returns a string representing the given {@link LetterboxBackgroundType}. */
+ static String letterboxBackgroundTypeToString(
+ @LetterboxBackgroundType int backgroundType) {
+ switch (backgroundType) {
+ case LETTERBOX_BACKGROUND_SOLID_COLOR:
+ return "LETTERBOX_BACKGROUND_SOLID_COLOR";
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+ return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND";
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+ return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING";
+ case LETTERBOX_BACKGROUND_WALLPAPER:
+ return "LETTERBOX_BACKGROUND_WALLPAPER";
+ default:
+ return "unknown=" + backgroundType;
+ }
+ }
+
+ @LetterboxBackgroundType
+ private static int readLetterboxBackgroundTypeFromConfig(Context context) {
+ int backgroundType = context.getResources().getInteger(
+ com.android.internal.R.integer.config_letterboxBackgroundType);
+ return backgroundType == LETTERBOX_BACKGROUND_SOLID_COLOR
+ || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND
+ || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING
+ || backgroundType == LETTERBOX_BACKGROUND_WALLPAPER
+ ? backgroundType : LETTERBOX_BACKGROUND_SOLID_COLOR;
+ }
+
+ /**
+ * Overrides alpha of a black scrim shown over wallpaper for {@link
+ * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}.
+ *
+ * <p>If given value is < 0 or >= 1, both it and a value of {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored
+ * and 0.0 (transparent) is instead.
+ */
+ void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) {
+ mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha;
+ }
+
+ /**
+ * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}.
+ */
+ void resetLetterboxBackgroundWallpaperDarkScrimAlpha() {
+ mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
+ }
+
+ /**
+ * Gets alpha of a black scrim shown over wallpaper letterbox background.
+ */
+ float getLetterboxBackgroundWallpaperDarkScrimAlpha() {
+ return mLetterboxBackgroundWallpaperDarkScrimAlpha;
+ }
+
+ /**
+ * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in
+ * {@link mLetterboxBackgroundType}.
+ *
+ * <p> If given value <= 0, both it and a value of {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored
+ * and 0 is used instead.
+ */
+ void setLetterboxBackgroundWallpaperBlurRadius(int radius) {
+ mLetterboxBackgroundWallpaperBlurRadius = radius;
+ }
+
+ /**
+ * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
+ * mLetterboxBackgroundType} to {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}.
+ */
+ void resetLetterboxBackgroundWallpaperBlurRadius() {
+ mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
+ }
+
+ /**
+ * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
+ * mLetterboxBackgroundType}.
+ */
+ int getLetterboxBackgroundWallpaperBlurRadius() {
+ return mLetterboxBackgroundWallpaperBlurRadius;
+ }
+
+ /*
+ * Gets horizontal position of a center of the letterboxed app window specified
+ * in {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}
+ * or via an ADB command. 0 corresponds to the left side of the screen and 1 to the
+ * right side.
+ *
+ * <p>This value can be outside of [0, 1] range so clients need to check and default to the
+ * central position (0.5).
+ */
+ float getLetterboxHorizontalPositionMultiplier() {
+ return mLetterboxHorizontalPositionMultiplier;
+ }
+
+ /**
+ * Overrides horizontal position of a center of the letterboxed app window. If given value < 0
+ * or > 1, then it and a value of {@link
+ * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and
+ * central position (0.5) is used.
+ */
+ void setLetterboxHorizontalPositionMultiplier(float multiplier) {
+ mLetterboxHorizontalPositionMultiplier = multiplier;
+ }
+
+ /**
+ * Resets horizontal position of a center of the letterboxed app window to {@link
+ * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}.
+ */
+ void resetLetterboxHorizontalPositionMultiplier() {
+ mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier);
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
new file mode 100644
index 0000000..130f680
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -0,0 +1,336 @@
+/*
+ * 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.wm;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+import static com.android.server.wm.LetterboxConfiguration.letterboxBackgroundTypeToString;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager.TaskDescription;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
+
+import java.io.PrintWriter;
+
+/** Controls behaviour of the letterbox UI for {@link mActivityRecord}. */
+// TODO(b/185262487): Improve test coverage of this class. Parts of it are tested in
+// SizeCompatTests and LetterboxTests but not all.
+// TODO(b/185264020): Consider making LetterboxUiController applicable to any level of the
+// hierarchy in addition to ActivityRecord (Task, DisplayArea, ...).
+final class LetterboxUiController {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
+
+ private final Point mTmpPoint = new Point();
+
+ private final LetterboxConfiguration mLetterboxConfiguration;
+ private final ActivityRecord mActivityRecord;
+
+ private boolean mShowWallpaperForLetterboxBackground;
+
+ @Nullable
+ private Letterbox mLetterbox;
+
+ LetterboxUiController(WindowManagerService wmService, ActivityRecord activityRecord) {
+ mLetterboxConfiguration = wmService.mLetterboxConfiguration;
+ // Given activityRecord may not be fully constructed since LetterboxUiController
+ // is created in its constructor. It shouldn't be used in this constructor but it's safe
+ // to use it after since controller is only used in ActivityRecord.
+ mActivityRecord = activityRecord;
+ }
+
+ /** Cleans up {@link Letterbox} if it exists.*/
+ void destroy() {
+ if (mLetterbox != null) {
+ mLetterbox.destroy();
+ mLetterbox = null;
+ }
+ }
+
+ void onMovedToDisplay(int displayId) {
+ if (mLetterbox != null) {
+ mLetterbox.onMovedToDisplay(displayId);
+ }
+ }
+
+ boolean hasWallpaperBackgroudForLetterbox() {
+ return mShowWallpaperForLetterboxBackground;
+ }
+
+ /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
+ Rect getLetterboxInsets() {
+ if (mLetterbox != null) {
+ return mLetterbox.getInsets();
+ } else {
+ return new Rect();
+ }
+ }
+
+ /** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
+ void getLetterboxInnerBounds(Rect outBounds) {
+ if (mLetterbox != null) {
+ outBounds.set(mLetterbox.getInnerFrame());
+ } else {
+ outBounds.setEmpty();
+ }
+ }
+
+ /**
+ * @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
+ * when the current activity is displayed.
+ */
+ boolean isFullyTransparentBarAllowed(Rect rect) {
+ return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
+ }
+
+ /**
+ * @return {@code true} if there is a letterbox and any part of that letterbox overlaps with
+ * the given {@code rect}.
+ */
+ boolean isLetterboxOverlappingWith(Rect rect) {
+ return mLetterbox != null && mLetterbox.isOverlappingWith(rect);
+ }
+
+ void updateLetterboxSurface(WindowState winHint) {
+ final WindowState w = mActivityRecord.findMainWindow();
+ if (w != winHint && winHint != null && w != null) {
+ return;
+ }
+ layoutLetterbox(winHint);
+ if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
+ mLetterbox.applySurfaceChanges(mActivityRecord.getSyncTransaction());
+ }
+ }
+
+ void layoutLetterbox(WindowState winHint) {
+ final WindowState w = mActivityRecord.findMainWindow();
+ if (w == null || winHint != null && w != winHint) {
+ return;
+ }
+ final boolean surfaceReady = w.isDrawn() // Regular case
+ || w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface.
+ final boolean needsLetterbox = surfaceReady && isLetterboxed(w);
+ updateRoundedCorners(w);
+ updateWallpaperForLetterbox(w);
+ if (needsLetterbox) {
+ if (mLetterbox == null) {
+ mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null),
+ mActivityRecord.mWmService.mTransactionFactory,
+ mLetterboxConfiguration::isLetterboxActivityCornersRounded,
+ this::getLetterboxBackgroundColor,
+ this::hasWallpaperBackgroudForLetterbox,
+ this::getLetterboxWallpaperBlurRadius,
+ this::getLetterboxWallpaperDarkScrimAlpha);
+ mLetterbox.attachInput(w);
+ }
+ mActivityRecord.getPosition(mTmpPoint);
+ // Get the bounds of the "space-to-fill". The transformed bounds have the highest
+ // priority because the activity is launched in a rotated environment. In multi-window
+ // mode, the task-level represents this. In fullscreen-mode, the task container does
+ // (since the orientation letterbox is also applied to the task).
+ final Rect transformedBounds = mActivityRecord.getFixedRotationTransformDisplayBounds();
+ final Rect spaceToFill = transformedBounds != null
+ ? transformedBounds
+ : mActivityRecord.inMultiWindowMode()
+ ? mActivityRecord.getRootTask().getBounds()
+ : mActivityRecord.getRootTask().getParent().getBounds();
+ mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
+ } else if (mLetterbox != null) {
+ mLetterbox.hide();
+ }
+ }
+
+ /**
+ * @return {@code true} when the main window is letterboxed, this activity isn't transparent
+ * and doesn't show a wallpaper.
+ */
+ @VisibleForTesting
+ boolean isLetterboxed(WindowState mainWindow) {
+ return mainWindow.isLetterboxedAppWindow() && mActivityRecord.fillsParent()
+ // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
+ // WindowContainer#showWallpaper because the later will return true when this
+ // activity is using blurred wallpaper for letterbox backgroud.
+ && (mainWindow.mAttrs.flags & FLAG_SHOW_WALLPAPER) == 0;
+ }
+
+ private Color getLetterboxBackgroundColor() {
+ final WindowState w = mActivityRecord.findMainWindow();
+ if (w == null || w.isLetterboxedForDisplayCutout()) {
+ return Color.valueOf(Color.BLACK);
+ }
+ @LetterboxBackgroundType int letterboxBackgroundType =
+ mLetterboxConfiguration.getLetterboxBackgroundType();
+ TaskDescription taskDescription = mActivityRecord.taskDescription;
+ switch (letterboxBackgroundType) {
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+ if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
+ return Color.valueOf(taskDescription.getBackgroundColorFloating());
+ }
+ break;
+ case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+ if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
+ return Color.valueOf(taskDescription.getBackgroundColor());
+ }
+ break;
+ case LETTERBOX_BACKGROUND_WALLPAPER:
+ if (hasWallpaperBackgroudForLetterbox()) {
+ // Color is used for translucent scrim that dims wallpaper.
+ return Color.valueOf(Color.BLACK);
+ }
+ Slog.w(TAG, "Wallpaper option is selected for letterbox background but "
+ + "blur is not supported by a device or not supported in the current "
+ + "window configuration or both alpha scrim and blur radius aren't "
+ + "provided so using solid color background");
+ break;
+ case LETTERBOX_BACKGROUND_SOLID_COLOR:
+ return mLetterboxConfiguration.getLetterboxBackgroundColor();
+ default:
+ throw new AssertionError(
+ "Unexpected letterbox background type: " + letterboxBackgroundType);
+ }
+ // If picked option configured incorrectly or not supported then default to a solid color
+ // background.
+ return mLetterboxConfiguration.getLetterboxBackgroundColor();
+ }
+
+ private void updateRoundedCorners(WindowState mainWindow) {
+ int cornersRadius =
+ // Don't round corners if letterboxed only for display cutout.
+ isLetterboxed(mainWindow)
+ && !mainWindow.isLetterboxedForDisplayCutout()
+ ? Math.max(0, mLetterboxConfiguration.getLetterboxActivityCornersRadius())
+ : 0;
+ setCornersRadius(mainWindow, cornersRadius);
+ }
+
+ private void setCornersRadius(WindowState mainWindow, int cornersRadius) {
+ final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
+ if (windowSurface != null && windowSurface.isValid()) {
+ Transaction transaction = mActivityRecord.getSyncTransaction();
+ transaction.setCornerRadius(windowSurface, cornersRadius);
+ }
+ }
+
+ private void updateWallpaperForLetterbox(WindowState mainWindow) {
+ @LetterboxBackgroundType int letterboxBackgroundType =
+ mLetterboxConfiguration.getLetterboxBackgroundType();
+ boolean wallpaperShouldBeShown =
+ letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER
+ && isLetterboxed(mainWindow)
+ // Don't use wallpaper as a background if letterboxed for display cutout.
+ && !mainWindow.isLetterboxedForDisplayCutout()
+ // Check that dark scrim alpha or blur radius are provided
+ && (getLetterboxWallpaperBlurRadius() > 0
+ || getLetterboxWallpaperDarkScrimAlpha() > 0)
+ // Check that blur is supported by a device if blur radius is provided.
+ && (getLetterboxWallpaperBlurRadius() <= 0
+ || isLetterboxWallpaperBlurSupported());
+ if (mShowWallpaperForLetterboxBackground != wallpaperShouldBeShown) {
+ mShowWallpaperForLetterboxBackground = wallpaperShouldBeShown;
+ mActivityRecord.requestUpdateWallpaperIfNeeded();
+ }
+ }
+
+ private int getLetterboxWallpaperBlurRadius() {
+ int blurRadius = mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius();
+ return blurRadius < 0 ? 0 : blurRadius;
+ }
+
+ private float getLetterboxWallpaperDarkScrimAlpha() {
+ float alpha = mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha();
+ // No scrim by default.
+ return (alpha < 0 || alpha >= 1) ? 0.0f : alpha;
+ }
+
+ private boolean isLetterboxWallpaperBlurSupported() {
+ return mLetterboxConfiguration.mContext.getSystemService(WindowManager.class)
+ .isCrossWindowBlurEnabled();
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ final WindowState mainWin = mActivityRecord.findMainWindow();
+ if (mainWin == null) {
+ return;
+ }
+
+ boolean areBoundsLetterboxed = mainWin.isLetterboxedAppWindow();
+ pw.println(prefix + "areBoundsLetterboxed=" + areBoundsLetterboxed);
+ if (!areBoundsLetterboxed) {
+ return;
+ }
+
+ pw.println(prefix + " letterboxReason=" + getLetterboxReasonString(mainWin));
+ pw.println(prefix + " letterboxAspectRatio="
+ + mActivityRecord.computeAspectRatio(mActivityRecord.getBounds()));
+
+ boolean isLetterboxUiShown = isLetterboxed(mainWin);
+ pw.println(prefix + "isLetterboxUiShown=" + isLetterboxUiShown);
+
+ if (!isLetterboxUiShown) {
+ return;
+ }
+ pw.println(prefix + " letterboxBackgroundColor=" + Integer.toHexString(
+ getLetterboxBackgroundColor().toArgb()));
+ pw.println(prefix + " letterboxBackgroundType="
+ + letterboxBackgroundTypeToString(
+ mLetterboxConfiguration.getLetterboxBackgroundType()));
+ if (mLetterboxConfiguration.getLetterboxBackgroundType()
+ == LETTERBOX_BACKGROUND_WALLPAPER) {
+ pw.println(prefix + " isLetterboxWallpaperBlurSupported="
+ + isLetterboxWallpaperBlurSupported());
+ pw.println(prefix + " letterboxBackgroundWallpaperDarkScrimAlpha="
+ + getLetterboxWallpaperDarkScrimAlpha());
+ pw.println(prefix + " letterboxBackgroundWallpaperBlurRadius="
+ + getLetterboxWallpaperBlurRadius());
+ }
+ pw.println(prefix + " letterboxHorizontalPositionMultiplier="
+ + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
+ }
+
+ /**
+ * Returns a string representing the reason for letterboxing. This method assumes the activity
+ * is letterboxed.
+ */
+ private String getLetterboxReasonString(WindowState mainWin) {
+ if (mActivityRecord.inSizeCompatMode()) {
+ return "SIZE_COMPAT_MODE";
+ }
+ if (mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio()) {
+ return "FIXED_ORIENTATION";
+ }
+ if (mainWin.isLetterboxedForDisplayCutout()) {
+ return "DISPLAY_CUTOUT";
+ }
+ return "UNKNOWN_REASON";
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 6631a3e..9f9ac3e 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1415,6 +1415,9 @@
return true;
}
+ // The given task if always treated as in visible range if it is the origin of pinned task.
+ if (task.mChildPipActivity != null) return true;
+
if (mMaxNumVisibleTasks >= 0) {
// Always keep up to the max number of recent tasks, but return false afterwards
return numVisibleTasks <= mMaxNumVisibleTasks;
@@ -1433,20 +1436,18 @@
/** @return whether the given task can be trimmed even if it is outside the visible range. */
protected boolean isTrimmable(Task task) {
- final Task rootTask = task.getRootTask();
-
- // No stack for task, just trim it
- if (rootTask == null) {
+ // The task was detached, just trim it.
+ if (!task.isAttached()) {
return true;
}
// Ignore tasks from different displays
// TODO (b/115289124): No Recents on non-default displays.
- if (!rootTask.isOnHomeDisplay()) {
+ if (!task.isOnHomeDisplay()) {
return false;
}
- final Task rootHomeTask = rootTask.getDisplayArea().getRootHomeTask();
+ final Task rootHomeTask = task.getDisplayArea().getRootHomeTask();
// Home task does not exist. Don't trim the task.
if (rootHomeTask == null) {
return false;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 129a6ce..19c2e73 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -675,6 +675,8 @@
}
final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction();
final WindowContainer parent = navToken.getParent();
+ t.setLayer(navToken.getSurfaceControl(), navToken.getLastLayer());
+
if (animate) {
final NavBarFadeAnimationController navBarFadeAnimationController =
mDisplayContent.getDisplayPolicy().getNavBarFadeAnimationController();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 52551ec..b9fc8b1 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2124,6 +2124,8 @@
.setDeferTaskAppear(true)
.setHasBeenVisible(true)
.build();
+ // Establish bi-directional link between the original and pinned task.
+ r.setLastParentBeforePip();
// It's possible the task entering PIP is in freeform, so save the last
// non-fullscreen bounds. Then when this new PIP task exits PIP, it can restore
// to its previous freeform bounds.
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 95a4f69e..fb66c04 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -36,9 +36,11 @@
import android.animation.ArgbEvaluator;
import android.content.Context;
import android.graphics.Color;
+import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.os.Trace;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -210,9 +212,9 @@
String name = "RotationLayer";
mScreenshotLayer = displayContent.makeOverlay()
.setName(name)
- .setBufferSize(mWidth, mHeight)
.setSecure(isSecure)
.setCallsite("ScreenRotationAnimation")
+ .setBLASTLayer()
.build();
// This is the way to tell the input system to exclude this surface from occlusion
// detection since we don't have a window for it. We do this because this window is
@@ -225,32 +227,29 @@
.setCallsite("ScreenRotationAnimation")
.build();
- final Surface surface = mService.mSurfaceFactory.get();
- // In case display bounds change, screenshot buffer and surface may mismatch so
- // set a scaling mode.
- surface.copyFrom(mScreenshotLayer);
- surface.setScalingMode(Surface.SCALING_MODE_SCALE_TO_WINDOW);
-
+ HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
"ScreenRotationAnimation#getMedianBorderLuma");
- mStartLuma = RotationAnimationUtils.getMedianBorderLuma(
- screenshotBuffer.getHardwareBuffer(), screenshotBuffer.getColorSpace());
+ mStartLuma = RotationAnimationUtils.getMedianBorderLuma(hardwareBuffer,
+ screenshotBuffer.getColorSpace());
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- try {
- surface.attachAndQueueBufferWithColorSpace(screenshotBuffer.getHardwareBuffer(),
- screenshotBuffer.getColorSpace());
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage());
- }
+
+ GraphicBuffer buffer = GraphicBuffer.createFromHardwareBuffer(
+ screenshotBuffer.getHardwareBuffer());
+ // Scale the layer to the display size.
+ float dsdx = (float) mWidth / hardwareBuffer.getWidth();
+ float dsdy = (float) mHeight / hardwareBuffer.getHeight();
t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
t.reparent(mBackColorSurface, displayContent.getSurfaceControl());
t.setLayer(mBackColorSurface, -1);
t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
t.setAlpha(mBackColorSurface, 1);
+ t.setBuffer(mScreenshotLayer, buffer);
+ t.setColorSpace(mScreenshotLayer, screenshotBuffer.getColorSpace());
+ t.setMatrix(mScreenshotLayer, dsdx, 0, 0, dsdy);
t.show(mScreenshotLayer);
t.show(mBackColorSurface);
- surface.destroy();
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate freeze surface", e);
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b82a308..c6cd560 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -640,9 +640,16 @@
}
}
- void windowAddedLocked(String packageName) {
- mPackageName = packageName;
- mRelayoutTag = "relayoutWindow: " + mPackageName;
+ void windowAddedLocked() {
+ if (mPackageName == null) {
+ final WindowProcessController wpc = mService.mAtmService.mProcessMap.getProcess(mPid);
+ if (wpc != null) {
+ mPackageName = wpc.mInfo.packageName;
+ mRelayoutTag = "relayoutWindow: " + mPackageName;
+ } else {
+ Slog.e(TAG_WM, "Unknown process pid=" + mPid);
+ }
+ }
if (mSurfaceSession == null) {
if (DEBUG) {
Slog.v(TAG_WM, "First window added to " + this + ", creating SurfaceSession");
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index 140ae3e..a9b06ca 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -58,10 +58,12 @@
overrideConfig, displayId);
}
- final Task task = activity.getTask();
- if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow(task,
- activity.token, theme)) {
- return new ShellStartingSurface(task);
+ synchronized (mService.mGlobalLock) {
+ final Task task = activity.getTask();
+ if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow(
+ task, activity.token, theme, null /* taskSnapshot */)) {
+ return new ShellStartingSurface(task);
+ }
}
return null;
}
@@ -124,14 +126,13 @@
activity.mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(
topFullscreenActivity, false /* checkOpening */);
}
+ if (DEBUG_ENABLE_SHELL_DRAWER) {
+ mService.mAtmService.mTaskOrganizerController.addStartingWindow(task,
+ activity.token, 0 /* launchTheme */, taskSnapshot);
+ return new ShellStartingSurface(task);
+ }
}
- if (!DEBUG_ENABLE_SHELL_DRAWER) {
- return mService.mTaskSnapshotController
- .createStartingSurface(activity, taskSnapshot);
- }
- mService.mAtmService.mTaskOrganizerController.addStartingWindow(task, activity.token,
- 0 /* launchTheme */);
- return new ShellStartingSurface(task);
+ return mService.mTaskSnapshotController.createStartingSurface(activity, taskSnapshot);
}
@@ -144,8 +145,9 @@
@Override
public void remove(boolean animate) {
- mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask,
- animate);
+ synchronized (mService.mGlobalLock) {
+ mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask, animate);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index 6f434e0..e0a791e 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
@@ -153,29 +154,24 @@
*/
Snapshot(Supplier<Surface> surfaceFactory, SurfaceControl.Transaction t,
SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer, SurfaceControl parent) {
- Surface drawSurface = surfaceFactory.get();
// We can't use a delegating constructor since we need to
// reference this::onAnimationFinished
- HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
- final int width = hardwareBuffer.getWidth();
- final int height = hardwareBuffer.getHeight();
+ GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
+ screenshotBuffer.getHardwareBuffer());
mSurfaceControl = mAnimatable.makeAnimationLeash()
.setName("snapshot anim: " + mAnimatable.toString())
- .setBufferSize(width, height)
.setFormat(PixelFormat.TRANSLUCENT)
.setParent(parent)
.setSecure(screenshotBuffer.containsSecureLayers())
.setCallsite("SurfaceFreezer.Snapshot")
+ .setBLASTLayer()
.build();
ProtoLog.i(WM_SHOW_TRANSACTIONS, " THUMBNAIL %s: CREATE", mSurfaceControl);
- // Transfer the thumbnail to the surface
- drawSurface.copyFrom(mSurfaceControl);
- drawSurface.attachAndQueueBufferWithColorSpace(hardwareBuffer,
- screenshotBuffer.getColorSpace());
- drawSurface.release();
+ t.setBuffer(mSurfaceControl, graphicBuffer);
+ t.setColorSpace(mSurfaceControl, screenshotBuffer.getColorSpace());
t.show(mSurfaceControl);
// We parent the thumbnail to the container, and just place it on top of anything else
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8690499..858d9f3 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -131,6 +131,7 @@
import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
import static com.android.server.wm.TaskProto.DISPLAY_ID;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
+import static com.android.server.wm.TaskProto.HAS_CHILD_PIP_ACTIVITY;
import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
import static com.android.server.wm.TaskProto.MIN_HEIGHT;
import static com.android.server.wm.TaskProto.MIN_WIDTH;
@@ -836,6 +837,14 @@
// The task will be removed when TaskOrganizer, which is managing the task, is destroyed.
boolean mRemoveWithTaskOrganizer;
+ /**
+ * Reference to the pinned activity that is logically parented to this task, ie.
+ * the previous top activity within this task is put into pinned mode.
+ * This always gets cleared in pair with the ActivityRecord-to-Task link as seen in
+ * {@link ActivityRecord#clearLastParentBeforePip()}.
+ */
+ ActivityRecord mChildPipActivity;
+
private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
Intent _affinityIntent, String _affinity, String _rootAffinity,
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
@@ -1841,6 +1850,10 @@
/** Completely remove all activities associated with an existing task. */
void performClearTask(String reason) {
+ // The original task is to be removed, try remove also the pinned task.
+ if (mChildPipActivity != null && mChildPipActivity.getTask() != null) {
+ mTaskSupervisor.removeRootTask(mChildPipActivity.getTask());
+ }
// Broken down into to cases to avoid object create due to capturing mStack.
if (getRootTask() == null) {
forAllActivities((r) -> {
@@ -4449,6 +4462,7 @@
}
pw.print(prefix); pw.print("taskId=" + mTaskId);
pw.println(" rootTaskId=" + getRootTaskId());
+ pw.print(prefix); pw.println("hasChildPipActivity=" + (mChildPipActivity != null));
pw.print(prefix); pw.print("mHasBeenVisible="); pw.println(getHasBeenVisible());
pw.print(prefix); pw.print("mResizeMode=");
pw.print(ActivityInfo.resizeModeToString(mResizeMode));
@@ -5328,7 +5342,6 @@
return;
}
final int currentMode = getWindowingMode();
- final int currentOverrideMode = getRequestedOverrideWindowingMode();
final Task topTask = getTopMostTask();
int windowingMode = preferredWindowingMode;
@@ -5397,9 +5410,26 @@
mTaskSupervisor.mNoAnimActivities.add(topActivity);
}
super.setWindowingMode(windowingMode);
- // setWindowingMode triggers an onConfigurationChanged cascade which can result in a
- // different resolved windowing mode (usually when preferredWindowingMode is UNDEFINED).
- windowingMode = getWindowingMode();
+
+ // Try reparent pinned activity back to its original task after onConfigurationChanged
+ // cascade finishes. This is done on Task level instead of
+ // {@link ActivityRecord#onConfigurationChanged(Configuration)} since when we exit PiP,
+ // we set final windowing mode on the ActivityRecord first and then on its Task when
+ // the exit PiP transition finishes. Meanwhile, the exit transition is always
+ // performed on its original task, reparent immediately in ActivityRecord breaks it.
+ if (currentMode == WINDOWING_MODE_PINNED) {
+ if (topActivity != null && topActivity.getLastParentBeforePip() != null) {
+ // Do not reparent if the pinned task is in removal, indicated by the
+ // force hidden flag.
+ if (!isForceHidden()) {
+ final Task lastParentBeforePip = topActivity.getLastParentBeforePip();
+ topActivity.reparent(lastParentBeforePip,
+ lastParentBeforePip.getChildCount() /* top */,
+ "movePinnedActivityToOriginalTask");
+ lastParentBeforePip.moveToFront("movePinnedActivityToOriginalTask");
+ }
+ }
+ }
if (creating) {
// Nothing else to do if we don't have a window container yet. E.g. call from ctor.
@@ -6279,6 +6309,8 @@
// Launching this app's activity, make sure the app is no longer
// considered stopped.
try {
+ mTaskSupervisor.getActivityMetricsLogger()
+ .notifyBeforePackageUnstopped(next.packageName);
mAtmService.getPackageManager().setPackageStoppedState(
next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
} catch (RemoteException e1) {
@@ -7530,7 +7562,11 @@
final Task task = getBottomMostTask();
setWindowingMode(WINDOWING_MODE_UNDEFINED);
- getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
+ // Task could have been removed from the hierarchy due to windowing mode change
+ // where its only child is reparented back to their original parent task.
+ if (isAttached()) {
+ getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
+ }
mTaskSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
});
@@ -7831,6 +7867,7 @@
proto.write(CREATED_BY_ORGANIZER, mCreatedByOrganizer);
proto.write(AFFINITY, affinity);
+ proto.write(HAS_CHILD_PIP_ACTIVITY, mChildPipActivity != null);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index dff621c..625cff3 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -439,6 +439,13 @@
taskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
}
+ // Re-route to default display if the home activity doesn't support multi-display
+ if (taskDisplayArea != null && activityRecord.isActivityTypeHome()
+ && !mSupervisor.mRootWindowContainer.canStartHomeOnDisplayArea(activityRecord.info,
+ taskDisplayArea, false /* allowInstrumenting */)) {
+ taskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
+ }
+
return (taskDisplayArea != null)
? taskDisplayArea
: getFallbackDisplayAreaForActivity(activityRecord, request);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 565804f..ccc0916 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -43,6 +43,7 @@
import android.window.ITaskOrganizerController;
import android.window.StartingWindowInfo;
import android.window.TaskAppearedInfo;
+import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
@@ -118,25 +119,25 @@
return mTaskOrganizer.asBinder();
}
- void addStartingWindow(Task task, IBinder appToken, int launchTheme) {
+ void addStartingWindow(Task task, IBinder appToken, int launchTheme,
+ TaskSnapshot taskSnapshot) {
final StartingWindowInfo info = task.getStartingWindowInfo();
if (launchTheme != 0) {
info.splashScreenThemeResId = launchTheme;
}
- mDeferTaskOrgCallbacksConsumer.accept(() -> {
- try {
- mTaskOrganizer.addStartingWindow(info, appToken);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending onTaskStart callback", e);
- }
- });
+ info.mTaskSnapshot = taskSnapshot;
+ // make this happen prior than prepare surface
+ try {
+ mTaskOrganizer.addStartingWindow(info, appToken);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskStart callback", e);
+ }
}
void removeStartingWindow(Task task, boolean prepareAnimation) {
- mDeferTaskOrgCallbacksConsumer.accept(() -> {
- SurfaceControl firstWindowLeash = null;
- Rect mainFrame = null;
- // TODO enable shift up animation once we fix flicker test
+ SurfaceControl firstWindowLeash = null;
+ Rect mainFrame = null;
+ // TODO enable shift up animation once we fix flicker test
// final boolean playShiftUpAnimation = !task.inMultiWindowMode();
// if (prepareAnimation && playShiftUpAnimation) {
// final ActivityRecord topActivity = task.topActivityWithStartingWindow();
@@ -144,32 +145,29 @@
// final WindowState mainWindow =
// topActivity.findMainWindow(false/* includeStartingApp */);
// if (mainWindow != null) {
- // TODO create proper leash instead of the copied SC
+ // TODO create proper leash instead of the copied SC
// firstWindowLeash = new SurfaceControl(mainWindow.getSurfaceControl(),
// "TaskOrganizerController.removeStartingWindow");
// mainFrame = mainWindow.getRelativeFrame();
// }
// }
// }
- try {
- mTaskOrganizer.removeStartingWindow(task.mTaskId, firstWindowLeash, mainFrame,
- /* TODO(183004107) Revert this when jankiness is solved
- prepareAnimation); */ false);
+ try {
+ mTaskOrganizer.removeStartingWindow(task.mTaskId, firstWindowLeash, mainFrame,
+ /* TODO(183004107) Revert this when jankiness is solved
+ prepareAnimation); */ false);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
- }
- });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
+ }
}
void copySplashScreenView(Task task) {
- mDeferTaskOrgCallbacksConsumer.accept(() -> {
- try {
- mTaskOrganizer.copySplashScreenView(task.mTaskId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending copyStartingWindowView callback", e);
- }
- });
+ try {
+ mTaskOrganizer.copySplashScreenView(task.mTaskId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending copyStartingWindowView callback", e);
+ }
}
SurfaceControl prepareLeash(Task task, boolean visible, String reason) {
@@ -266,8 +264,9 @@
mUid = uid;
}
- void addStartingWindow(Task t, IBinder appToken, int launchTheme) {
- mOrganizer.addStartingWindow(t, appToken, launchTheme);
+ void addStartingWindow(Task t, IBinder appToken, int launchTheme,
+ TaskSnapshot taskSnapshot) {
+ mOrganizer.addStartingWindow(t, appToken, launchTheme, taskSnapshot);
}
void removeStartingWindow(Task t, boolean prepareAnimation) {
@@ -505,14 +504,15 @@
return !ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, winMode);
}
- boolean addStartingWindow(Task task, IBinder appToken, int launchTheme) {
+ boolean addStartingWindow(Task task, IBinder appToken, int launchTheme,
+ TaskSnapshot taskSnapshot) {
final Task rootTask = task.getRootTask();
if (rootTask == null || rootTask.mTaskOrganizer == null) {
return false;
}
final TaskOrganizerState state =
mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
- state.addStartingWindow(task, appToken, launchTheme);
+ state.addStartingWindow(task, appToken, launchTheme, taskSnapshot);
return true;
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 5ffab48..d49b6a0 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -486,7 +486,7 @@
return builder.build();
}
- private boolean shouldDisableSnapshots() {
+ boolean shouldDisableSnapshots() {
return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT;
}
@@ -646,8 +646,12 @@
if (shouldDisableSnapshots()) {
return;
}
+ final DisplayContent displayContent = mService.mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ return;
+ }
mTmpTasks.clear();
- mService.mRoot.getDisplayContent(displayId).forAllTasks(task -> {
+ displayContent.forAllTasks(task -> {
// Since RecentsAnimation will handle task snapshot while switching apps with the best
// capture timing (e.g. IME window capture), No need additional task capture while task
// is controlled by RecentsAnimation.
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index 5e75996..0bb56e4 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -231,7 +231,7 @@
}
@Override
- public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
+ public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
reportConfigToWindowTokenClient();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7650fa1..61fce88 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -159,7 +159,6 @@
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Bitmap;
-import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Point;
@@ -731,7 +730,7 @@
final TaskSnapshotController mTaskSnapshotController;
- final BlurController mBlurController = new BlurController();
+ final BlurController mBlurController;
boolean mIsTouchDevice;
boolean mIsFakeTouchDevice;
@@ -977,52 +976,7 @@
private boolean mAnimationsDisabled = false;
boolean mPointerLocationEnabled = false;
- /**
- * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
- * set-fixed-orientation-letterbox-aspect-ratio or via {@link
- * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored
- * if it is <= this value.
- */
- static final float MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.0f;
-
- /** Enum for Letterbox background type. */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({LETTERBOX_BACKGROUND_SOLID_COLOR, LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND,
- LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING, LETTERBOX_BACKGROUND_WALLPAPER})
- @interface LetterboxBackgroundType {};
- /** Solid background using color specified in R.color.config_letterboxBackgroundColor. */
- static final int LETTERBOX_BACKGROUND_SOLID_COLOR = 0;
-
- /** Color specified in R.attr.colorBackground for the letterboxed application. */
- static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND = 1;
-
- /** Color specified in R.attr.colorBackgroundFloating for the letterboxed application. */
- static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING = 2;
-
- /** Using wallpaper as a background which can be blurred or dimmed with dark scrim. */
- static final int LETTERBOX_BACKGROUND_WALLPAPER = 3;
-
- // Aspect ratio of letterbox for fixed orientation, values <=
- // MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored.
- private float mFixedOrientationLetterboxAspectRatio;
-
- // Corners radius for activities presented in the letterbox mode, values < 0 will be ignored.
- private int mLetterboxActivityCornersRadius;
-
- // Color for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type.
- private Color mLetterboxBackgroundColor;
-
- @LetterboxBackgroundType
- private int mLetterboxBackgroundType;
-
- // Blur radius for LETTERBOX_BACKGROUND_WALLPAPER option in mLetterboxBackgroundType.
- // Values <= 0 are ignored and 0 is used instead.
- private int mLetterboxBackgroundWallpaperBlurRadius;
-
- // Alpha of a black scrim shown over wallpaper letterbox background when
- // LETTERBOX_BACKGROUND_WALLPAPER option is selected for mLetterboxBackgroundType.
- // Values < 0 or >= 1 are ignored and 0.0 (transparent) is used instead.
- private float mLetterboxBackgroundWallpaperDarkScrimAlpha;
+ final LetterboxConfiguration mLetterboxConfiguration;
final InputManagerService mInputManager;
final DisplayManagerInternal mDisplayManagerInternal;
@@ -1254,17 +1208,7 @@
mAssistantOnTopOfDream = context.getResources().getBoolean(
com.android.internal.R.bool.config_assistantOnTopOfDream);
- mFixedOrientationLetterboxAspectRatio = context.getResources().getFloat(
- com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
- mLetterboxActivityCornersRadius = context.getResources().getInteger(
- com.android.internal.R.integer.config_letterboxActivityCornersRadius);
- mLetterboxBackgroundColor = Color.valueOf(context.getResources().getColor(
- com.android.internal.R.color.config_letterboxBackgroundColor));
- mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(context);
- mLetterboxBackgroundWallpaperBlurRadius = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
- mLetterboxBackgroundWallpaperDarkScrimAlpha = context.getResources().getFloat(
- com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
+ mLetterboxConfiguration = new LetterboxConfiguration(context);
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -1418,6 +1362,8 @@
setGlobalShadowSettings();
mAnrController = new AnrController(this);
mStartingSurfaceController = new StartingSurfaceController(this);
+
+ mBlurController = new BlurController(mContext, mPowerManager);
}
DisplayAreaPolicy.Provider getDisplayAreaPolicyProvider() {
@@ -1740,8 +1686,11 @@
}
// Switch to listen to the {@link WindowToken token}'s configuration changes when
- // adding a window to the window context.
- if (mWindowContextListenerController.hasListener(windowContextToken)) {
+ // adding a window to the window context. Filter sub window type here because the sub
+ // window must be attached to the parent window, which is attached to the window context
+ // created window token.
+ if (!win.isChildWindow()
+ && mWindowContextListenerController.hasListener(windowContextToken)) {
final int windowContextType = mWindowContextListenerController
.getWindowType(windowContextToken);
if (type != windowContextType) {
@@ -3858,201 +3807,6 @@
}
}
- /**
- * Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link
- * #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
- * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
- * the framework implementation will be used to determine the aspect ratio.
- */
- void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
- mFixedOrientationLetterboxAspectRatio = aspectRatio;
- }
-
- /**
- * Resets the aspect ratio of letterbox for fixed orientation to {@link
- * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}.
- */
- void resetFixedOrientationLetterboxAspectRatio() {
- mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
- }
-
- /**
- * Gets the aspect ratio of letterbox for fixed orientation.
- */
- float getFixedOrientationLetterboxAspectRatio() {
- return mFixedOrientationLetterboxAspectRatio;
- }
-
- /**
- * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
- * both it and a value of {@link
- * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
- * and corners of the activity won't be rounded.
- */
- void setLetterboxActivityCornersRadius(int cornersRadius) {
- mLetterboxActivityCornersRadius = cornersRadius;
- }
-
- /**
- * Resets corners raidus for activities presented in the letterbox mode to {@link
- * com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
- */
- void resetLetterboxActivityCornersRadius() {
- mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_letterboxActivityCornersRadius);
- }
-
- /**
- * Whether corners of letterboxed activities are rounded.
- */
- boolean isLetterboxActivityCornersRounded() {
- return getLetterboxActivityCornersRadius() > 0;
- }
-
- /**
- * Gets corners raidus for activities presented in the letterbox mode.
- */
- int getLetterboxActivityCornersRadius() {
- return mLetterboxActivityCornersRadius;
- }
-
- /**
- * Gets color of letterbox background which is used when {@link
- * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
- * fallback for other backfround types.
- */
- Color getLetterboxBackgroundColor() {
- return mLetterboxBackgroundColor;
- }
-
-
- /**
- * Sets color of letterbox background which is used when {@link
- * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
- * fallback for other backfround types.
- */
- void setLetterboxBackgroundColor(Color color) {
- mLetterboxBackgroundColor = color;
- }
-
- /**
- * Resets color of letterbox background to {@link
- * com.android.internal.R.color.config_letterboxBackgroundColor}.
- */
- void resetLetterboxBackgroundColor() {
- mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
- com.android.internal.R.color.config_letterboxBackgroundColor));
- }
-
- /**
- * Gets {@link LetterboxBackgroundType} specified in {@link
- * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
- */
- @LetterboxBackgroundType
- int getLetterboxBackgroundType() {
- return mLetterboxBackgroundType;
- }
-
- /** Sets letterbox background type. */
- void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
- mLetterboxBackgroundType = backgroundType;
- }
-
- /**
- * Resets cletterbox background type to {@link
- * com.android.internal.R.integer.config_letterboxBackgroundType}.
- */
- void resetLetterboxBackgroundType() {
- mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
- }
-
- @LetterboxBackgroundType
- private static int readLetterboxBackgroundTypeFromConfig(Context context) {
- int backgroundType = context.getResources().getInteger(
- com.android.internal.R.integer.config_letterboxBackgroundType);
- return backgroundType == LETTERBOX_BACKGROUND_SOLID_COLOR
- || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND
- || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING
- || backgroundType == LETTERBOX_BACKGROUND_WALLPAPER
- ? backgroundType : LETTERBOX_BACKGROUND_SOLID_COLOR;
- }
-
- /** Returns a string representing the given {@link LetterboxBackgroundType}. */
- static String letterboxBackgroundTypeToString(
- @LetterboxBackgroundType int backgroundType) {
- switch (backgroundType) {
- case LETTERBOX_BACKGROUND_SOLID_COLOR:
- return "LETTERBOX_BACKGROUND_SOLID_COLOR";
- case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
- return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND";
- case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
- return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING";
- case LETTERBOX_BACKGROUND_WALLPAPER:
- return "LETTERBOX_BACKGROUND_WALLPAPER";
- default:
- return "unknown=" + backgroundType;
- }
- }
-
- /**
- * Overrides alpha of a black scrim shown over wallpaper for {@link
- * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}.
- *
- * <p>If given value is < 0 or >= 1, both it and a value of {@link
- * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored
- * and 0.0 (transparent) is instead.
- */
- void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) {
- mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha;
- }
-
- /**
- * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link
- * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}.
- */
- void resetLetterboxBackgroundWallpaperDarkScrimAlpha() {
- mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
- }
-
- /**
- * Gets alpha of a black scrim shown over wallpaper letterbox background.
- */
- float getLetterboxBackgroundWallpaperDarkScrimAlpha() {
- return mLetterboxBackgroundWallpaperDarkScrimAlpha;
- }
-
- /**
- * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in
- * {@link mLetterboxBackgroundType}.
- *
- * <p> If given value <= 0, both it and a value of {@link
- * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored
- * and 0 is used instead.
- */
- void setLetterboxBackgroundWallpaperBlurRadius(int radius) {
- mLetterboxBackgroundWallpaperBlurRadius = radius;
- }
-
- /**
- * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
- * mLetterboxBackgroundType} to {@link
- * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}.
- */
- void resetLetterboxBackgroundWallpaperBlurRadius() {
- mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
- }
-
- /**
- * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
- * mLetterboxBackgroundType}.
- */
- int getLetterboxBackgroundWallpaperBlurRadius() {
- return mLetterboxBackgroundWallpaperBlurRadius;
- }
-
@Override
public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
if (!checkCallingPermission(
@@ -8744,6 +8498,13 @@
mDisplayHashController.setDisplayHashThrottlingEnabled(enable);
}
+ @Override
+ public boolean isTaskSnapshotSupported() {
+ synchronized (mGlobalLock) {
+ return !mTaskSnapshotController.shouldDisableSnapshots();
+ }
+ }
+
void generateDisplayHash(Session session, IWindow window, Rect boundsInWindow,
String hashAlgorithm, RemoteCallback callback) {
final SurfaceControl displaySurfaceControl;
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 5942f34..1b578d1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -19,10 +19,10 @@
import static android.os.Build.IS_USER;
import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
-import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_WALLPAPER;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
import android.graphics.Color;
import android.graphics.Point;
@@ -41,7 +41,7 @@
import com.android.internal.protolog.ProtoLogImpl;
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.wm.WindowManagerService.LetterboxBackgroundType;
+import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
import java.io.IOException;
import java.io.PrintWriter;
@@ -64,10 +64,12 @@
// Internal service impl -- must perform security checks before touching.
private final WindowManagerService mInternal;
+ private final LetterboxConfiguration mLetterboxConfiguration;
public WindowManagerShellCommand(WindowManagerService service) {
mInterface = service;
mInternal = service;
+ mLetterboxConfiguration = service.mLetterboxConfiguration;
}
@Override
@@ -119,30 +121,12 @@
return runGetIgnoreOrientationRequest(pw);
case "dump-visible-window-views":
return runDumpVisibleWindowViews(pw);
- case "set-fixed-orientation-letterbox-aspect-ratio":
- return runSetFixedOrientationLetterboxAspectRatio(pw);
- case "get-fixed-orientation-letterbox-aspect-ratio":
- return runGetFixedOrientationLetterboxAspectRatio(pw);
- case "set-letterbox-activity-corners-radius":
- return runSetLetterboxActivityCornersRadius(pw);
- case "get-letterbox-activity-corners-radius":
- return runGetLetterboxActivityCornersRadius(pw);
- case "set-letterbox-background-type":
- return runSetLetterboxBackgroundType(pw);
- case "get-letterbox-background-type":
- return runGetLetterboxBackgroundType(pw);
- case "set-letterbox-background-color":
- return runSetLetterboxBackgroundColor(pw);
- case "get-letterbox-background-color":
- return runGetLetterboxBackgroundColor(pw);
- case "set-letterbox-background-wallpaper-blur-radius":
- return runSetLetterboxBackgroundWallpaperBlurRadius(pw);
- case "get-letterbox-background-wallpaper-blur-radius":
- return runGetLetterboxBackgroundWallpaperBlurRadius(pw);
- case "set-letterbox-background-wallpaper-dark-scrim-alpha":
- return runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
- case "get-letterbox-background-wallpaper-dark-scrim-alpha":
- return runGetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
+ case "set-letterbox-style":
+ return runSetLetterboxStyle(pw);
+ case "get-letterbox-style":
+ return runGetLetterboxStyle(pw);
+ case "reset-letterbox-style":
+ return runResetLetterboxStyle(pw);
case "set-sandbox-display-apis":
return runSandboxDisplayApis(pw);
case "reset":
@@ -607,12 +591,6 @@
final float aspectRatio;
try {
String arg = getNextArgRequired();
- if ("reset".equals(arg)) {
- synchronized (mInternal.mGlobalLock) {
- mInternal.resetFixedOrientationLetterboxAspectRatio();
- }
- return 0;
- }
aspectRatio = Float.parseFloat(arg);
} catch (NumberFormatException e) {
getErrPrintWriter().println("Error: bad aspect ratio format " + e);
@@ -623,19 +601,7 @@
return -1;
}
synchronized (mInternal.mGlobalLock) {
- mInternal.setFixedOrientationLetterboxAspectRatio(aspectRatio);
- }
- return 0;
- }
-
- private int runGetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
- synchronized (mInternal.mGlobalLock) {
- final float aspectRatio = mInternal.getFixedOrientationLetterboxAspectRatio();
- if (aspectRatio <= WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) {
- pw.println("Letterbox aspect ratio is not set");
- } else {
- pw.println("Letterbox aspect ratio is " + aspectRatio);
- }
+ mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio);
}
return 0;
}
@@ -644,12 +610,6 @@
final int cornersRadius;
try {
String arg = getNextArgRequired();
- if ("reset".equals(arg)) {
- synchronized (mInternal.mGlobalLock) {
- mInternal.resetLetterboxActivityCornersRadius();
- }
- return 0;
- }
cornersRadius = Integer.parseInt(arg);
} catch (NumberFormatException e) {
getErrPrintWriter().println("Error: bad corners radius format " + e);
@@ -660,110 +620,59 @@
return -1;
}
synchronized (mInternal.mGlobalLock) {
- mInternal.setLetterboxActivityCornersRadius(cornersRadius);
- }
- return 0;
- }
-
- private int runGetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
- synchronized (mInternal.mGlobalLock) {
- final int cornersRadius = mInternal.getLetterboxActivityCornersRadius();
- if (cornersRadius < 0) {
- pw.println("Letterbox corners radius is not set");
- } else {
- pw.println("Letterbox corners radius is " + cornersRadius);
- }
+ mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius);
}
return 0;
}
private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
@LetterboxBackgroundType final int backgroundType;
-
- String arg = getNextArgRequired();
- if ("reset".equals(arg)) {
- synchronized (mInternal.mGlobalLock) {
- mInternal.resetLetterboxBackgroundType();
- }
- return 0;
- }
- switch (arg) {
- case "solid_color":
- backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
- break;
- case "app_color_background":
- backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
- break;
- case "app_color_background_floating":
- backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
- break;
- case "wallpaper":
- backgroundType = LETTERBOX_BACKGROUND_WALLPAPER;
- break;
- default:
- getErrPrintWriter().println(
- "Error: 'reset', 'solid_color', 'app_color_background' or "
- + "'wallpaper' should be provided as an argument");
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mInternal.setLetterboxBackgroundType(backgroundType);
- }
- return 0;
- }
-
- private int runGetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
- synchronized (mInternal.mGlobalLock) {
- @LetterboxBackgroundType final int backgroundType =
- mInternal.getLetterboxBackgroundType();
- switch (backgroundType) {
- case LETTERBOX_BACKGROUND_SOLID_COLOR:
- pw.println("Letterbox background type is 'solid_color'");
+ try {
+ String arg = getNextArgRequired();
+ switch (arg) {
+ case "solid_color":
+ backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
break;
- case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
- pw.println("Letterbox background type is 'app_color_background'");
+ case "app_color_background":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
break;
- case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
- pw.println("Letterbox background type is 'app_color_background_floating'");
+ case "app_color_background_floating":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
break;
- case LETTERBOX_BACKGROUND_WALLPAPER:
- pw.println("Letterbox background type is 'wallpaper'");
+ case "wallpaper":
+ backgroundType = LETTERBOX_BACKGROUND_WALLPAPER;
break;
default:
- throw new AssertionError(
- "Unexpected letterbox background type: " + backgroundType);
+ getErrPrintWriter().println(
+ "Error: 'reset', 'solid_color', 'app_color_background' or "
+ + "'wallpaper' should be provided as an argument");
+ return -1;
}
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset', 'solid_color', 'app_color_background' or "
+ + "'wallpaper' should be provided as an argument" + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType);
}
return 0;
}
private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
final Color color;
- String arg = getNextArgRequired();
try {
- if ("reset".equals(arg)) {
- synchronized (mInternal.mGlobalLock) {
- mInternal.resetLetterboxBackgroundColor();
- }
- return 0;
- }
+ String arg = getNextArgRequired();
color = Color.valueOf(Color.parseColor(arg));
} catch (IllegalArgumentException e) {
getErrPrintWriter().println(
"Error: 'reset' or color in #RRGGBB format should be provided as "
- + "an argument " + e + " but got " + arg);
+ + "an argument " + e);
return -1;
}
synchronized (mInternal.mGlobalLock) {
- mInternal.setLetterboxBackgroundColor(color);
- }
- return 0;
- }
-
- private int runGetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
- synchronized (mInternal.mGlobalLock) {
- final Color color = mInternal.getLetterboxBackgroundColor();
- pw.println("Letterbox background color is " + Integer.toHexString(color.toArgb()));
+ mLetterboxConfiguration.setLetterboxBackgroundColor(color);
}
return 0;
}
@@ -773,12 +682,6 @@
final int radius;
try {
String arg = getNextArgRequired();
- if ("reset".equals(arg)) {
- synchronized (mInternal.mGlobalLock) {
- mInternal.resetLetterboxBackgroundWallpaperBlurRadius();
- }
- return 0;
- }
radius = Integer.parseInt(arg);
} catch (NumberFormatException e) {
getErrPrintWriter().println("Error: blur radius format " + e);
@@ -789,20 +692,7 @@
return -1;
}
synchronized (mInternal.mGlobalLock) {
- mInternal.setLetterboxBackgroundWallpaperBlurRadius(radius);
- }
- return 0;
- }
-
- private int runGetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw)
- throws RemoteException {
- synchronized (mInternal.mGlobalLock) {
- final int radius = mInternal.getLetterboxBackgroundWallpaperBlurRadius();
- if (radius <= 0) {
- pw.println("Letterbox background wallpaper blur radius is not set");
- } else {
- pw.println("Letterbox background wallpaper blur radius is " + radius);
- }
+ mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius);
}
return 0;
}
@@ -812,12 +702,6 @@
final float alpha;
try {
String arg = getNextArgRequired();
- if ("reset".equals(arg)) {
- synchronized (mInternal.mGlobalLock) {
- mInternal.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
- }
- return 0;
- }
alpha = Float.parseFloat(arg);
} catch (NumberFormatException e) {
getErrPrintWriter().println("Error: bad alpha format " + e);
@@ -828,24 +712,140 @@
return -1;
}
synchronized (mInternal.mGlobalLock) {
- mInternal.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha);
+ mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha);
}
return 0;
}
- private int runGetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw)
- throws RemoteException {
+ private int runSeLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException {
+ final float multiplier;
+ try {
+ String arg = getNextArgRequired();
+ multiplier = Float.parseFloat(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad multiplier format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or multiplier should be provided as an argument " + e);
+ return -1;
+ }
synchronized (mInternal.mGlobalLock) {
- final float alpha = mInternal.getLetterboxBackgroundWallpaperDarkScrimAlpha();
- if (alpha < 0 || alpha >= 1) {
- pw.println("Letterbox dark scrim alpha is not set");
- } else {
- pw.println("Letterbox dark scrim alpha is " + alpha);
+ mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
+ if (peekNextArg() == null) {
+ getErrPrintWriter().println("Error: No arguments provided.");
+ }
+ while (peekNextArg() != null) {
+ String arg = getNextArg();
+ switch (arg) {
+ case "--aspectRatio":
+ runSetFixedOrientationLetterboxAspectRatio(pw);
+ break;
+ case "--cornerRadius":
+ runSetLetterboxActivityCornersRadius(pw);
+ break;
+ case "--backgroundType":
+ runSetLetterboxBackgroundType(pw);
+ break;
+ case "--backgroundColor":
+ runSetLetterboxBackgroundColor(pw);
+ break;
+ case "--wallpaperBlurRadius":
+ runSetLetterboxBackgroundWallpaperBlurRadius(pw);
+ break;
+ case "--wallpaperDarkScrimAlpha":
+ runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
+ break;
+ case "--horizontalPositionMultiplier":
+ runSeLetterboxHorizontalPositionMultiplier(pw);
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: Unrecognized letterbox style option: " + arg);
+ return -1;
}
}
return 0;
}
+ private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException {
+ if (peekNextArg() == null) {
+ resetLetterboxStyle();
+ }
+ synchronized (mInternal.mGlobalLock) {
+ while (peekNextArg() != null) {
+ String arg = getNextArg();
+ switch (arg) {
+ case "aspectRatio":
+ mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
+ break;
+ case "cornerRadius":
+ mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
+ break;
+ case "backgroundType":
+ mLetterboxConfiguration.resetLetterboxBackgroundType();
+ break;
+ case "backgroundColor":
+ mLetterboxConfiguration.resetLetterboxBackgroundColor();
+ break;
+ case "wallpaperBlurRadius":
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
+ break;
+ case "wallpaperDarkScrimAlpha":
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
+ break;
+ case "horizontalPositionMultiplier":
+ mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: Unrecognized letterbox style option: " + arg);
+ return -1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ private void resetLetterboxStyle() {
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
+ mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
+ mLetterboxConfiguration.resetLetterboxBackgroundType();
+ mLetterboxConfiguration.resetLetterboxBackgroundColor();
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
+ mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+ }
+ }
+
+ private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException {
+ synchronized (mInternal.mGlobalLock) {
+ pw.println("Corner radius: "
+ + mLetterboxConfiguration.getLetterboxActivityCornersRadius());
+ pw.println("Horizontal position multiplier: "
+ + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
+ pw.println("Aspect ratio: "
+ + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
+
+ pw.println("Background type: "
+ + LetterboxConfiguration.letterboxBackgroundTypeToString(
+ mLetterboxConfiguration.getLetterboxBackgroundType()));
+ pw.println(" Background color: " + Integer.toHexString(
+ mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb()));
+ pw.println(" Wallpaper blur radius: "
+ + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius());
+ pw.println(" Wallpaper dark scrim alpha: "
+ + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha());
+ }
+ return 0;
+ }
+
private int runReset(PrintWriter pw) throws RemoteException {
int displayId = getDisplayId(getNextArg());
@@ -870,23 +870,8 @@
// set-ignore-orientation-request
mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
- // set-fixed-orientation-letterbox-aspect-ratio
- mInternal.resetFixedOrientationLetterboxAspectRatio();
-
- // set-letterbox-activity-corners-radius
- mInternal.resetLetterboxActivityCornersRadius();
-
- // set-letterbox-background-type
- mInternal.resetLetterboxBackgroundType();
-
- // set-letterbox-background-color
- mInternal.resetLetterboxBackgroundColor();
-
- // set-letterbox-background-wallpaper-blur-radius
- mInternal.resetLetterboxBackgroundWallpaperBlurRadius();
-
- // set-letterbox-background-wallpaper-dark-scrim-alpha
- mInternal.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
+ // set-letterbox-style
+ resetLetterboxStyle();
// set-sandbox-display-apis
mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true);
@@ -922,42 +907,13 @@
pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] ");
pw.println(" If app requested orientation should be ignored.");
- pw.println(" set-fixed-orientation-letterbox-aspect-ratio [reset|aspectRatio]");
- pw.println(" get-fixed-orientation-letterbox-aspect-ratio");
- pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
- + WindowManagerService.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
- pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will be");
- pw.println(" ignored and framework implementation will determine aspect ratio.");
- pw.println(" set-letterbox-activity-corners-radius [reset|cornersRadius]");
- pw.println(" get-letterbox-activity-corners-radius");
- pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
- pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be");
- pw.println(" ignored and corners of the activity won't be rounded.");
- pw.println(" set-letterbox-background-color [reset|colorName|'\\#RRGGBB']");
- pw.println(" get-letterbox-background-color");
- pw.println(" Color of letterbox background which is be used when letterbox background");
- pw.println(" type is 'solid-color'. Use get(set)-letterbox-background-type to check");
- pw.println(" and control letterbox background type. See Color#parseColor for allowed");
- pw.println(" color formats (#RRGGBB and some colors by name, e.g. magenta or olive). ");
- pw.println(" set-letterbox-background-type [reset|solid_color|app_color_background");
- pw.println(" |app_color_background_floating|wallpaper]");
- pw.println(" get-letterbox-background-type");
- pw.println(" Type of background used in the letterbox mode.");
- pw.println(" set-letterbox-background-wallpaper-blur-radius [reset|radius]");
- pw.println(" get-letterbox-background-wallpaper-blur-radius");
- pw.println(" Blur radius for 'wallpaper' letterbox background. If radius <= 0");
- pw.println(" both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius are ");
- pw.println(" ignored and 0 is used.");
- pw.println(" set-letterbox-background-wallpaper-dark-scrim-alpha [reset|alpha]");
- pw.println(" get-letterbox-background-wallpaper-dark-scrim-alpha");
- pw.println(" Alpha of a black translucent scrim shown over 'wallpaper'");
- pw.println(" letterbox background. If alpha < 0 or >= 1 both it and");
- pw.println(" R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored and ");
- pw.println(" 0.0 (transparent) is used instead.");
pw.println(" set-sandbox-display-apis [true|1|false|0]");
pw.println(" Sets override of Display APIs getRealSize / getRealMetrics to reflect ");
pw.println(" DisplayArea of the activity, or the window bounds if in letterbox or");
pw.println(" Size Compat Mode.");
+
+ printLetterboxHelp(pw);
+
pw.println(" reset [-d DISPLAY_ID]");
pw.println(" Reset all override settings.");
if (!IS_USER) {
@@ -967,4 +923,47 @@
pw.println(" Logging settings.");
}
}
+
+ private void printLetterboxHelp(PrintWriter pw) {
+ pw.println(" set-letterbox-style");
+ pw.println(" Sets letterbox style using the following options:");
+ pw.println(" --aspectRatio aspectRatio");
+ pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
+ + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+ pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will");
+ pw.println(" be ignored and framework implementation will determine aspect ratio.");
+ pw.println(" --cornerRadius radius");
+ pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
+ pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be");
+ pw.println(" ignored and corners of the activity won't be rounded.");
+ pw.println(" --backgroundType [reset|solid_color|app_color_background");
+ pw.println(" |app_color_background_floating|wallpaper]");
+ pw.println(" Type of background used in the letterbox mode.");
+ pw.println(" --backgroundColor color");
+ pw.println(" Color of letterbox which is be used when letterbox background type");
+ pw.println(" is 'solid-color'. Use (set)get-letterbox-style to check and control");
+ pw.println(" letterbox background type. See Color#parseColor for allowed color");
+ pw.println(" formats (#RRGGBB and some colors by name, e.g. magenta or olive).");
+ pw.println(" --wallpaperBlurRadius radius");
+ pw.println(" Blur radius for 'wallpaper' letterbox background. If radius <= 0");
+ pw.println(" both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius");
+ pw.println(" are ignored and 0 is used.");
+ pw.println(" --wallpaperDarkScrimAlpha alpha");
+ pw.println(" Alpha of a black translucent scrim shown over 'wallpaper'");
+ pw.println(" letterbox background. If alpha < 0 or >= 1 both it and");
+ pw.println(" R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored");
+ pw.println(" and 0.0 (transparent) is used instead.");
+ pw.println(" --horizontalPositionMultiplier multiplier");
+ pw.println(" Horizontal position of app window center. If multiplier < 0 or > 1,");
+ pw.println(" both it and R.dimen.config_letterboxHorizontalPositionMultiplier");
+ pw.println(" are ignored and central position (0.5) is used.");
+ pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
+ pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
+ pw.println(" |horizontalPositionMultiplier]");
+ pw.println(" Resets overrides to default values for specified properties separated");
+ pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
+ pw.println(" If no arguments provided, all values will be reset.");
+ pw.println(" get-letterbox-style");
+ pw.println(" Prints letterbox style configuration.");
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 2e37fee..9382b8e 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -36,6 +36,7 @@
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Binder;
@@ -45,7 +46,6 @@
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Slog;
-import android.view.Surface;
import android.view.SurfaceControl;
import android.window.IDisplayAreaOrganizerController;
import android.window.ITaskOrganizerController;
@@ -766,18 +766,21 @@
return false;
}
+ GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
+ buffer.getHardwareBuffer());
SurfaceControl screenshot = mService.mWindowManager.mSurfaceControlFactory.apply(null)
.setName(wc.getName() + " - Organizer Screenshot")
- .setBufferSize(bounds.width(), bounds.height())
.setFormat(PixelFormat.TRANSLUCENT)
.setParent(wc.getParentSurfaceControl())
+ .setSecure(buffer.containsSecureLayers())
.setCallsite("WindowOrganizerController.takeScreenshot")
+ .setBLASTLayer()
.build();
- Surface surface = new Surface();
- surface.copyFrom(screenshot);
- surface.attachAndQueueBufferWithColorSpace(buffer.getHardwareBuffer(), null);
- surface.release();
+ SurfaceControl.Transaction transaction = mService.mWindowManager.mTransactionFactory.get();
+ transaction.setBuffer(screenshot, graphicBuffer);
+ transaction.setColorSpace(screenshot, buffer.getColorSpace());
+ transaction.apply();
outSurfaceControl.copyFrom(screenshot, "WindowOrganizerController.takeScreenshot");
return true;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 46d923b..1a5042f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1140,7 +1140,7 @@
void attach() {
if (DEBUG) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
- mSession.windowAddedLocked(mAttrs.packageName);
+ mSession.windowAddedLocked();
}
/**
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index a99679a..74fbb42 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -190,7 +190,6 @@
shared_libs: [
"libarcbridge",
"libarcbridgeservice",
- "libarctimer",
"libbase",
"libcap",
"libchrome",
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index f0210ee..db52683 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -171,17 +171,23 @@
return result;
}
-static inline std::vector<char> readBytes(borrowed_fd fd) {
- int32_t size = readLEInt32(fd);
- std::vector<char> result(size);
- android::base::ReadFully(fd, result.data(), size);
- return result;
+static inline bool skipBytes(borrowed_fd fd, int* max_size) {
+ int32_t size = std::min(readLEInt32(fd), *max_size);
+ if (size <= 0) {
+ return false;
+ }
+ *max_size -= size;
+ return (TEMP_FAILURE_RETRY(lseek64(fd.get(), size, SEEK_CUR)) >= 0);
}
static inline int32_t skipIdSigHeaders(borrowed_fd fd) {
- readLEInt32(fd); // version
- readBytes(fd); // hashingInfo
- readBytes(fd); // signingInfo
+ // version
+ auto version = readLEInt32(fd);
+ int max_size = INCFS_MAX_SIGNATURE_SIZE - sizeof(version);
+ // hashingInfo and signingInfo
+ if (!skipBytes(fd, &max_size) || !skipBytes(fd, &max_size)) {
+ return -1;
+ }
return readLEInt32(fd); // size of the verity tree
}
@@ -253,8 +259,12 @@
unique_fd idsigFd = openLocalFile(env, jni, shellCommand, idsigPath);
if (idsigFd.ok()) {
- auto treeSize = verityTreeSizeForFile(size);
auto actualTreeSize = skipIdSigHeaders(idsigFd);
+ if (actualTreeSize < 0) {
+ ALOGE("Error reading .idsig file: wrong format.");
+ return {};
+ }
+ auto treeSize = verityTreeSizeForFile(size);
if (treeSize != actualTreeSize) {
ALOGE("Verity tree size mismatch: %d vs .idsig: %d.", int(treeSize),
int(actualTreeSize));
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ddcb2bf..6283b4e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -96,6 +96,7 @@
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED;
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED;
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_STARTING_PROFILE_FAILED;
+import static android.app.admin.DevicePolicyManager.STATE_USER_UNMANAGED;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -8517,20 +8518,16 @@
+ " as profile owner for user " + userHandle);
return false;
}
- if (who == null
- || !isPackageInstalledForUser(who.getPackageName(), userHandle)) {
- throw new IllegalArgumentException("Component " + who
- + " not installed for userId:" + userHandle);
- }
+ Preconditions.checkArgument(who != null);
final CallerIdentity caller = getCallerIdentity();
synchronized (getLockObject()) {
enforceCanSetProfileOwnerLocked(caller, who, userHandle);
-
+ Preconditions.checkArgument(isPackageInstalledForUser(who.getPackageName(), userHandle),
+ "Component " + who + " not installed for userId:" + userHandle);
final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
- if (admin == null || getUserData(userHandle).mRemovingAdmins.contains(who)) {
- throw new IllegalArgumentException("Not active admin: " + who);
- }
+ Preconditions.checkArgument(admin != null && !getUserData(
+ userHandle).mRemovingAdmins.contains(who), "Not active admin: " + who);
final int parentUserId = getProfileParentId(userHandle);
// When trying to set a profile owner on a new user, it may be that this user is
@@ -8739,7 +8736,8 @@
final CallerIdentity caller = getCallerIdentity();
if (userHandle != mOwners.getDeviceOwnerUserId() && !mOwners.hasProfileOwner(userHandle)
- && getManagedUserId(userHandle) == -1) {
+ && getManagedUserId(userHandle) == -1
+ && newState != STATE_USER_UNMANAGED) {
// No managed device, user or profile, so setting provisioning state makes no sense.
throw new IllegalStateException("Not allowed to change provisioning state unless a "
+ "device or profile owner is set.");
@@ -8802,6 +8800,12 @@
case DevicePolicyManager.STATE_USER_SETUP_FINALIZED:
// Cannot transition out of finalized.
break;
+ case DevicePolicyManager.STATE_USER_PROFILE_FINALIZED:
+ // Should only move to an unmanaged state after removing the work profile.
+ if (newState == DevicePolicyManager.STATE_USER_UNMANAGED) {
+ return;
+ }
+ break;
}
// Didn't meet any of the accepted state transition checks above, throw appropriate error.
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 5140b9f..0bd737b 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -77,6 +77,7 @@
"libcutils",
"libincfs",
"liblog",
+ "libpermission",
"libz",
],
}
diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING
index d935256..6aa8a93 100644
--- a/services/incremental/TEST_MAPPING
+++ b/services/incremental/TEST_MAPPING
@@ -24,7 +24,9 @@
},
{
"name": "CtsIncrementalInstallHostTestCases"
- },
+ }
+ ],
+ "presubmit-large": [
{
"name": "CtsInstalledLoadingProgressHostTests"
}
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 9f24d9a..d913d4e 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -111,7 +111,8 @@
private static final long RECENT_NOTIFICATIONS_MAX_AGE_MS = 10 * DateUtils.DAY_IN_MILLIS;
private static final long QUERY_EVENTS_MAX_AGE_MS = 5L * DateUtils.MINUTE_IN_MILLIS;
private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L;
- @VisibleForTesting static final int MAX_CACHED_RECENT_SHORTCUTS = 30;
+ @VisibleForTesting
+ static final int MAX_CACHED_RECENT_SHORTCUTS = 30;
private final Context mContext;
private final Injector mInjector;
@@ -256,14 +257,23 @@
@Nullable
private ConversationChannel getConversationChannel(String packageName, int userId,
String shortcutId, ConversationInfo conversationInfo) {
+ ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId);
+ return getConversationChannel(shortcutInfo, conversationInfo);
+ }
+
+ @Nullable
+ private ConversationChannel getConversationChannel(ShortcutInfo shortcutInfo,
+ ConversationInfo conversationInfo) {
if (conversationInfo == null) {
return null;
}
- ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId);
if (shortcutInfo == null) {
- Slog.e(TAG, " Shortcut no longer found: " + shortcutId);
+ Slog.e(TAG, " Shortcut no longer found");
return null;
}
+ String packageName = shortcutInfo.getPackage();
+ String shortcutId = shortcutInfo.getId();
+ int userId = shortcutInfo.getUserId();
int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
NotificationChannel parentChannel =
mNotificationManagerInternal.getNotificationChannel(packageName, uid,
@@ -363,7 +373,9 @@
}
}
builder.setStatuses(newStatuses);
- cs.addOrUpdate(builder.build());
+ updateConversationStoreThenNotifyListeners(cs, builder.build(),
+ packageData.getPackageName(),
+ packageData.getUserId());
});
});
}
@@ -397,11 +409,7 @@
ConversationInfo convToModify = getConversationInfoOrThrow(cs, conversationId);
ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify);
builder.addOrUpdateStatus(status);
- ConversationInfo modifiedConv = builder.build();
- cs.addOrUpdate(modifiedConv);
- ConversationChannel conversation = getConversationChannel(packageName, userId,
- conversationId, modifiedConv);
- notifyConversationsListeners(Arrays.asList(conversation));
+ updateConversationStoreThenNotifyListeners(cs, builder.build(), packageName, userId);
if (status.getEndTimeMillis() >= 0) {
mStatusExpReceiver.scheduleExpiration(
@@ -416,7 +424,7 @@
ConversationInfo convToModify = getConversationInfoOrThrow(cs, conversationId);
ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify);
builder.clearStatus(statusId);
- cs.addOrUpdate(builder.build());
+ updateConversationStoreThenNotifyListeners(cs, builder.build(), packageName, userId);
}
public void clearStatuses(String packageName, int userId, String conversationId) {
@@ -424,7 +432,7 @@
ConversationInfo convToModify = getConversationInfoOrThrow(cs, conversationId);
ConversationInfo.Builder builder = new ConversationInfo.Builder(convToModify);
builder.setStatuses(null);
- cs.addOrUpdate(builder.build());
+ updateConversationStoreThenNotifyListeners(cs, builder.build(), packageName, userId);
}
public @NonNull List<ConversationStatus> getStatuses(String packageName, int userId,
@@ -882,7 +890,8 @@
}
}
}
- conversationStore.addOrUpdate(builder.build());
+ updateConversationStoreThenNotifyListeners(conversationStore, builder.build(),
+ shortcutInfo);
}
@VisibleForTesting
@@ -945,6 +954,7 @@
conversationSelector.mConversationStore =
packageData.getConversationStore();
conversationSelector.mConversationInfo = ci;
+ conversationSelector.mPackageName = packageData.getPackageName();
}
});
if (conversationSelector.mConversationInfo == null) {
@@ -955,13 +965,16 @@
new ConversationInfo.Builder(conversationSelector.mConversationInfo);
builder.setContactStarred(helper.isStarred());
builder.setContactPhoneNumber(helper.getPhoneNumber());
- conversationSelector.mConversationStore.addOrUpdate(builder.build());
+ updateConversationStoreThenNotifyListeners(conversationSelector.mConversationStore,
+ builder.build(),
+ conversationSelector.mPackageName, userId);
mLastUpdatedTimestamp = helper.getLastUpdatedTimestamp();
}
private class ConversationSelector {
private ConversationStore mConversationStore = null;
private ConversationInfo mConversationInfo = null;
+ private String mPackageName = null;
}
}
@@ -1140,6 +1153,7 @@
.setLastEventTimestamp(sbn.getPostTime())
.setParentNotificationChannelId(sbn.getNotification().getChannelId())
.build();
+ // Don't update listeners on notifications posted.
packageData.getConversationStore().addOrUpdate(updated);
EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
@@ -1215,7 +1229,8 @@
builder.setBubbled(false);
break;
}
- conversationStore.addOrUpdate(builder.build());
+ updateConversationStoreThenNotifyListeners(conversationStore, builder.build(), pkg,
+ packageData.getUserId());
}
synchronized boolean hasActiveNotifications(String packageName, String shortcutId) {
@@ -1253,7 +1268,9 @@
ConversationInfo updated = new ConversationInfo.Builder(conversationInfo)
.setLastEventTimestamp(event.getTimestamp())
.build();
- packageData.getConversationStore().addOrUpdate(updated);
+ updateConversationStoreThenNotifyListeners(packageData.getConversationStore(),
+ updated,
+ packageData.getPackageName(), packageData.getUserId());
}
}
}
@@ -1266,7 +1283,27 @@
}
}
- // TODO(b/178792356): Trigger ConversationsListener on all-related data changes.
+ private void updateConversationStoreThenNotifyListeners(ConversationStore cs,
+ ConversationInfo modifiedConv,
+ String packageName, int userId) {
+ cs.addOrUpdate(modifiedConv);
+ ConversationChannel channel = getConversationChannel(packageName, userId,
+ modifiedConv.getShortcutId(), modifiedConv);
+ if (channel != null) {
+ notifyConversationsListeners(Arrays.asList(channel));
+ }
+ }
+
+ private void updateConversationStoreThenNotifyListeners(ConversationStore cs,
+ ConversationInfo modifiedConv, ShortcutInfo shortcutInfo) {
+ cs.addOrUpdate(modifiedConv);
+ ConversationChannel channel = getConversationChannel(shortcutInfo, modifiedConv);
+ if (channel != null) {
+ notifyConversationsListeners(Arrays.asList(channel));
+ }
+ }
+
+
@VisibleForTesting
void notifyConversationsListeners(
@Nullable final List<ConversationChannel> changedConversations) {
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index b136d00..83677c2 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -36,6 +36,10 @@
],
test_suites: ["general-tests"],
java_resources: [
+ ":PackageManagerTestOverlayActor",
+ ":PackageManagerTestOverlay",
+ ":PackageManagerTestOverlayTarget",
+ ":PackageManagerTestOverlayTargetNoOverlayable",
":PackageManagerTestAppDeclaresStaticLibrary",
":PackageManagerTestAppStub",
":PackageManagerTestAppUsesStaticLibrary",
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OverlayActorVisibilityTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OverlayActorVisibilityTest.kt
new file mode 100644
index 0000000..558d01ed
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OverlayActorVisibilityTest.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test
+
+import com.android.internal.util.test.SystemPreparer
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.After
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import java.io.File
+
+@RunWith(DeviceJUnit4ClassRunner::class)
+class OverlayActorVisibilityTest : BaseHostJUnit4Test() {
+
+ companion object {
+ private const val ACTOR_PKG_NAME = "com.android.server.pm.test.overlay.actor"
+ private const val ACTOR_APK = "PackageManagerTestOverlayActor.apk"
+ private const val TARGET_APK = "PackageManagerTestOverlayTarget.apk"
+ private const val OVERLAY_APK = "PackageManagerTestOverlay.apk"
+ private const val TARGET_NO_OVERLAYABLE_APK =
+ "PackageManagerTestOverlayTargetNoOverlayable.apk"
+
+ @get:ClassRule
+ val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
+ }
+
+ @get:Rule
+ val tempFolder = TemporaryFolder()
+
+ private val preparer: SystemPreparer = SystemPreparer(
+ tempFolder,
+ SystemPreparer.RebootStrategy.FULL,
+ deviceRebootRule
+ ) { this.device }
+
+ private val namedActorFile = File(
+ "/system/etc/sysconfig/com.android.server.pm.test.OverlayActorVisibilityTest.xml"
+ )
+
+ @Before
+ @After
+ fun uninstallPackages() {
+ device.uninstallPackages(ACTOR_APK, TARGET_APK, OVERLAY_APK)
+ }
+
+ @Before
+ fun pushSysConfigFile() {
+ // In order for the test app to be the verification agent, it needs a permission file
+ // which can be pushed onto the system and removed afterwards.
+ // language=XML
+ val file = tempFolder.newFile().apply {
+ """
+ <config>
+ <named-actor
+ namespace="androidTest"
+ name="OverlayActorVisibilityTest"
+ package="$ACTOR_PKG_NAME"
+ />
+ </config>
+ """
+ .trimIndent()
+ .let { writeText(it) }
+ }
+
+ preparer.pushFile(file, namedActorFile.toString())
+ .reboot()
+ }
+
+ @After
+ fun deleteSysConfigFile() {
+ preparer.deleteFile(namedActorFile.toString())
+ .reboot()
+ }
+
+ @Test
+ fun testVisibilityByOverlayable() {
+ assertThat(device.installJavaResourceApk(tempFolder, ACTOR_APK, false)).isNull()
+ assertThat(device.installJavaResourceApk(tempFolder, OVERLAY_APK, false)).isNull()
+ assertThat(device.installJavaResourceApk(tempFolder, TARGET_NO_OVERLAYABLE_APK, false))
+ .isNull()
+
+ runDeviceTests(
+ ACTOR_PKG_NAME, "$ACTOR_PKG_NAME.OverlayableVisibilityTest",
+ "verifyNotVisible"
+ )
+
+ assertThat(device.installJavaResourceApk(tempFolder, TARGET_APK, true)).isNull()
+
+ assertWithMessage(device.executeShellCommand("dumpsys package $OVERLAY_APK"))
+
+ runDeviceTests(
+ ACTOR_PKG_NAME, "$ACTOR_PKG_NAME.OverlayableVisibilityTest",
+ "verifyVisible"
+ )
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/Android.bp
new file mode 100644
index 0000000..92dcd34
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestOverlay",
+ srcs: [
+ "src/**/*.kt",
+ ],
+}
diff --git a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml b/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/AndroidManifest.xml
similarity index 64%
copy from packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
copy to services/tests/PackageManagerServiceTests/host/test-apps/Overlay/AndroidManifest.xml
index b8ea622..21e4432 100644
--- a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/AndroidManifest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
+<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,10 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
- <solid
- android:color="?android:attr/textColorSecondary" />
- <corners android:radius="2dp" />
-</shape>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.overlay">
+ <application/>
+ <overlay android:targetPackage="com.android.server.pm.test.overlay.target"
+ android:targetName="Testing"/>
+</manifest>
diff --git a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml b/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/res/values/values.xml
similarity index 68%
copy from packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
copy to services/tests/PackageManagerServiceTests/host/test-apps/Overlay/res/values/values.xml
index b8ea622..f0b8586 100644
--- a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/res/values/values.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
+<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,10 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
- <solid
- android:color="?android:attr/textColorSecondary" />
- <corners android:radius="2dp" />
-</shape>
+
+<resources>
+ <string name="policy_public">You have been overlaid</string>
+</resources>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
new file mode 100644
index 0000000..5718474
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestOverlayActor",
+ srcs: ["src/**/*.kt"],
+ static_libs: [
+ "androidx.test.runner",
+ "junit",
+ "kotlin-test",
+ "truth-prebuilt",
+ ],
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/AndroidManifest.xml
new file mode 100644
index 0000000..a92a14f
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.overlay.actor"
+ >
+
+ <application/>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.pm.test.overlay.actor"
+ />
+
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/src/com/android/server/pm/test/overlay/actor/OverlayableVisibilityTest.kt b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/src/com/android/server/pm/test/overlay/actor/OverlayableVisibilityTest.kt
new file mode 100644
index 0000000..7537247f
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/src/com/android/server/pm/test/overlay/actor/OverlayableVisibilityTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.overlay.actor
+
+import android.content.Context
+import android.content.pm.PackageManager
+import androidx.test.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import kotlin.test.assertFailsWith
+
+class OverlayableVisibilityTest {
+
+ companion object {
+ private const val TARGET_PACKAGE = "com.android.server.pm.test.overlay.target"
+ private const val OVERLAY_PACKAGE = "com.android.server.pm.test.overlay"
+ }
+
+ private val context: Context = InstrumentationRegistry.getContext()
+ private val packageManager = context.packageManager
+
+ @Test
+ fun verifyVisible() {
+ assertThat(packageManager.getApplicationInfo(TARGET_PACKAGE, 0)).isNotNull()
+ assertThat(packageManager.getApplicationInfo(OVERLAY_PACKAGE, 0)).isNotNull()
+ }
+
+ @Test
+ fun verifyNotVisible() {
+ assertFailsWith(PackageManager.NameNotFoundException::class) {
+ packageManager.getApplicationInfo(TARGET_PACKAGE, 0)
+ }
+ assertFailsWith(PackageManager.NameNotFoundException::class) {
+ packageManager.getApplicationInfo(OVERLAY_PACKAGE, 0)
+ }
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/Android.bp
new file mode 100644
index 0000000..2bb6b82
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestOverlayTarget",
+ defaults: ["cts_support_defaults"],
+ sdk_version: "current",
+ resource_dirs: [
+ "res",
+ "res_overlayable",
+ ],
+}
+
+android_test_helper_app {
+ name: "PackageManagerTestOverlayTargetNoOverlayable",
+ defaults: ["cts_support_defaults"],
+ sdk_version: "current",
+ resource_dirs: [
+ "res",
+ ],
+}
diff --git a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/AndroidManifest.xml
similarity index 68%
copy from packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
copy to services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/AndroidManifest.xml
index b8ea622..6038cb1 100644
--- a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/AndroidManifest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
+<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,10 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
- <solid
- android:color="?android:attr/textColorSecondary" />
- <corners android:radius="2dp" />
-</shape>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.overlay.target">
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/assets/asset.txt b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/assets/asset.txt
new file mode 100644
index 0000000..4625e3b
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/assets/asset.txt
@@ -0,0 +1 @@
+Not overlaid
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/res/values/public.xml
similarity index 68%
copy from packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
copy to services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/res/values/public.xml
index b8ea622..283f5c1 100644
--- a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/res/values/public.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
+<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,10 +13,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
- <solid
- android:color="?android:attr/textColorSecondary" />
- <corners android:radius="2dp" />
-</shape>
+
+<resources>
+ <public-group type="string" first-id="0x7f010000">
+ <public name="policy_public" />
+ </public-group>
+</resources>
diff --git a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/res/values/values.xml
similarity index 68%
copy from packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
copy to services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/res/values/values.xml
index b8ea622..822194f 100644
--- a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/res/values/values.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
+<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,10 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
- <solid
- android:color="?android:attr/textColorSecondary" />
- <corners android:radius="2dp" />
-</shape>
+
+<resources>
+ <string name="policy_public">Not overlaid</string>
+</resources>
diff --git a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/res_overlayable/values/overlayable.xml
similarity index 68%
rename from packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
rename to services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/res_overlayable/values/overlayable.xml
index b8ea622..0100389 100644
--- a/packages/SystemUI/res/drawable/qs_footer_drag_handle.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/res_overlayable/values/overlayable.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
+<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,10 +13,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
- <solid
- android:color="?android:attr/textColorSecondary" />
- <corners android:radius="2dp" />
-</shape>
+
+<resources>
+ <overlayable name="Testing" actor="overlay://androidTest/OverlayActorVisibilityTest">
+ <policy type="public">
+ <item type="string" name="policy_public" />
+ </policy>
+ </overlayable>
+</resources>
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
index dce853a..4de8d52 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
@@ -235,9 +235,23 @@
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="https"/>
- <data android:path="/sub5"/>
- <data android:host="example5.com"/>
- <data android:host="invalid5"/>
+ <data android:path="/sub6"/>
+ <data android:host="example6.com"/>
+ <data android:host="invalid6"/>
+ </intent-filter>
+ <intent-filter android:autoVerify="$autoVerify">
+ <category android:name="android.intent.category.BROWSABLE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="example7.com"/>
+ <intent-filter android:autoVerify="$autoVerify">
+ <category android:name="android.intent.category.BROWSABLE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="https"/>
+ </intent-filter>
+ <intent-filter android:autoVerify="$autoVerify">
+ <category android:name="android.intent.category.BROWSABLE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:path="/sub7"/>
</intent-filter>
</xml>
""".trimIndent()
@@ -324,6 +338,30 @@
addDataAuthority("invalid6", null)
}
)
+ addIntent(
+ ParsedIntentInfo().apply {
+ setAutoVerify(autoVerify)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataAuthority("example7.com", null)
+ }
+ )
+ addIntent(
+ ParsedIntentInfo().apply {
+ setAutoVerify(autoVerify)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("https")
+ }
+ )
+ addIntent(
+ ParsedIntentInfo().apply {
+ setAutoVerify(autoVerify)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataPath("/sub7", PatternMatcher.PATTERN_LITERAL)
+ }
+ )
},
)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 2d852e5..886b2e0 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -338,6 +338,7 @@
whenever(readUserState(0)) { PackageUserState() }
whenever(readUserState(1)) { PackageUserState() }
whenever(getInstantApp(anyInt())) { false }
+ whenever(isSystem()) { false }
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java
index 14c02d5..0a5a3bf 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java
@@ -20,11 +20,14 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.PackageManager;
+import android.content.pm.verify.domain.DomainOwner;
import android.content.pm.verify.domain.DomainVerificationManager;
import com.android.server.pm.verify.domain.DomainVerificationService;
+import java.util.List;
import java.util.Set;
+import java.util.SortedSet;
import java.util.UUID;
/**
@@ -58,4 +61,14 @@
throws PackageManager.NameNotFoundException {
return manager.setDomainVerificationUserSelection(domainSetId, domains, enabled);
}
+
+ static SortedSet<DomainOwner> getOwnersForDomain(@NonNull DomainVerificationManager manager,
+ @Nullable String domain) {
+ return manager.getOwnersForDomain(domain);
+ }
+
+ static List<DomainOwner> getOwnersForDomain(@NonNull DomainVerificationService service,
+ @Nullable String domain, @UserIdInt int userId) {
+ return service.getOwnersForDomain(domain, userId);
+ }
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index 7fea4ee..0fe3913 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -292,8 +292,17 @@
val manager0 = makeManager(service, 0)
val manager1 = makeManager(service, 1)
- assertThat(service.getOwnersForDomain(DOMAIN_1, 0)).isEmpty()
- assertThat(manager0.getOwnersForDomain(DOMAIN_1)).isEmpty()
+ listOf(DOMAIN_1, "").forEach {
+ assertThat(service.getOwnersForDomain(it, 0)).isEmpty()
+ assertThat(manager0.getOwnersForDomain(it)).isEmpty()
+ }
+
+ assertFailsWith(NullPointerException::class) {
+ DomainVerificationJavaUtil.getOwnersForDomain(service, null, 0)
+ }
+ assertFailsWith(NullPointerException::class) {
+ DomainVerificationJavaUtil.getOwnersForDomain(manager0, null)
+ }
assertThat(
service.setStatus(
@@ -518,6 +527,7 @@
whenever(firstInstallTime) { 0L }
whenever(readUserState(0)) { pkgUserState0() }
whenever(readUserState(1)) { pkgUserState1() }
+ whenever(isSystem()) { false }
}
private fun DomainVerificationService.addPackages(vararg pkgSettings: PackageSetting) =
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index 1097c45..8540b8a 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -101,6 +101,70 @@
}
@Test
+ fun addPackageSystemConfigured() {
+ val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, SIGNATURE_ONE, isSystemApp = false)
+ val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, SIGNATURE_TWO, isSystemApp = true)
+
+ val service = makeService(
+ systemConfiguredPackageNames = ArraySet(setOf(pkg1.getName(), pkg2.getName())),
+ pkg1, pkg2
+ )
+ service.addPackage(pkg1)
+ service.addPackage(pkg2)
+
+ service.getInfo(pkg1.getName()).apply {
+ assertThat(packageName).isEqualTo(pkg1.getName())
+ assertThat(identifier).isEqualTo(pkg1.domainSetId)
+ assertThat(hostToStateMap).containsExactlyEntriesIn(
+ mapOf(
+ DOMAIN_1 to STATE_NO_RESPONSE,
+ DOMAIN_2 to STATE_NO_RESPONSE,
+ )
+ )
+ }
+
+ service.getUserState(pkg1.getName()).apply {
+ assertThat(packageName).isEqualTo(pkg1.getName())
+ assertThat(identifier).isEqualTo(pkg1.domainSetId)
+ assertThat(isLinkHandlingAllowed).isEqualTo(true)
+ assertThat(user.identifier).isEqualTo(USER_ID)
+ assertThat(hostToStateMap).containsExactlyEntriesIn(
+ mapOf(
+ DOMAIN_1 to DOMAIN_STATE_NONE,
+ DOMAIN_2 to DOMAIN_STATE_NONE,
+ )
+ )
+ }
+
+ service.getInfo(pkg2.getName()).apply {
+ assertThat(packageName).isEqualTo(pkg2.getName())
+ assertThat(identifier).isEqualTo(pkg2.domainSetId)
+ assertThat(hostToStateMap).containsExactlyEntriesIn(
+ mapOf(
+ DOMAIN_1 to STATE_UNMODIFIABLE,
+ DOMAIN_2 to STATE_UNMODIFIABLE,
+ )
+ )
+ }
+
+ service.getUserState(pkg2.getName()).apply {
+ assertThat(packageName).isEqualTo(pkg2.getName())
+ assertThat(identifier).isEqualTo(pkg2.domainSetId)
+ assertThat(isLinkHandlingAllowed).isEqualTo(true)
+ assertThat(user.identifier).isEqualTo(USER_ID)
+ assertThat(hostToStateMap).containsExactlyEntriesIn(
+ mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_VERIFIED,
+ )
+ )
+ }
+
+ assertThat(service.queryValidVerificationPackageNames())
+ .containsExactly(pkg1.getName(), pkg2.getName())
+ }
+
+ @Test
fun addPackageRestoredMatchingSignature() {
// language=XML
val xml = """
@@ -457,42 +521,52 @@
getDomainVerificationUserState(pkgName, USER_ID)
.also { assertThat(it).isNotNull() }!!
+ private fun makeService(
+ systemConfiguredPackageNames: ArraySet<String> = ArraySet(),
+ vararg pkgSettings: PackageSetting
+ ) = makeService(systemConfiguredPackageNames = systemConfiguredPackageNames) {
+ pkgName -> pkgSettings.find { pkgName == it.getName() }
+ }
+
private fun makeService(vararg pkgSettings: PackageSetting) =
- makeService { pkgName -> pkgSettings.find { pkgName == it.getName()} }
+ makeService { pkgName -> pkgSettings.find { pkgName == it.getName() } }
- private fun makeService(pkgSettingFunction: (String) -> PackageSetting? = { null }) =
- DomainVerificationService(mockThrowOnUnmocked {
- // Assume the test has every permission necessary
- whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
- whenever(checkPermission(anyString(), anyInt(), anyInt())) {
- PackageManager.PERMISSION_GRANTED
- }
- }, mockThrowOnUnmocked {
- whenever(linkedApps) { ArraySet<String>() }
- }, mockThrowOnUnmocked {
- whenever(isChangeEnabledInternalNoLogging(anyLong(), any())) { true }
- }).apply {
- setConnection(mockThrowOnUnmocked {
- whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
- whenever(doesUserExist(0)) { true }
- whenever(doesUserExist(1)) { true }
- whenever(scheduleWriteSettings())
-
- // Need to provide an internal UID so some permission checks are ignored
- whenever(callingUid) { Process.ROOT_UID }
- whenever(callingUserId) { 0 }
-
- mockPackageSettings {
- pkgSettingFunction(it)
- }
- })
+ private fun makeService(
+ systemConfiguredPackageNames: ArraySet<String> = ArraySet(),
+ pkgSettingFunction: (String) -> PackageSetting? = { null }
+ ) = DomainVerificationService(mockThrowOnUnmocked {
+ // Assume the test has every permission necessary
+ whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
+ whenever(checkPermission(anyString(), anyInt(), anyInt())) {
+ PackageManager.PERMISSION_GRANTED
}
+ }, mockThrowOnUnmocked {
+ whenever(this.linkedApps) { systemConfiguredPackageNames }
+ }, mockThrowOnUnmocked {
+ whenever(isChangeEnabledInternalNoLogging(anyLong(), any())) { true }
+ }).apply {
+ setConnection(mockThrowOnUnmocked {
+ whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+ whenever(doesUserExist(0)) { true }
+ whenever(doesUserExist(1)) { true }
+ whenever(scheduleWriteSettings())
+
+ // Need to provide an internal UID so some permission checks are ignored
+ whenever(callingUid) { Process.ROOT_UID }
+ whenever(callingUserId) { 0 }
+
+ mockPackageSettings {
+ pkgSettingFunction(it)
+ }
+ })
+ }
private fun mockPkgSetting(
pkgName: String,
domainSetId: UUID,
signature: String,
- domains: List<String> = listOf(DOMAIN_1, DOMAIN_2)
+ domains: List<String> = listOf(DOMAIN_1, DOMAIN_2),
+ isSystemApp: Boolean = false
) = mockThrowOnUnmocked<PackageSetting> {
val pkg = mockThrowOnUnmocked<AndroidPackage> {
whenever(packageName) { pkgName }
@@ -528,5 +602,6 @@
whenever(firstInstallTime) { 0L }
whenever(readUserState(USER_ID)) { PackageUserState() }
whenever(signatures) { arrayOf(Signature(signature)) }
+ whenever(isSystem) { isSystemApp }
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 26e8d28..6a75795 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -238,6 +238,7 @@
whenever(readUserState(0)) { PackageUserState() }
whenever(readUserState(10)) { PackageUserState() }
whenever(getInstantApp(anyInt())) { false }
+ whenever(isSystem()) { false }
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index 52ae7a5..3e2853c 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -145,6 +145,7 @@
whenever(firstInstallTime) { 0L }
whenever(readUserState(0)) { PackageUserState() }
whenever(readUserState(1)) { PackageUserState() }
+ whenever(isSystem()) { false }
}
@Test
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationValidIntentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationValidIntentTest.kt
new file mode 100644
index 0000000..98634b2
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationValidIntentTest.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri
+import com.android.server.pm.verify.domain.DomainVerificationUtils
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class DomainVerificationValidIntentTest {
+
+ companion object {
+
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun parameters(): Array<Params> {
+ val succeeding = mutableListOf<Params>()
+ val failing = mutableListOf<Params>()
+
+ // Start with the base intent
+ val base = Params(categorySet = emptySet()).also { succeeding += it }
+
+ // Add all explicit supported categorySet
+ succeeding += base.copy(
+ categorySet = setOf(Intent.CATEGORY_BROWSABLE),
+ matchDefaultOnly = true
+ )
+
+ failing += base.copy(
+ categorySet = setOf(Intent.CATEGORY_BROWSABLE),
+ matchDefaultOnly = false
+ )
+
+ succeeding += listOf(true, false).map {
+ base.copy(
+ categorySet = setOf(Intent.CATEGORY_DEFAULT),
+ matchDefaultOnly = it
+ )
+ }
+
+ succeeding += listOf(true, false).map {
+ base.copy(
+ categorySet = setOf(Intent.CATEGORY_BROWSABLE, Intent.CATEGORY_DEFAULT),
+ matchDefaultOnly = it
+ )
+ }
+
+ // Fail on unsupported category
+ failing += listOf(
+ emptySet(),
+ setOf(Intent.CATEGORY_BROWSABLE),
+ setOf(Intent.CATEGORY_DEFAULT),
+ setOf(Intent.CATEGORY_BROWSABLE, Intent.CATEGORY_DEFAULT)
+ ).map { base.copy(categorySet = it + "invalid.CATEGORY") }
+
+ // Fail on unsupported action
+ failing += base.copy(action = Intent.ACTION_SEND)
+
+ // Fail on unsupported domain
+ failing += base.copy(domain = "invalid")
+
+ // Fail on empty domains
+ failing += base.copy(domain = "")
+
+ // Fail on missing scheme
+ failing += base.copy(
+ uriFunction = { Uri.Builder().authority("test.com").build() }
+ )
+
+ // Fail on missing host
+ failing += base.copy(
+ domain = "",
+ uriFunction = { Uri.Builder().scheme("https").build() }
+ )
+
+ succeeding.forEach { it.expected = true }
+ failing.forEach { it.expected = false }
+ return (succeeding + failing).toTypedArray()
+ }
+
+ data class Params(
+ val action: String = Intent.ACTION_VIEW,
+ val categorySet: Set<String> = mutableSetOf(),
+ val domain: String = "test.com",
+ val matchDefaultOnly: Boolean = true,
+ var expected: Boolean? = null,
+ val uriFunction: (domain: String) -> Uri = { Uri.parse("https://$it") }
+ ) {
+ val intent = Intent(action, uriFunction(domain)).apply {
+ categorySet.forEach(::addCategory)
+ }
+
+ override fun toString() = intent.toShortString(false, false, false, false) +
+ ", matchDefaultOnly = $matchDefaultOnly, expected = $expected"
+ }
+ }
+
+ @Parameterized.Parameter(0)
+ lateinit var params: Params
+
+ @Test
+ fun verify() {
+ val flags = if (params.matchDefaultOnly) PackageManager.MATCH_DEFAULT_ONLY else 0
+ assertThat(DomainVerificationUtils.isDomainVerificationIntent(params.intent, flags))
+ .isEqualTo(params.expected)
+ }
+}
diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
index e4b650c..17a5dcc 100644
--- a/services/tests/mockingservicestests/AndroidManifest.xml
+++ b/services/tests/mockingservicestests/AndroidManifest.xml
@@ -28,6 +28,8 @@
<uses-permission android:name="android.permission.MANAGE_APPOPS"/>
<uses-permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"/>
<uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>
+ <uses-permission
+ android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
<!-- needed by MasterClearReceiverTest to display a system dialog -->
<uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index edfc21d..d55bbd1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -468,8 +468,9 @@
TEST_CALLING_UID);
}
- private void setPrioritizedAlarm(int type, long triggerTime, IAlarmListener listener) {
- mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test",
+ private void setPrioritizedAlarm(int type, long triggerTime, long windowLength,
+ IAlarmListener listener) {
+ mService.setImpl(type, triggerTime, windowLength, 0, null, listener, "test",
FLAG_STANDALONE | FLAG_PRIORITIZE, null, null, TEST_CALLING_UID,
TEST_CALLING_PACKAGE, null);
}
@@ -1685,7 +1686,7 @@
final int numAlarms = 10;
for (int i = 0; i < numAlarms; i++) {
setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
- new IAlarmListener.Stub() {
+ 0, new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback)
throws RemoteException {
@@ -1720,7 +1721,7 @@
final int numAlarms = 10;
for (int i = 0; i < numAlarms; i++) {
setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
- new IAlarmListener.Stub() {
+ 0, new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback)
throws RemoteException {
@@ -1738,12 +1739,12 @@
}
assertEquals(numAlarms, alarmsFired.get());
- setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 3, new IAlarmListener.Stub() {
+ setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 3, 0, new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
}
});
- setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 2, new IAlarmListener.Stub() {
+ setPrioritizedAlarm(ELAPSED_REALTIME_WAKEUP, idleUntil - 2, 0, new IAlarmListener.Stub() {
@Override
public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
}
@@ -2223,7 +2224,11 @@
}
@Test
- public void minWindow() {
+ public void minWindowChangeEnabled() {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(
+ eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS),
+ anyString(), any(UserHandle.class)));
final long minWindow = 73;
setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
@@ -2239,6 +2244,48 @@
}
@Test
+ public void minWindowChangeDisabled() {
+ doReturn(false).when(
+ () -> CompatChanges.isChangeEnabled(
+ eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS),
+ anyString(), any(UserHandle.class)));
+ final long minWindow = 73;
+ setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
+
+ // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
+ for (int window = 1; window <= minWindow; window++) {
+ final PendingIntent pi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, 0, window, pi, 0, 0, TEST_CALLING_UID, null);
+
+ assertEquals(1, mService.mAlarmStore.size());
+ final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
+ assertEquals(window, a.windowLength);
+ }
+ }
+
+ @Test
+ public void minWindowPriorityAlarm() {
+ doReturn(true).when(
+ () -> CompatChanges.isChangeEnabled(
+ eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS),
+ anyString(), any(UserHandle.class)));
+ final long minWindow = 73;
+ setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
+
+ // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
+ for (int window = 1; window <= minWindow; window++) {
+ setPrioritizedAlarm(ELAPSED_REALTIME, 0, window, new IAlarmListener.Stub() {
+ @Override
+ public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
+ }
+ });
+ assertEquals(1, mService.mAlarmStore.size());
+ final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
+ assertEquals(window, a.windowLength);
+ }
+ }
+
+ @Test
public void denyListPackagesAdded() {
mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{"p1", "p2", "p3"});
setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, "p2,p4,p5");
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
new file mode 100644
index 0000000..a8d8a90
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -0,0 +1,488 @@
+/*
+ * 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.app;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.GameManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.function.Supplier;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class GameManagerServiceTests {
+ @Mock MockContext mMockContext;
+ private static final String TAG = "GameServiceTests";
+ private static final String PACKAGE_NAME_INVALID = "com.android.app";
+ private static final int USER_ID_1 = 1001;
+ private static final int USER_ID_2 = 1002;
+
+ private MockitoSession mMockingSession;
+ private String mPackageName;
+ @Mock
+ private PackageManager mMockPackageManager;
+
+ // Stolen from ConnectivityServiceTest.MockContext
+ class MockContext extends ContextWrapper {
+ private static final String TAG = "MockContext";
+
+ // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
+ private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
+
+ MockContext(Context base) {
+ super(base);
+ }
+
+ /**
+ * Mock checks for the specified permission, and have them behave as per {@code granted}.
+ *
+ * <p>Passing null reverts to default behavior, which does a real permission check on the
+ * test package.
+ *
+ * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
+ * {@link PackageManager#PERMISSION_DENIED}.
+ */
+ public void setPermission(String permission, Integer granted) {
+ mMockedPermissions.put(permission, granted);
+ }
+
+ private int checkMockedPermission(String permission, Supplier<Integer> ifAbsent) {
+ final Integer granted = mMockedPermissions.get(permission);
+ return granted != null ? granted : ifAbsent.get();
+ }
+
+ @Override
+ public int checkPermission(String permission, int pid, int uid) {
+ return checkMockedPermission(
+ permission, () -> super.checkPermission(permission, pid, uid));
+ }
+
+ @Override
+ public int checkCallingOrSelfPermission(String permission) {
+ return checkMockedPermission(
+ permission, () -> super.checkCallingOrSelfPermission(permission));
+ }
+
+ @Override
+ public void enforceCallingOrSelfPermission(String permission, String message) {
+ final Integer granted = mMockedPermissions.get(permission);
+ if (granted == null) {
+ super.enforceCallingOrSelfPermission(permission, message);
+ return;
+ }
+
+ if (!granted.equals(PackageManager.PERMISSION_GRANTED)) {
+ throw new SecurityException("[Test] permission denied: " + permission);
+ }
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mMockPackageManager;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(DeviceConfig.class)
+ .strictness(Strictness.WARN)
+ .startMocking();
+ mMockContext = new MockContext(InstrumentationRegistry.getContext());
+ mPackageName = mMockContext.getPackageName();
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.category = ApplicationInfo.CATEGORY_GAME;
+ final PackageInfo pi = new PackageInfo();
+ pi.packageName = mPackageName;
+ final List<PackageInfo> packages = new ArrayList<>();
+ packages.add(pi);
+ when(mMockPackageManager.getInstalledPackages(anyInt())).thenReturn(packages);
+ when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(applicationInfo);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ private void mockModifyGameModeGranted() {
+ mMockContext.setPermission(Manifest.permission.MANAGE_GAME_MODE,
+ PackageManager.PERMISSION_GRANTED);
+ }
+
+ private void mockModifyGameModeDenied() {
+ mMockContext.setPermission(Manifest.permission.MANAGE_GAME_MODE,
+ PackageManager.PERMISSION_DENIED);
+ }
+
+ private void mockDeviceConfigDefault() {
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
+ DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, "").build();
+ when(DeviceConfig.getProperties(anyString(), anyString()))
+ .thenReturn(properties);
+ }
+
+ private void mockDeviceConfigNone() {
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
+ DeviceConfig.NAMESPACE_GAME_OVERLAY).build();
+ when(DeviceConfig.getProperties(anyString(), anyString()))
+ .thenReturn(properties);
+ }
+
+ private void mockDeviceConfigPerformance() {
+ String configString = "mode=2,downscaleFactor=0.5";
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
+ DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
+ when(DeviceConfig.getProperties(anyString(), anyString()))
+ .thenReturn(properties);
+ }
+
+ private void mockDeviceConfigBattery() {
+ String configString = "mode=3,downscaleFactor=0.7";
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
+ DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
+ when(DeviceConfig.getProperties(anyString(), anyString()))
+ .thenReturn(properties);
+ }
+
+ private void mockDeviceConfigAll() {
+ String configString = "mode=3,downscaleFactor=0.7:mode=2,downscaleFactor=0.5";
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
+ DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
+ when(DeviceConfig.getProperties(anyString(), anyString()))
+ .thenReturn(properties);
+ }
+
+ private void mockDeviceConfigInvalid() {
+ String configString = "mode=2,downscaleFactor=0.55";
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
+ DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
+ when(DeviceConfig.getProperties(anyString(), anyString()))
+ .thenReturn(properties);
+ }
+
+ private void mockDeviceConfigMalformed() {
+ String configString = "adsljckv=nin3rn9hn1231245:8795tq=21ewuydg";
+ DeviceConfig.Properties properties = new DeviceConfig.Properties.Builder(
+ DeviceConfig.NAMESPACE_GAME_OVERLAY).setString(mPackageName, configString).build();
+ when(DeviceConfig.getProperties(anyString(), anyString()))
+ .thenReturn(properties);
+ }
+
+ /**
+ * By default game mode is not supported.
+ */
+ @Test
+ public void testGameModeDefaultValue() {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+
+ mockModifyGameModeGranted();
+
+ assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+ }
+
+ /**
+ * Test the default behaviour for a nonexistent user.
+ */
+ @Test
+ public void testDefaultValueForNonexistentUser() {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+
+ mockModifyGameModeGranted();
+
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_2);
+ assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
+ gameManagerService.getGameMode(mPackageName, USER_ID_2));
+ }
+
+ /**
+ * Test getter and setter of game modes.
+ */
+ @Test
+ public void testGameMode() {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+
+ mockModifyGameModeGranted();
+
+ assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE,
+ USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+ }
+
+ /**
+ * Test permission.MANAGE_GAME_MODE is checked
+ */
+ @Test
+ public void testGetGameModeInvalidPackageName() {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ try {
+ assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
+ gameManagerService.getGameMode(PACKAGE_NAME_INVALID,
+ USER_ID_1));
+
+ fail("GameManagerService failed to generate SecurityException when "
+ + "permission.MANAGE_GAME_MODE is not granted.");
+ } catch (SecurityException ignored) {
+ }
+
+ // The test should throw an exception, so the test is passing if we get here.
+ }
+
+ /**
+ * Test permission.MANAGE_GAME_MODE is checked
+ */
+ @Test
+ public void testSetGameModePermissionDenied() {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+
+ // Update the game mode so we can read back something valid.
+ mockModifyGameModeGranted();
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+
+ // Deny permission.MANAGE_GAME_MODE and verify the game mode is not updated.
+ mockModifyGameModeDenied();
+ try {
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE,
+ USER_ID_1);
+
+ fail("GameManagerService failed to generate SecurityException when "
+ + "permission.MANAGE_GAME_MODE is denied.");
+ } catch (SecurityException ignored) {
+ }
+
+ // The test should throw an exception, so the test is passing if we get here.
+ mockModifyGameModeGranted();
+ // Verify that the Game Mode value wasn't updated.
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+ }
+
+ /**
+ * Test game modes are user-specific.
+ */
+ @Test
+ public void testGameModeMultipleUsers() {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ gameManagerService.onUserStarting(USER_ID_2);
+
+ mockModifyGameModeGranted();
+
+ // Set User 1 to Standard
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+
+ // Set User 2 to Performance and verify User 1 is still Standard
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE,
+ USER_ID_2);
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE,
+ gameManagerService.getGameMode(mPackageName, USER_ID_2));
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+
+ // Set User 1 to Battery and verify User 2 is still Performance
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY,
+ USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_BATTERY,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE,
+ gameManagerService.getGameMode(mPackageName, USER_ID_2));
+ }
+
+ /**
+ * Phonesky device config exists, but is only propagating the default value.
+ */
+ @Test
+ public void testDeviceConfigDefault() {
+ mockDeviceConfigDefault();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ gameManagerService.loadDeviceConfigLocked();
+
+ int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
+ assertEquals(modes.length, 1);
+ assertEquals(modes[0], GameManager.GAME_MODE_UNSUPPORTED);
+ }
+
+ /**
+ * Phonesky device config does not exists.
+ */
+ @Test
+ public void testDeviceConfigNone() {
+ mockDeviceConfigNone();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ gameManagerService.loadDeviceConfigLocked();
+
+ int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
+ assertEquals(modes.length, 1);
+ assertEquals(modes[0], GameManager.GAME_MODE_UNSUPPORTED);
+ }
+
+ /**
+ * Phonesky device config for performance mode exists and is valid.
+ */
+ @Test
+ public void testDeviceConfigPerformance() {
+ mockDeviceConfigPerformance();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ gameManagerService.loadDeviceConfigLocked();
+
+ boolean perfModeExists = false;
+ int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
+ for (int mode : modes) {
+ if (mode == GameManager.GAME_MODE_PERFORMANCE) {
+ perfModeExists = true;
+ }
+ }
+ assertEquals(modes.length, 1);
+ assertTrue(perfModeExists);
+ }
+
+ /**
+ * Phonesky device config for battery mode exists and is valid.
+ */
+ @Test
+ public void testDeviceConfigBattery() {
+ mockDeviceConfigBattery();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ gameManagerService.loadDeviceConfigLocked();
+
+ boolean batteryModeExists = false;
+ int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
+ for (int mode : modes) {
+ if (mode == GameManager.GAME_MODE_BATTERY) {
+ batteryModeExists = true;
+ }
+ }
+ assertEquals(modes.length, 1);
+ assertTrue(batteryModeExists);
+ }
+
+ /**
+ * Phonesky device configs for both battery and performance modes exists and are valid.
+ */
+ @Test
+ public void testDeviceConfigAll() {
+ mockDeviceConfigAll();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ gameManagerService.loadDeviceConfigLocked();
+
+ boolean batteryModeExists = false;
+ boolean perfModeExists = false;
+ int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
+ for (int mode : modes) {
+ if (mode == GameManager.GAME_MODE_BATTERY) {
+ batteryModeExists = true;
+ } else if (mode == GameManager.GAME_MODE_PERFORMANCE) {
+ perfModeExists = true;
+ }
+ }
+ assertTrue(batteryModeExists);
+ assertTrue(perfModeExists);
+ }
+
+ /**
+ * Phonesky device config contains values that parse correctly but are not valid in game mode.
+ */
+ @Test
+ public void testDeviceConfigInvalid() {
+ mockDeviceConfigInvalid();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ gameManagerService.loadDeviceConfigLocked();
+
+ int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
+ assertEquals(modes.length, 1);
+ assertEquals(modes[0], GameManager.GAME_MODE_UNSUPPORTED);
+ }
+
+ /**
+ * Phonesky device config is garbage.
+ */
+ @Test
+ public void testDeviceConfigMalformed() {
+ mockDeviceConfigMalformed();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ gameManagerService.loadDeviceConfigLocked();
+
+ int[] modes = gameManagerService.getAvailableGameModes(mPackageName);
+ assertEquals(modes.length, 1);
+ assertEquals(modes[0], GameManager.GAME_MODE_UNSUPPORTED);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
index 924ad7f..76f7e80 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -35,7 +35,6 @@
import android.os.Message;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
-import android.util.ArrayMap;
import android.util.LongSparseArray;
import androidx.test.InstrumentationRegistry;
@@ -68,7 +67,6 @@
private File mBlobsDir;
private LongSparseArray<BlobStoreSession> mUserSessions;
- private ArrayMap<BlobHandle, BlobMetadata> mUserBlobs;
private static final String TEST_PKG1 = "com.example1";
private static final String TEST_PKG2 = "com.example2";
@@ -99,10 +97,8 @@
mHandler = new TestHandler(Looper.getMainLooper());
mService = new BlobStoreManagerService(mContext, new TestInjector());
mUserSessions = new LongSparseArray<>();
- mUserBlobs = new ArrayMap<>();
mService.addUserSessionsForTest(mUserSessions, UserHandle.myUserId());
- mService.addUserBlobsForTest(mUserBlobs, UserHandle.myUserId());
}
@After
@@ -147,7 +143,7 @@
final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1,
blobHandle1, true /* hasLeases */);
doReturn(true).when(blobMetadata1).isACommitter(TEST_PKG1, TEST_UID1);
- mUserBlobs.put(blobHandle1, blobMetadata1);
+ addBlob(blobHandle1, blobMetadata1);
final long blobId2 = 347;
final File blobFile2 = mock(File.class);
@@ -156,7 +152,7 @@
final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2,
blobHandle2, false /* hasLeases */);
doReturn(false).when(blobMetadata2).isACommitter(TEST_PKG1, TEST_UID1);
- mUserBlobs.put(blobHandle2, blobMetadata2);
+ addBlob(blobHandle2, blobMetadata2);
final long blobId3 = 49875;
final File blobFile3 = mock(File.class);
@@ -165,7 +161,7 @@
final BlobMetadata blobMetadata3 = createBlobMetadataMock(blobId3, blobFile3,
blobHandle3, true /* hasLeases */);
doReturn(true).when(blobMetadata3).isACommitter(TEST_PKG1, TEST_UID1);
- mUserBlobs.put(blobHandle3, blobMetadata3);
+ addBlob(blobHandle3, blobMetadata3);
mService.addActiveIdsForTest(sessionId1, sessionId2, sessionId3, sessionId4,
blobId1, blobId2, blobId3);
@@ -197,10 +193,10 @@
verify(blobMetadata2).destroy();
verify(blobMetadata3).destroy();
- assertThat(mUserBlobs.size()).isEqualTo(1);
- assertThat(mUserBlobs.get(blobHandle1)).isNotNull();
- assertThat(mUserBlobs.get(blobHandle2)).isNull();
- assertThat(mUserBlobs.get(blobHandle3)).isNull();
+ assertThat(mService.getBlobsCountForTest()).isEqualTo(1);
+ assertThat(mService.getBlobForTest(blobHandle1)).isNotNull();
+ assertThat(mService.getBlobForTest(blobHandle2)).isNull();
+ assertThat(mService.getBlobForTest(blobHandle3)).isNull();
assertThat(mService.getActiveIdsForTest()).containsExactly(
sessionId2, sessionId3, blobId1);
@@ -293,7 +289,7 @@
"label1", System.currentTimeMillis() - 2000, "tag1");
final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1, blobHandle1,
true /* hasLeases */);
- mUserBlobs.put(blobHandle1, blobMetadata1);
+ addBlob(blobHandle1, blobMetadata1);
final long blobId2 = 78974;
final File blobFile2 = mock(File.class);
@@ -301,7 +297,7 @@
"label2", System.currentTimeMillis() + 30000, "tag2");
final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2, blobHandle2,
true /* hasLeases */);
- mUserBlobs.put(blobHandle2, blobMetadata2);
+ addBlob(blobHandle2, blobMetadata2);
final long blobId3 = 97;
final File blobFile3 = mock(File.class);
@@ -309,7 +305,7 @@
"label3", System.currentTimeMillis() + 4400000, "tag3");
final BlobMetadata blobMetadata3 = createBlobMetadataMock(blobId3, blobFile3, blobHandle3,
false /* hasLeases */);
- mUserBlobs.put(blobHandle3, blobMetadata3);
+ addBlob(blobHandle3, blobMetadata3);
mService.addActiveIdsForTest(blobId1, blobId2, blobId3);
@@ -321,8 +317,8 @@
verify(blobMetadata2, never()).destroy();
verify(blobMetadata3).destroy();
- assertThat(mUserBlobs.size()).isEqualTo(1);
- assertThat(mUserBlobs.get(blobHandle2)).isNotNull();
+ assertThat(mService.getBlobsCountForTest()).isEqualTo(1);
+ assertThat(mService.getBlobForTest(blobHandle2)).isNotNull();
assertThat(mService.getActiveIdsForTest()).containsExactly(blobId2);
assertThat(mService.getKnownIdsForTest()).containsExactly(blobId1, blobId2, blobId3);
@@ -336,21 +332,21 @@
doReturn(size1).when(blobMetadata1).getSize();
doReturn(true).when(blobMetadata1).isALeasee(TEST_PKG1, TEST_UID1);
doReturn(true).when(blobMetadata1).isALeasee(TEST_PKG2, TEST_UID2);
- mUserBlobs.put(mock(BlobHandle.class), blobMetadata1);
+ addBlob(mock(BlobHandle.class), blobMetadata1);
final BlobMetadata blobMetadata2 = mock(BlobMetadata.class);
final long size2 = 89475;
doReturn(size2).when(blobMetadata2).getSize();
doReturn(false).when(blobMetadata2).isALeasee(TEST_PKG1, TEST_UID1);
doReturn(true).when(blobMetadata2).isALeasee(TEST_PKG2, TEST_UID2);
- mUserBlobs.put(mock(BlobHandle.class), blobMetadata2);
+ addBlob(mock(BlobHandle.class), blobMetadata2);
final BlobMetadata blobMetadata3 = mock(BlobMetadata.class);
final long size3 = 328732;
doReturn(size3).when(blobMetadata3).getSize();
doReturn(true).when(blobMetadata3).isALeasee(TEST_PKG1, TEST_UID1);
doReturn(false).when(blobMetadata3).isALeasee(TEST_PKG2, TEST_UID2);
- mUserBlobs.put(mock(BlobHandle.class), blobMetadata3);
+ addBlob(mock(BlobHandle.class), blobMetadata3);
// Verify usage is calculated correctly
assertThat(mService.getTotalUsageBytesLocked(TEST_UID1, TEST_PKG1))
@@ -388,6 +384,11 @@
return blobMetadata;
}
+ private void addBlob(BlobHandle blobHandle, BlobMetadata blobMetadata) {
+ doReturn(blobHandle).when(blobMetadata).getBlobHandle();
+ mService.addBlobLocked(blobMetadata);
+ }
+
private class TestHandler extends Handler {
TestHandler(Looper looper) {
super(looper);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 0cd470ae..ffbcc45 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -137,7 +137,7 @@
@Test
public void testUsable() throws Exception {
- final Network net = new Network(101);
+ final Network net = mock(Network.class);
final JobInfo.Builder job = createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1),
DataUnit.MEBIBYTES.toBytes(1))
@@ -148,52 +148,52 @@
// Slow network is too slow
assertFalse(controller.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(1)
- .setLinkDownstreamBandwidthKbps(1), mConstants));
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
+ .setLinkDownstreamBandwidthKbps(1).build(), mConstants));
// Slow downstream
assertFalse(controller.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(1024)
- .setLinkDownstreamBandwidthKbps(1), mConstants));
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(1).build(), mConstants));
// Slow upstream
assertFalse(controller.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(1)
- .setLinkDownstreamBandwidthKbps(1024), mConstants));
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
+ .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
// Fast network looks great
assertTrue(controller.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(1024)
- .setLinkDownstreamBandwidthKbps(1024), mConstants));
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
// Slow network still good given time
assertTrue(controller.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(130)
- .setLinkDownstreamBandwidthKbps(130), mConstants));
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
+ .setLinkDownstreamBandwidthKbps(130).build(), mConstants));
when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(60_000L);
// Slow network is too slow
assertFalse(controller.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(1)
- .setLinkDownstreamBandwidthKbps(1), mConstants));
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
+ .setLinkDownstreamBandwidthKbps(1).build(), mConstants));
// Slow downstream
assertFalse(controller.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(137)
- .setLinkDownstreamBandwidthKbps(1), mConstants));
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(137)
+ .setLinkDownstreamBandwidthKbps(1).build(), mConstants));
// Slow upstream
assertFalse(controller.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(1)
- .setLinkDownstreamBandwidthKbps(137), mConstants));
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
+ .setLinkDownstreamBandwidthKbps(137).build(), mConstants));
// Network good enough
assertTrue(controller.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(137)
- .setLinkDownstreamBandwidthKbps(137), mConstants));
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(137)
+ .setLinkDownstreamBandwidthKbps(137).build(), mConstants));
// Network slightly too slow given reduced time
assertFalse(controller.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(130)
- .setLinkDownstreamBandwidthKbps(130), mConstants));
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
+ .setLinkDownstreamBandwidthKbps(130).build(), mConstants));
}
@Test
public void testInsane() throws Exception {
- final Network net = new Network(101);
+ final Network net = mock(Network.class);
final JobInfo.Builder job = createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1),
DataUnit.MEBIBYTES.toBytes(1))
@@ -205,14 +205,15 @@
// Suspended networks aren't usable.
assertFalse(controller.isSatisfied(createJobStatus(job), net,
- createCapabilities().removeCapability(NET_CAPABILITY_NOT_SUSPENDED)
- .setLinkUpstreamBandwidthKbps(1024).setLinkDownstreamBandwidthKbps(1024),
+ createCapabilitiesBuilder().removeCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ .setLinkUpstreamBandwidthKbps(1024).setLinkDownstreamBandwidthKbps(1024)
+ .build(),
mConstants));
// Not suspended networks are usable.
assertTrue(controller.isSatisfied(createJobStatus(job), net,
- createCapabilities().setLinkUpstreamBandwidthKbps(1024)
- .setLinkDownstreamBandwidthKbps(1024), mConstants));
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
}
@Test
@@ -229,17 +230,17 @@
// Uncongested network is whenever
{
- final Network net = new Network(101);
- final NetworkCapabilities caps = createCapabilities()
- .addCapability(NET_CAPABILITY_NOT_CONGESTED);
+ final Network net = mock(Network.class);
+ final NetworkCapabilities caps = createCapabilitiesBuilder()
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED).build();
assertTrue(controller.isSatisfied(early, net, caps, mConstants));
assertTrue(controller.isSatisfied(late, net, caps, mConstants));
}
// Congested network is more selective
{
- final Network net = new Network(101);
- final NetworkCapabilities caps = createCapabilities();
+ final Network net = mock(Network.class);
+ final NetworkCapabilities caps = createCapabilitiesBuilder().build();
assertFalse(controller.isSatisfied(early, net, caps, mConstants));
assertTrue(controller.isSatisfied(late, net, caps, mConstants));
}
@@ -263,10 +264,11 @@
// Unmetered network is whenever
{
- final Network net = new Network(101);
- final NetworkCapabilities caps = createCapabilities()
+ final Network net = mock(Network.class);
+ final NetworkCapabilities caps = createCapabilitiesBuilder()
.addCapability(NET_CAPABILITY_NOT_CONGESTED)
- .addCapability(NET_CAPABILITY_NOT_METERED);
+ .addCapability(NET_CAPABILITY_NOT_METERED)
+ .build();
assertTrue(controller.isSatisfied(early, net, caps, mConstants));
assertTrue(controller.isSatisfied(late, net, caps, mConstants));
assertTrue(controller.isSatisfied(earlyPrefetch, net, caps, mConstants));
@@ -275,9 +277,10 @@
// Metered network is only when prefetching and late
{
- final Network net = new Network(101);
- final NetworkCapabilities caps = createCapabilities()
- .addCapability(NET_CAPABILITY_NOT_CONGESTED);
+ final Network net = mock(Network.class);
+ final NetworkCapabilities caps = createCapabilitiesBuilder()
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+ .build();
assertFalse(controller.isSatisfied(early, net, caps, mConstants));
assertFalse(controller.isSatisfied(late, net, caps, mConstants));
assertFalse(controller.isSatisfied(earlyPrefetch, net, caps, mConstants));
@@ -301,11 +304,12 @@
final ConnectivityController controller = new ConnectivityController(mService);
- final Network meteredNet = new Network(101);
- final NetworkCapabilities meteredCaps = createCapabilities();
- final Network unmeteredNet = new Network(202);
- final NetworkCapabilities unmeteredCaps = createCapabilities()
- .addCapability(NET_CAPABILITY_NOT_METERED);
+ final Network meteredNet = mock(Network.class);
+ final NetworkCapabilities meteredCaps = createCapabilitiesBuilder().build();
+ final Network unmeteredNet = mock(Network.class);
+ final NetworkCapabilities unmeteredCaps = createCapabilitiesBuilder()
+ .addCapability(NET_CAPABILITY_NOT_METERED)
+ .build();
final JobStatus red = createJobStatus(createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
@@ -610,9 +614,9 @@
networked.setStandbyBucket(FREQUENT_INDEX);
unnetworked.setStandbyBucket(FREQUENT_INDEX);
- final Network cellularNet = new Network(101);
+ final Network cellularNet = mock(Network.class);
final NetworkCapabilities cellularCaps =
- createCapabilities().addTransportType(TRANSPORT_CELLULAR);
+ createCapabilitiesBuilder().addTransportType(TRANSPORT_CELLULAR).build();
final ConnectivityController controller = new ConnectivityController(mService);
controller.maybeStartTrackingJobLocked(networked, null);
@@ -660,8 +664,8 @@
}
}
- private static NetworkCapabilities createCapabilities() {
- return new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET)
+ private static NetworkCapabilities.Builder createCapabilitiesBuilder() {
+ return new NetworkCapabilities.Builder().addCapability(NET_CAPABILITY_INTERNET)
.addCapability(NET_CAPABILITY_NOT_SUSPENDED)
.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
.addCapability(NET_CAPABILITY_VALIDATED);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index bcc756a..2a5bb18 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -157,7 +157,7 @@
new Object(),
mMockSecurityPolicy,
mMockSystemSupport,
- mA11yms,
+ mA11yms.getTraceManager(),
mMockWindowManagerService,
mMockSystemActionPerformer,
mMockA11yWindowManager,
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index 5bef877..e9b5b62 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -184,6 +184,7 @@
false /* callerInstantApp */,
null /* resolvedType */,
null /* requiredPermissions */,
+ null /* excludedPermissions */,
0 /* appOp */,
null /* options */,
new ArrayList<>(receivers), // Make a copy to not affect the original list.
diff --git a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
deleted file mode 100644
index edc0d46..0000000
--- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.app;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-import android.Manifest;
-import android.app.GameManager;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-import java.util.HashMap;
-import java.util.function.Supplier;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class GameManagerServiceTests {
-
- private static final String TAG = "GameServiceTests";
- private static final String PACKAGE_NAME_INVALID = "com.android.app";
- private static final int USER_ID_1 = 1001;
- private static final int USER_ID_2 = 1002;
-
- // Stolen from ConnectivityServiceTest.MockContext
- static class MockContext extends ContextWrapper {
- private static final String TAG = "MockContext";
-
- // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
- private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
-
- @Mock
- private final MockPackageManager mMockPackageManager;
-
- MockContext(Context base) {
- super(base);
- mMockPackageManager = new MockPackageManager();
- }
-
- /**
- * Mock checks for the specified permission, and have them behave as per {@code granted}.
- *
- * <p>Passing null reverts to default behavior, which does a real permission check on the
- * test package.
- *
- * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
- * {@link PackageManager#PERMISSION_DENIED}.
- */
- public void setPermission(String permission, Integer granted) {
- mMockedPermissions.put(permission, granted);
- }
-
- private int checkMockedPermission(String permission, Supplier<Integer> ifAbsent) {
- final Integer granted = mMockedPermissions.get(permission);
- return granted != null ? granted : ifAbsent.get();
- }
-
- @Override
- public int checkPermission(String permission, int pid, int uid) {
- return checkMockedPermission(
- permission, () -> super.checkPermission(permission, pid, uid));
- }
-
- @Override
- public int checkCallingOrSelfPermission(String permission) {
- return checkMockedPermission(
- permission, () -> super.checkCallingOrSelfPermission(permission));
- }
-
- @Override
- public void enforceCallingOrSelfPermission(String permission, String message) {
- final Integer granted = mMockedPermissions.get(permission);
- if (granted == null) {
- super.enforceCallingOrSelfPermission(permission, message);
- return;
- }
-
- if (!granted.equals(PackageManager.PERMISSION_GRANTED)) {
- throw new SecurityException("[Test] permission denied: " + permission);
- }
- }
-
- @Override
- public PackageManager getPackageManager() {
- return mMockPackageManager;
- }
- }
-
- @Mock
- private MockContext mMockContext;
-
- private String mPackageName;
-
- @Before
- public void setUp() throws Exception {
- mMockContext = new MockContext(InstrumentationRegistry.getContext());
- mPackageName = mMockContext.getPackageName();
- }
-
- private void mockModifyGameModeGranted() {
- mMockContext.setPermission(Manifest.permission.MANAGE_GAME_MODE,
- PackageManager.PERMISSION_GRANTED);
- }
-
- private void mockModifyGameModeDenied() {
- mMockContext.setPermission(Manifest.permission.MANAGE_GAME_MODE,
- PackageManager.PERMISSION_DENIED);
- }
-
- /**
- * By default game mode is not supported.
- */
- @Test
- public void testGameModeDefaultValue() {
- GameManagerService gameManagerService = new GameManagerService(mMockContext);
- gameManagerService.onUserStarting(USER_ID_1);
-
- mockModifyGameModeGranted();
-
- assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
- gameManagerService.getGameMode(mPackageName, USER_ID_1));
- }
-
- /**
- * Test the default behaviour for a nonexistent user.
- */
- @Test
- public void testDefaultValueForNonexistentUser() {
- GameManagerService gameManagerService = new GameManagerService(mMockContext);
- gameManagerService.onUserStarting(USER_ID_1);
-
- mockModifyGameModeGranted();
-
- gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_2);
- assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
- gameManagerService.getGameMode(mPackageName, USER_ID_2));
- }
-
- /**
- * Test getter and setter of game modes.
- */
- @Test
- public void testGameMode() {
- GameManagerService gameManagerService = new GameManagerService(mMockContext);
- gameManagerService.onUserStarting(USER_ID_1);
-
- mockModifyGameModeGranted();
-
- assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
- gameManagerService.getGameMode(mPackageName, USER_ID_1));
- gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
- assertEquals(GameManager.GAME_MODE_STANDARD,
- gameManagerService.getGameMode(mPackageName, USER_ID_1));
- gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE,
- USER_ID_1);
- assertEquals(GameManager.GAME_MODE_PERFORMANCE,
- gameManagerService.getGameMode(mPackageName, USER_ID_1));
- }
-
- /**
- * Test permission.MANAGE_GAME_MODE is checked
- */
- @Test
- public void testGetGameModeInvalidPackageName() {
- GameManagerService gameManagerService = new GameManagerService(mMockContext);
- gameManagerService.onUserStarting(USER_ID_1);
-
- try {
- assertEquals(GameManager.GAME_MODE_UNSUPPORTED,
- gameManagerService.getGameMode(PACKAGE_NAME_INVALID,
- USER_ID_1));
-
- fail("GameManagerService failed to generate SecurityException when "
- + "permission.MANAGE_GAME_MODE is not granted.");
- } catch (SecurityException ignored) {
- }
-
- // The test should throw an exception, so the test is passing if we get here.
- }
-
- /**
- * Test permission.MANAGE_GAME_MODE is checked
- */
- @Test
- public void testSetGameModePermissionDenied() {
- GameManagerService gameManagerService = new GameManagerService(mMockContext);
- gameManagerService.onUserStarting(USER_ID_1);
-
- // Update the game mode so we can read back something valid.
- mockModifyGameModeGranted();
- gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
- assertEquals(GameManager.GAME_MODE_STANDARD,
- gameManagerService.getGameMode(mPackageName, USER_ID_1));
-
- // Deny permission.MANAGE_GAME_MODE and verify the game mode is not updated.
- mockModifyGameModeDenied();
- try {
- gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE,
- USER_ID_1);
-
- fail("GameManagerService failed to generate SecurityException when "
- + "permission.MANAGE_GAME_MODE is denied.");
- } catch (SecurityException ignored) {
- }
-
- // The test should throw an exception, so the test is passing if we get here.
- mockModifyGameModeGranted();
- // Verify that the Game Mode value wasn't updated.
- assertEquals(GameManager.GAME_MODE_STANDARD,
- gameManagerService.getGameMode(mPackageName, USER_ID_1));
- }
-
- /**
- * Test game modes are user-specific.
- */
- @Test
- public void testGameModeMultipleUsers() {
- GameManagerService gameManagerService = new GameManagerService(mMockContext);
- gameManagerService.onUserStarting(USER_ID_1);
- gameManagerService.onUserStarting(USER_ID_2);
-
- mockModifyGameModeGranted();
-
- // Set User 1 to Standard
- gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
- assertEquals(GameManager.GAME_MODE_STANDARD,
- gameManagerService.getGameMode(mPackageName, USER_ID_1));
-
- // Set User 2 to Performance and verify User 1 is still Standard
- gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE,
- USER_ID_2);
- assertEquals(GameManager.GAME_MODE_PERFORMANCE,
- gameManagerService.getGameMode(mPackageName, USER_ID_2));
- assertEquals(GameManager.GAME_MODE_STANDARD,
- gameManagerService.getGameMode(mPackageName, USER_ID_1));
-
- // Set User 1 to Battery and verify User 2 is still Performance
- gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY,
- USER_ID_1);
- assertEquals(GameManager.GAME_MODE_BATTERY,
- gameManagerService.getGameMode(mPackageName, USER_ID_1));
- assertEquals(GameManager.GAME_MODE_PERFORMANCE,
- gameManagerService.getGameMode(mPackageName, USER_ID_2));
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 9a430e4..f280aea 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -123,7 +123,7 @@
doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1);
mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
- mAppHibernationService.mIsServiceEnabled = true;
+ mAppHibernationService.sIsServiceEnabled = true;
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
index 4240581..b552fd5 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
@@ -32,6 +32,8 @@
import androidx.test.core.app.ApplicationProvider;
+import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -203,7 +205,7 @@
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
// Set schema1
- String prefix = AppSearchImpl.createPrefix("package", "database");
+ String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
@@ -280,7 +282,7 @@
mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
- String prefix = AppSearchImpl.createPrefix("package", "database");
+ String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
@@ -353,7 +355,7 @@
@Test
public void testSetSchema_defaultPlatformVisible() throws Exception {
- String prefix = AppSearchImpl.createPrefix("package", "database");
+ String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
@@ -372,7 +374,7 @@
@Test
public void testSetSchema_platformHidden() throws Exception {
- String prefix = AppSearchImpl.createPrefix("package", "database");
+ String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
@@ -391,7 +393,7 @@
@Test
public void testSetSchema_defaultNotPackageAccessible() throws Exception {
- String prefix = AppSearchImpl.createPrefix("package", "database");
+ String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
@@ -419,7 +421,7 @@
mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
- String prefix = AppSearchImpl.createPrefix("package", "database");
+ String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
index 8d35ebe..11ae76b 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
@@ -28,6 +28,8 @@
import androidx.test.core.app.ApplicationProvider;
+import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -78,13 +80,9 @@
@Test
public void testValidPackageName() {
assertThat(VisibilityStore.PACKAGE_NAME)
- .doesNotContain(
- "" + AppSearchImpl.PACKAGE_DELIMITER); // Convert the chars to CharSequences
+ .doesNotContain(String.valueOf(PrefixUtil.PACKAGE_DELIMITER));
assertThat(VisibilityStore.PACKAGE_NAME)
- .doesNotContain(
- ""
- + AppSearchImpl
- .DATABASE_DELIMITER); // Convert the chars to CharSequences
+ .doesNotContain(String.valueOf(PrefixUtil.DATABASE_DELIMITER));
}
/**
@@ -93,13 +91,9 @@
@Test
public void testValidDatabaseName() {
assertThat(VisibilityStore.DATABASE_NAME)
- .doesNotContain(
- "" + AppSearchImpl.PACKAGE_DELIMITER); // Convert the chars to CharSequences
+ .doesNotContain(String.valueOf(PrefixUtil.PACKAGE_DELIMITER));
assertThat(VisibilityStore.DATABASE_NAME)
- .doesNotContain(
- ""
- + AppSearchImpl
- .DATABASE_DELIMITER); // Convert the chars to CharSequences
+ .doesNotContain(String.valueOf(PrefixUtil.DATABASE_DELIMITER));
}
@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 ba4d585..380d9be 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
@@ -16,6 +16,10 @@
package com.android.server.appsearch.external.localstorage;
+import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.addPrefixToDocument;
+import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix;
+import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.removePrefixesFromDocument;
+
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.expectThrows;
@@ -35,8 +39,10 @@
import androidx.test.core.app.ApplicationProvider;
import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
+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.SchemaProto;
@@ -47,6 +53,7 @@
import com.android.server.appsearch.proto.TermMatchType;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
@@ -54,6 +61,7 @@
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -96,56 +104,71 @@
// Create a copy so we can modify it.
List<SchemaTypeConfigProto> existingTypes =
new ArrayList<>(existingSchemaBuilder.getTypesList());
-
- SchemaProto newSchema =
- SchemaProto.newBuilder()
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build())
- .addTypes(
- SchemaTypeConfigProto.newBuilder()
- .setSchemaType("TestType")
- .addProperties(
- PropertyConfigProto.newBuilder()
- .setPropertyName("subject")
- .setDataType(
- PropertyConfigProto.DataType.Code
- .STRING)
- .setCardinality(
- PropertyConfigProto.Cardinality.Code
- .OPTIONAL)
- .setStringIndexingConfig(
- StringIndexingConfig.newBuilder()
- .setTokenizerType(
- StringIndexingConfig
- .TokenizerType
- .Code.PLAIN)
- .setTermMatchType(
- TermMatchType.Code
- .PREFIX)
- .build())
- .build())
- .addProperties(
- PropertyConfigProto.newBuilder()
- .setPropertyName("link")
- .setDataType(
- PropertyConfigProto.DataType.Code
- .DOCUMENT)
- .setCardinality(
- PropertyConfigProto.Cardinality.Code
- .OPTIONAL)
- .setSchemaType("RefType")
+ SchemaTypeConfigProto schemaTypeConfigProto1 =
+ SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build();
+ SchemaTypeConfigProto schemaTypeConfigProto2 =
+ SchemaTypeConfigProto.newBuilder()
+ .setSchemaType("TestType")
+ .addProperties(
+ PropertyConfigProto.newBuilder()
+ .setPropertyName("subject")
+ .setDataType(PropertyConfigProto.DataType.Code.STRING)
+ .setCardinality(
+ PropertyConfigProto.Cardinality.Code.OPTIONAL)
+ .setStringIndexingConfig(
+ StringIndexingConfig.newBuilder()
+ .setTokenizerType(
+ StringIndexingConfig.TokenizerType
+ .Code.PLAIN)
+ .setTermMatchType(TermMatchType.Code.PREFIX)
.build())
.build())
+ .addProperties(
+ PropertyConfigProto.newBuilder()
+ .setPropertyName("link")
+ .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
+ .setCardinality(
+ PropertyConfigProto.Cardinality.Code.OPTIONAL)
+ .setSchemaType("RefType")
+ .build())
+ .build();
+ SchemaTypeConfigProto schemaTypeConfigProto3 =
+ SchemaTypeConfigProto.newBuilder().setSchemaType("RefType").build();
+ SchemaProto newSchema =
+ SchemaProto.newBuilder()
+ .addTypes(schemaTypeConfigProto1)
+ .addTypes(schemaTypeConfigProto2)
+ .addTypes(schemaTypeConfigProto3)
.build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
mAppSearchImpl.rewriteSchema(
- AppSearchImpl.createPrefix("package", "newDatabase"),
- existingSchemaBuilder,
- newSchema);
+ createPrefix("package", "newDatabase"), existingSchemaBuilder, newSchema);
// We rewrote all the new types that were added. And nothing was removed.
- assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
- .containsExactly("package$newDatabase/Foo", "package$newDatabase/TestType");
+ assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet())
+ .containsExactly(
+ "package$newDatabase/Foo",
+ "package$newDatabase/TestType",
+ "package$newDatabase/RefType");
+ assertThat(
+ rewrittenSchemaResults
+ .mRewrittenPrefixedTypes
+ .get("package$newDatabase/Foo")
+ .getSchemaType())
+ .isEqualTo("package$newDatabase/Foo");
+ assertThat(
+ rewrittenSchemaResults
+ .mRewrittenPrefixedTypes
+ .get("package$newDatabase/TestType")
+ .getSchemaType())
+ .isEqualTo("package$newDatabase/TestType");
+ assertThat(
+ rewrittenSchemaResults
+ .mRewrittenPrefixedTypes
+ .get("package$newDatabase/RefType")
+ .getSchemaType())
+ .isEqualTo("package$newDatabase/RefType");
assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty();
SchemaProto expectedSchema =
@@ -190,6 +213,10 @@
"package$newDatabase/RefType")
.build())
.build())
+ .addTypes(
+ SchemaTypeConfigProto.newBuilder()
+ .setSchemaType("package$newDatabase/RefType")
+ .build())
.build();
existingTypes.addAll(expectedSchema.getTypesList());
@@ -216,12 +243,12 @@
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
mAppSearchImpl.rewriteSchema(
- AppSearchImpl.createPrefix("package", "existingDatabase"),
+ createPrefix("package", "existingDatabase"),
existingSchemaBuilder,
newSchema);
// Nothing was removed, but the method did rewrite the type name.
- assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
+ assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet())
.containsExactly("package$existingDatabase/Foo");
assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty();
@@ -251,14 +278,15 @@
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
mAppSearchImpl.rewriteSchema(
- AppSearchImpl.createPrefix("package", "existingDatabase"),
+ createPrefix("package", "existingDatabase"),
existingSchemaBuilder,
newSchema);
// Bar type was rewritten, but Foo ended up being deleted since it wasn't included in the
// new schema.
assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
- .containsExactly("package$existingDatabase/Bar");
+ .containsKey("package$existingDatabase/Bar");
+ assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet().size()).isEqualTo(1);
assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes)
.containsExactly("package$existingDatabase/Foo");
@@ -308,8 +336,7 @@
.build();
DocumentProto.Builder actualDocument = documentProto.toBuilder();
- mAppSearchImpl.addPrefixToDocument(
- actualDocument, AppSearchImpl.createPrefix("package", "databaseName"));
+ addPrefixToDocument(actualDocument, createPrefix("package", "databaseName"));
assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto);
}
@@ -347,8 +374,7 @@
.build();
DocumentProto.Builder actualDocument = documentProto.toBuilder();
- assertThat(mAppSearchImpl.removePrefixesFromDocument(actualDocument))
- .isEqualTo("package$databaseName/");
+ assertThat(removePrefixesFromDocument(actualDocument)).isEqualTo("package$databaseName/");
assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto);
}
@@ -365,8 +391,7 @@
DocumentProto.Builder actualDocument = documentProto.toBuilder();
AppSearchException e =
expectThrows(
- AppSearchException.class,
- () -> mAppSearchImpl.removePrefixesFromDocument(actualDocument));
+ AppSearchException.class, () -> removePrefixesFromDocument(actualDocument));
assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names");
}
@@ -391,8 +416,7 @@
DocumentProto.Builder actualDocument = documentProto.toBuilder();
AppSearchException e =
expectThrows(
- AppSearchException.class,
- () -> mAppSearchImpl.removePrefixesFromDocument(actualDocument));
+ AppSearchException.class, () -> removePrefixesFromDocument(actualDocument));
assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names");
}
@@ -484,7 +508,7 @@
// Rewrite SearchSpec
mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
searchSpecProto,
- Collections.singleton(AppSearchImpl.createPrefix("package", "database")),
+ Collections.singleton(createPrefix("package", "database")),
ImmutableSet.of("package$database/type"));
assertThat(searchSpecProto.getSchemaTypeFiltersList())
.containsExactly("package$database/type");
@@ -531,8 +555,7 @@
mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
searchSpecProto,
ImmutableSet.of(
- AppSearchImpl.createPrefix("package", "database1"),
- AppSearchImpl.createPrefix("package", "database2")),
+ createPrefix("package", "database1"), createPrefix("package", "database2")),
ImmutableSet.of(
"package$database1/typeA", "package$database1/typeB",
"package$database2/typeA", "package$database2/typeB"));
@@ -573,8 +596,7 @@
assertThat(
mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
searchSpecProto,
- Collections.singleton(
- AppSearchImpl.createPrefix("package", "database")),
+ Collections.singleton(createPrefix("package", "database")),
/*allowedPrefixedSchemas=*/ Collections.emptySet()))
.isFalse();
}
@@ -1082,7 +1104,7 @@
// Has database1
Set<String> expectedPrefixes = new ArraySet<>(existingPrefixes);
- expectedPrefixes.add(AppSearchImpl.createPrefix("package", "database1"));
+ expectedPrefixes.add(createPrefix("package", "database1"));
mAppSearchImpl.setSchema(
"package",
"database1",
@@ -1094,7 +1116,7 @@
assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactlyElementsIn(expectedPrefixes);
// Has both databases
- expectedPrefixes.add(AppSearchImpl.createPrefix("package", "database2"));
+ expectedPrefixes.add(createPrefix("package", "database2"));
mAppSearchImpl.setSchema(
"package",
"database2",
@@ -1110,9 +1132,9 @@
public void testRewriteSearchResultProto() throws Exception {
final String prefix =
"com.package.foo"
- + AppSearchImpl.PACKAGE_DELIMITER
+ + PrefixUtil.PACKAGE_DELIMITER
+ "databaseName"
- + AppSearchImpl.DATABASE_DELIMITER;
+ + PrefixUtil.DATABASE_DELIMITER;
final String uri = "uri";
final String namespace = prefix + "namespace";
final String schemaType = prefix + "schema";
@@ -1128,18 +1150,22 @@
SearchResultProto.ResultProto.newBuilder().setDocument(documentProto).build();
SearchResultProto searchResultProto =
SearchResultProto.newBuilder().addResults(resultProto).build();
+ SchemaTypeConfigProto schemaTypeConfigProto =
+ SchemaTypeConfigProto.newBuilder().setSchemaType(schemaType).build();
+ Map<String, Map<String, SchemaTypeConfigProto>> schemaMap =
+ ImmutableMap.of(prefix, ImmutableMap.of(schemaType, schemaTypeConfigProto));
DocumentProto.Builder strippedDocumentProto = documentProto.toBuilder();
- AppSearchImpl.removePrefixesFromDocument(strippedDocumentProto);
+ removePrefixesFromDocument(strippedDocumentProto);
SearchResultPage searchResultPage =
- AppSearchImpl.rewriteSearchResultProto(searchResultProto);
+ AppSearchImpl.rewriteSearchResultProto(searchResultProto, schemaMap);
for (SearchResult result : searchResultPage.getResults()) {
assertThat(result.getPackageName()).isEqualTo("com.package.foo");
assertThat(result.getDatabaseName()).isEqualTo("databaseName");
assertThat(result.getGenericDocument())
.isEqualTo(
GenericDocumentToProtoConverter.toGenericDocument(
- strippedDocumentProto.build()));
+ strippedDocumentProto.build(), prefix, schemaMap.get(prefix)));
}
}
@@ -1609,7 +1635,221 @@
expectThrows(
IllegalStateException.class,
() -> {
- appSearchImpl.persistToDisk();
+ appSearchImpl.persistToDisk(PersistType.Code.FULL);
});
}
+
+ @Test
+ public void testPutPersistsWithLiteFlush() throws Exception {
+ // Setup the index
+ Context context = ApplicationProvider.getApplicationContext();
+ File appsearchDir = mTemporaryFolder.newFolder();
+ AppSearchImpl appSearchImpl =
+ AppSearchImpl.create(
+ appsearchDir,
+ context,
+ VisibilityStore.NO_OP_USER_ID,
+ /*globalQuerierPackage=*/ "");
+
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ appSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Add a document and persist it.
+ GenericDocument document =
+ new GenericDocument.Builder<>("namespace1", "uri1", "type").build();
+ appSearchImpl.putDocument("package", "database", document, /*logger=*/ null);
+ appSearchImpl.persistToDisk(PersistType.Code.LITE);
+
+ GenericDocument getResult =
+ appSearchImpl.getDocument(
+ "package", "database", "namespace1", "uri1", Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document);
+
+ // That document should be visible even from another instance.
+ AppSearchImpl appSearchImpl2 =
+ AppSearchImpl.create(
+ appsearchDir,
+ context,
+ VisibilityStore.NO_OP_USER_ID,
+ /*globalQuerierPackage=*/ "");
+ getResult =
+ appSearchImpl2.getDocument(
+ "package", "database", "namespace1", "uri1", Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document);
+ }
+
+ @Test
+ public void testDeletePersistsWithLiteFlush() 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=*/ "");
+
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ appSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Add two documents and persist them.
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace1", "uri1", "type").build();
+ appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null);
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace1", "uri2", "type").build();
+ appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null);
+ appSearchImpl.persistToDisk(PersistType.Code.LITE);
+
+ GenericDocument getResult =
+ appSearchImpl.getDocument(
+ "package", "database", "namespace1", "uri1", Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document1);
+ getResult =
+ appSearchImpl.getDocument(
+ "package", "database", "namespace1", "uri2", Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document2);
+
+ // Delete the first document
+ appSearchImpl.remove("package", "database", "namespace1", "uri1");
+ appSearchImpl.persistToDisk(PersistType.Code.LITE);
+ expectThrows(
+ AppSearchException.class,
+ () ->
+ appSearchImpl.getDocument(
+ "package",
+ "database",
+ "namespace1",
+ "uri1",
+ Collections.emptyMap()));
+ getResult =
+ appSearchImpl.getDocument(
+ "package", "database", "namespace1", "uri2", Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document2);
+
+ // Only the second document should be retrievable from another instance.
+ AppSearchImpl appSearchImpl2 =
+ AppSearchImpl.create(
+ appsearchDir,
+ context,
+ VisibilityStore.NO_OP_USER_ID,
+ /*globalQuerierPackage=*/ "");
+ expectThrows(
+ AppSearchException.class,
+ () ->
+ appSearchImpl2.getDocument(
+ "package",
+ "database",
+ "namespace1",
+ "uri1",
+ Collections.emptyMap()));
+ getResult =
+ appSearchImpl2.getDocument(
+ "package", "database", "namespace1", "uri2", Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document2);
+ }
+
+ @Test
+ public void testDeleteByQueryPersistsWithLiteFlush() 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=*/ "");
+
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ appSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Add two documents and persist them.
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace1", "uri1", "type").build();
+ appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null);
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace2", "uri2", "type").build();
+ appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null);
+ appSearchImpl.persistToDisk(PersistType.Code.LITE);
+
+ GenericDocument getResult =
+ appSearchImpl.getDocument(
+ "package", "database", "namespace1", "uri1", Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document1);
+ getResult =
+ appSearchImpl.getDocument(
+ "package", "database", "namespace2", "uri2", Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document2);
+
+ // Delete the first document
+ appSearchImpl.removeByQuery(
+ "package",
+ "database",
+ "",
+ new SearchSpec.Builder()
+ .addFilterNamespaces("namespace1")
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .build());
+ appSearchImpl.persistToDisk(PersistType.Code.LITE);
+ expectThrows(
+ AppSearchException.class,
+ () ->
+ appSearchImpl.getDocument(
+ "package",
+ "database",
+ "namespace1",
+ "uri1",
+ Collections.emptyMap()));
+ getResult =
+ appSearchImpl.getDocument(
+ "package", "database", "namespace2", "uri2", Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document2);
+
+ // Only the second document should be retrievable from another instance.
+ AppSearchImpl appSearchImpl2 =
+ AppSearchImpl.create(
+ appsearchDir,
+ context,
+ VisibilityStore.NO_OP_USER_ID,
+ /*globalQuerierPackage=*/ "");
+ expectThrows(
+ AppSearchException.class,
+ () ->
+ appSearchImpl2.getDocument(
+ "package",
+ "database",
+ "namespace1",
+ "uri1",
+ Collections.emptyMap()));
+ getResult =
+ appSearchImpl2.getDocument(
+ "package", "database", "namespace2", "uri2", Collections.emptyMap());
+ assertThat(getResult).isEqualTo(document2);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
index 70e1e05..63f031722 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
@@ -21,35 +21,50 @@
import android.app.appsearch.GenericDocument;
import com.android.server.appsearch.proto.DocumentProto;
+import com.android.server.appsearch.proto.PropertyConfigProto;
import com.android.server.appsearch.proto.PropertyProto;
+import com.android.server.appsearch.proto.SchemaTypeConfigProto;
import com.android.server.appsearch.protobuf.ByteString;
+import com.google.common.collect.ImmutableMap;
+
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
public class GenericDocumentToProtoConverterTest {
private static final byte[] BYTE_ARRAY_1 = new byte[] {(byte) 1, (byte) 2, (byte) 3};
private static final byte[] BYTE_ARRAY_2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7};
+ private static final String SCHEMA_TYPE_1 = "sDocumentPropertiesSchemaType1";
+ private static final String SCHEMA_TYPE_2 = "sDocumentPropertiesSchemaType2";
private static final GenericDocument DOCUMENT_PROPERTIES_1 =
new GenericDocument.Builder<GenericDocument.Builder<?>>(
- "namespace", "sDocumentProperties1", "sDocumentPropertiesSchemaType1")
+ "namespace", "sDocumentProperties1", SCHEMA_TYPE_1)
.setCreationTimestampMillis(12345L)
.build();
private static final GenericDocument DOCUMENT_PROPERTIES_2 =
new GenericDocument.Builder<GenericDocument.Builder<?>>(
- "namespace", "sDocumentProperties2", "sDocumentPropertiesSchemaType2")
+ "namespace", "sDocumentProperties2", SCHEMA_TYPE_2)
.setCreationTimestampMillis(6789L)
.build();
+ private static final SchemaTypeConfigProto SCHEMA_PROTO_1 =
+ SchemaTypeConfigProto.newBuilder().setSchemaType(SCHEMA_TYPE_1).build();
+ private static final SchemaTypeConfigProto SCHEMA_PROTO_2 =
+ SchemaTypeConfigProto.newBuilder().setSchemaType(SCHEMA_TYPE_2).build();
+ private static final String PREFIX = "package$databaseName/";
+ private static final Map<String, SchemaTypeConfigProto> SCHEMA_MAP =
+ ImmutableMap.of(
+ PREFIX + SCHEMA_TYPE_1, SCHEMA_PROTO_1, PREFIX + SCHEMA_TYPE_2, SCHEMA_PROTO_2);
@Test
public void testDocumentProtoConvert() {
GenericDocument document =
new GenericDocument.Builder<GenericDocument.Builder<?>>(
- "namespace", "uri1", "schemaType1")
+ "namespace", "uri1", SCHEMA_TYPE_1)
.setCreationTimestampMillis(5L)
.setScore(1)
.setTtlMillis(1L)
@@ -66,7 +81,7 @@
DocumentProto.Builder documentProtoBuilder =
DocumentProto.newBuilder()
.setUri("uri1")
- .setSchema("schemaType1")
+ .setSchema(SCHEMA_TYPE_1)
.setCreationTimestampMs(5L)
.setScore(1)
.setTtlMs(1L)
@@ -109,9 +124,133 @@
documentProtoBuilder.addProperties(propertyProtoMap.get(key));
}
DocumentProto documentProto = documentProtoBuilder.build();
- assertThat(GenericDocumentToProtoConverter.toDocumentProto(document))
- .isEqualTo(documentProto);
- assertThat(document)
- .isEqualTo(GenericDocumentToProtoConverter.toGenericDocument(documentProto));
+
+ GenericDocument convertedGenericDocument =
+ GenericDocumentToProtoConverter.toGenericDocument(
+ documentProto, PREFIX, SCHEMA_MAP);
+ DocumentProto convertedDocumentProto =
+ GenericDocumentToProtoConverter.toDocumentProto(document);
+
+ assertThat(convertedDocumentProto).isEqualTo(documentProto);
+ assertThat(convertedGenericDocument).isEqualTo(document);
+ }
+
+ @Test
+ public void testConvertDocument_whenPropertyHasEmptyList() {
+ String emptyStringPropertyName = "emptyStringProperty";
+ DocumentProto documentProto =
+ DocumentProto.newBuilder()
+ .setUri("uri1")
+ .setSchema(SCHEMA_TYPE_1)
+ .setCreationTimestampMs(5L)
+ .setNamespace("namespace")
+ .addProperties(
+ PropertyProto.newBuilder().setName(emptyStringPropertyName).build())
+ .build();
+
+ PropertyConfigProto emptyStringListProperty =
+ PropertyConfigProto.newBuilder()
+ .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
+ .setDataType(PropertyConfigProto.DataType.Code.STRING)
+ .setPropertyName(emptyStringPropertyName)
+ .build();
+ SchemaTypeConfigProto schemaTypeConfigProto =
+ SchemaTypeConfigProto.newBuilder()
+ .addProperties(emptyStringListProperty)
+ .setSchemaType(SCHEMA_TYPE_1)
+ .build();
+ Map<String, SchemaTypeConfigProto> schemaMap =
+ ImmutableMap.of(PREFIX + SCHEMA_TYPE_1, schemaTypeConfigProto);
+
+ GenericDocument convertedDocument =
+ GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX, schemaMap);
+
+ GenericDocument expectedDocument =
+ new GenericDocument.Builder<GenericDocument.Builder<?>>(
+ "namespace", "uri1", SCHEMA_TYPE_1)
+ .setCreationTimestampMillis(5L)
+ .setPropertyString(emptyStringPropertyName)
+ .build();
+ assertThat(convertedDocument).isEqualTo(expectedDocument);
+ assertThat(expectedDocument.getPropertyStringArray(emptyStringPropertyName)).isEmpty();
+ }
+
+ @Test
+ public void testConvertDocument_whenNestedDocumentPropertyHasEmptyList() {
+ String emptyStringPropertyName = "emptyStringProperty";
+ String documentPropertyName = "documentProperty";
+ DocumentProto nestedDocumentProto =
+ DocumentProto.newBuilder()
+ .setUri("uri2")
+ .setSchema(SCHEMA_TYPE_2)
+ .setCreationTimestampMs(5L)
+ .setNamespace("namespace")
+ .addProperties(
+ PropertyProto.newBuilder().setName(emptyStringPropertyName).build())
+ .build();
+ DocumentProto documentProto =
+ DocumentProto.newBuilder()
+ .setUri("uri1")
+ .setSchema(SCHEMA_TYPE_1)
+ .setCreationTimestampMs(5L)
+ .setNamespace("namespace")
+ .addProperties(
+ PropertyProto.newBuilder()
+ .addDocumentValues(nestedDocumentProto)
+ .setName(documentPropertyName)
+ .build())
+ .build();
+
+ PropertyConfigProto documentProperty =
+ PropertyConfigProto.newBuilder()
+ .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
+ .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
+ .setPropertyName(documentPropertyName)
+ .setSchemaType(SCHEMA_TYPE_2)
+ .build();
+ SchemaTypeConfigProto schemaTypeConfigProto =
+ SchemaTypeConfigProto.newBuilder()
+ .addProperties(documentProperty)
+ .setSchemaType(SCHEMA_TYPE_1)
+ .build();
+ PropertyConfigProto emptyStringListProperty =
+ PropertyConfigProto.newBuilder()
+ .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
+ .setDataType(PropertyConfigProto.DataType.Code.STRING)
+ .setPropertyName(emptyStringPropertyName)
+ .build();
+ SchemaTypeConfigProto nestedSchemaTypeConfigProto =
+ SchemaTypeConfigProto.newBuilder()
+ .addProperties(emptyStringListProperty)
+ .setSchemaType(SCHEMA_TYPE_2)
+ .build();
+ Map<String, SchemaTypeConfigProto> schemaMap =
+ ImmutableMap.of(
+ PREFIX + SCHEMA_TYPE_1,
+ schemaTypeConfigProto,
+ PREFIX + SCHEMA_TYPE_2,
+ nestedSchemaTypeConfigProto);
+
+ GenericDocument convertedDocument =
+ GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX, schemaMap);
+
+ GenericDocument expectedDocument =
+ new GenericDocument.Builder<GenericDocument.Builder<?>>(
+ "namespace", "uri1", SCHEMA_TYPE_1)
+ .setCreationTimestampMillis(5L)
+ .setPropertyDocument(
+ documentPropertyName,
+ new GenericDocument.Builder<GenericDocument.Builder<?>>(
+ "namespace", "uri2", SCHEMA_TYPE_2)
+ .setCreationTimestampMillis(5L)
+ .setPropertyString(emptyStringPropertyName)
+ .build())
+ .build();
+ assertThat(convertedDocument).isEqualTo(expectedDocument);
+ assertThat(
+ expectedDocument
+ .getPropertyDocument(documentPropertyName)
+ .getPropertyStringArray(emptyStringPropertyName))
+ .isEmpty();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
index d07211f..26fac49 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
@@ -21,8 +21,10 @@
import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchResultPage;
+import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
import com.android.server.appsearch.proto.DocumentProto;
import com.android.server.appsearch.proto.PropertyProto;
+import com.android.server.appsearch.proto.SchemaTypeConfigProto;
import com.android.server.appsearch.proto.SearchResultProto;
import com.android.server.appsearch.proto.SnippetMatchProto;
import com.android.server.appsearch.proto.SnippetProto;
@@ -30,20 +32,29 @@
import org.junit.Test;
import java.util.Collections;
+import java.util.Map;
public class SnippetTest {
+ private static final String SCHEMA_TYPE = "schema1";
+ private static final String PACKAGE_NAME = "packageName";
+ private static final String DATABASE_NAME = "databaseName";
+ private static final String PREFIX = PrefixUtil.createPrefix(PACKAGE_NAME, DATABASE_NAME);
+ private static final SchemaTypeConfigProto SCHEMA_TYPE_CONFIG_PROTO =
+ SchemaTypeConfigProto.newBuilder().setSchemaType(PREFIX + SCHEMA_TYPE).build();
+ private static final Map<String, Map<String, SchemaTypeConfigProto>> SCHEMA_MAP =
+ Collections.singletonMap(
+ PREFIX,
+ Collections.singletonMap(PREFIX + SCHEMA_TYPE, SCHEMA_TYPE_CONFIG_PROTO));
// TODO(tytytyww): Add tests for Double and Long Snippets.
@Test
public void testSingleStringSnippet() {
-
final String propertyKeyString = "content";
final String propertyValueString =
"A commonly used fake word is foo.\n"
+ " Another nonsense word that’s used a lot\n"
+ " is bar.\n";
final String uri = "uri1";
- final String schemaType = "schema1";
final String searchWord = "foo";
final String exactMatch = "foo";
final String window = "is foo";
@@ -57,7 +68,7 @@
DocumentProto documentProto =
DocumentProto.newBuilder()
.setUri(uri)
- .setSchema(schemaType)
+ .setSchema(SCHEMA_TYPE)
.addProperties(property)
.build();
SnippetProto snippetProto =
@@ -86,8 +97,9 @@
SearchResultPage searchResultPage =
SearchResultToProtoConverter.toSearchResultPage(
searchResultProto,
- Collections.singletonList("packageName"),
- Collections.singletonList("databaseName"));
+ Collections.singletonList(PACKAGE_NAME),
+ Collections.singletonList(DATABASE_NAME),
+ SCHEMA_MAP);
for (SearchResult result : searchResultPage.getResults()) {
SearchResult.MatchInfo match = result.getMatches().get(0);
assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString);
@@ -112,7 +124,6 @@
+ " Another nonsense word that’s used a lot\n"
+ " is bar.\n";
final String uri = "uri1";
- final String schemaType = "schema1";
final String searchWord = "foo";
final String exactMatch = "foo";
final String window = "is foo";
@@ -126,7 +137,7 @@
DocumentProto documentProto =
DocumentProto.newBuilder()
.setUri(uri)
- .setSchema(schemaType)
+ .setSchema(SCHEMA_TYPE)
.addProperties(property)
.build();
SearchResultProto.ResultProto resultProto =
@@ -137,8 +148,9 @@
SearchResultPage searchResultPage =
SearchResultToProtoConverter.toSearchResultPage(
searchResultProto,
- Collections.singletonList("packageName"),
- Collections.singletonList("databaseName"));
+ Collections.singletonList(PACKAGE_NAME),
+ Collections.singletonList(DATABASE_NAME),
+ SCHEMA_MAP);
for (SearchResult result : searchResultPage.getResults()) {
assertThat(result.getMatches()).isEmpty();
}
@@ -162,7 +174,7 @@
DocumentProto documentProto =
DocumentProto.newBuilder()
.setUri("uri1")
- .setSchema("schema1")
+ .setSchema(SCHEMA_TYPE)
.addProperties(property1)
.addProperties(property2)
.build();
@@ -203,8 +215,9 @@
SearchResultPage searchResultPage =
SearchResultToProtoConverter.toSearchResultPage(
searchResultProto,
- Collections.singletonList("packageName"),
- Collections.singletonList("databaseName"));
+ Collections.singletonList(PACKAGE_NAME),
+ Collections.singletonList(DATABASE_NAME),
+ SCHEMA_MAP);
for (SearchResult result : searchResultPage.getResults()) {
SearchResult.MatchInfo match1 = result.getMatches().get(0);
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index 79a5ed6..5c53d43 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -98,8 +98,9 @@
Log.i(TAG, "starting testPostA2dpDeviceConnectionChange");
Assert.assertNotNull("invalid null BT device", mFakeBtDevice);
- mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
- BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1);
+ mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice,
+ BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1));
Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS);
verify(mSpyDevInventory, times(1)).setBluetoothA2dpDeviceConnectionState(
any(BluetoothDevice.class),
@@ -209,20 +210,23 @@
((NoOpAudioSystemAdapter) mSpyAudioSystem).configureIsStreamActive(mockMediaPlayback);
// first connection: ensure the device is connected as a starting condition for the test
- mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
- BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1);
+ mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice,
+ BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1));
Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
// disconnection
- mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
- BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP, false, -1);
+ mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice,
+ BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP, false, -1));
if (delayAfterDisconnection > 0) {
Thread.sleep(delayAfterDisconnection);
}
// reconnection
- mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
- BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 2);
+ mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice,
+ BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 2));
Thread.sleep(AudioService.BECOMING_NOISY_DELAY_MS + MAX_MESSAGE_HANDLING_DELAY_MS);
// Verify disconnection has been cancelled and we're seeing two connections attempts,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index e322ce5..96bab61 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -308,6 +308,8 @@
componentInfo,
type,
false /* resetLockoutRequiresHardwareAuthToken */));
+
+ when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
}
private void setupFace(int id, boolean confirmationAlwaysRequired,
@@ -329,6 +331,6 @@
}
});
- when(mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
+ when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index a5fbab5..ec3bea3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -378,7 +378,7 @@
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
// Disabled in user settings receives onError
- when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(false);
+ when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false);
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
null /* authenticators */);
waitForIdle();
@@ -389,7 +389,7 @@
// Enrolled, not disabled in settings, user requires confirmation in settings
resetReceivers();
- when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
+ when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
when(mBiometricService.mSettingObserver.getConfirmationAlwaysRequired(
anyInt() /* modality */, anyInt() /* userId */))
.thenReturn(true);
@@ -1197,7 +1197,7 @@
@Test
public void testCanAuthenticate_whenBiometricsNotEnabledForApps() throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
- when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(false);
+ when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false);
when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
// When only biometric is requested
@@ -1322,6 +1322,8 @@
final int testId = 0;
+ when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
+
when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any()))
.thenReturn(true);
when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
@@ -1536,7 +1538,7 @@
mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
- when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
+ when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
if ((modality & BiometricAuthenticator.TYPE_FINGERPRINT) != 0) {
when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any()))
@@ -1564,7 +1566,7 @@
mBiometricService = new BiometricService(mContext, mInjector);
mBiometricService.onStart();
- when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
+ when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
assertEquals(modalities.length, strengths.length);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 73ec5b8..1b42dfa 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -31,7 +31,7 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
-import static android.app.admin.PasswordMetrics.computeForPassword;
+import static android.app.admin.PasswordMetrics.computeForPasswordOrPin;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
import static android.net.InetAddresses.parseNumericAddress;
@@ -1551,6 +1551,16 @@
@Test
public void testSetProfileOwner_failures() throws Exception {
// TODO Test more failure cases. Basically test all chacks in enforceCanSetProfileOwner().
+ // Package doesn't exist and caller is not system
+ assertExpectException(SecurityException.class,
+ /* messageRegex= */ "Calling identity is not authorized",
+ () -> dpm.setProfileOwner(admin1, "owner-name", UserHandle.USER_SYSTEM));
+
+ // Package exists, but caller is not system
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ assertExpectException(SecurityException.class,
+ /* messageRegex= */ "Calling identity is not authorized",
+ () -> dpm.setProfileOwner(admin1, "owner-name", UserHandle.USER_SYSTEM));
}
@Test
@@ -3169,6 +3179,16 @@
}
@Test
+ public void testSetUserProvisioningState_profileFinalized_canTransitionToUserUnmanaged()
+ throws Exception {
+ setupProfileOwner();
+
+ exerciseUserProvisioningTransitions(CALLER_USER_HANDLE,
+ DevicePolicyManager.STATE_USER_PROFILE_FINALIZED,
+ DevicePolicyManager.STATE_USER_UNMANAGED);
+ }
+
+ @Test
public void testSetUserProvisioningState_illegalTransitionToAnotherInProgressState()
throws Exception {
setupProfileOwner();
@@ -5156,7 +5176,8 @@
reset(mContext.spiedContext);
- PasswordMetrics passwordMetricsNoSymbols = computeForPassword("abcdXYZ5".getBytes());
+ PasswordMetrics passwordMetricsNoSymbols = computeForPasswordOrPin(
+ "abcdXYZ5".getBytes(), /* isPin */ false);
setActivePasswordState(passwordMetricsNoSymbols);
assertThat(dpm.isActivePasswordSufficient()).isTrue();
@@ -5183,7 +5204,8 @@
reset(mContext.spiedContext);
assertThat(dpm.isActivePasswordSufficient()).isFalse();
- PasswordMetrics passwordMetricsWithSymbols = computeForPassword("abcd.XY5".getBytes());
+ PasswordMetrics passwordMetricsWithSymbols = computeForPasswordOrPin(
+ "abcd.XY5".getBytes(), /* isPin */ false);
setActivePasswordState(passwordMetricsWithSymbols);
assertThat(dpm.isActivePasswordSufficient()).isTrue();
@@ -5237,7 +5259,7 @@
parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_MEDIUM);
when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM))
- .thenReturn(computeForPassword("184342".getBytes()));
+ .thenReturn(computeForPasswordOrPin("184342".getBytes(), /* isPin */ true));
// Numeric password is compliant with current requirement (QUALITY_NUMERIC set explicitly
// on the parent admin)
@@ -6360,7 +6382,7 @@
.thenReturn(CALLER_USER_HANDLE);
when(getServices().lockSettingsInternal
.getUserPasswordMetrics(CALLER_USER_HANDLE))
- .thenReturn(computeForPassword("asdf".getBytes()));
+ .thenReturn(computeForPasswordOrPin("asdf".getBytes(), /* isPin */ false));
assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_MEDIUM);
}
@@ -6380,10 +6402,10 @@
when(getServices().lockSettingsInternal
.getUserPasswordMetrics(CALLER_USER_HANDLE))
- .thenReturn(computeForPassword("asdf".getBytes()));
+ .thenReturn(computeForPasswordOrPin("asdf".getBytes(), /* isPin */ false));
when(getServices().lockSettingsInternal
.getUserPasswordMetrics(parentUser.id))
- .thenReturn(computeForPassword("parentUser".getBytes()));
+ .thenReturn(computeForPasswordOrPin("parentUser".getBytes(), /* isPin */ false));
assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_HIGH);
}
@@ -7059,13 +7081,15 @@
assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_NONE);
reset(mContext.spiedContext);
- PasswordMetrics passwordMetricsNoSymbols = computeForPassword("1234".getBytes());
+ PasswordMetrics passwordMetricsNoSymbols = computeForPasswordOrPin(
+ "1234".getBytes(), /* isPin */ true);
setActivePasswordState(passwordMetricsNoSymbols);
assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_LOW);
assertThat(dpm.isActivePasswordSufficient()).isFalse();
reset(mContext.spiedContext);
- passwordMetricsNoSymbols = computeForPassword("84125312943a".getBytes());
+ passwordMetricsNoSymbols = computeForPasswordOrPin(
+ "84125312943a".getBytes(), /* isPin */ false);
setActivePasswordState(passwordMetricsNoSymbols);
assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_HIGH);
// using isActivePasswordSufficient
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 679d690..d62f83c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -36,9 +36,7 @@
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.hardware.authsecret.V1_0.IAuthSecret;
-import android.hardware.face.Face;
import android.hardware.face.FaceManager;
-import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.os.FileUtils;
import android.os.IProgressListener;
@@ -261,13 +259,12 @@
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Fingerprint fp = (Fingerprint) invocation.getArguments()[0];
FingerprintManager.RemovalCallback callback =
- (FingerprintManager.RemovalCallback) invocation.getArguments()[2];
- callback.onRemovalSucceeded(fp, 0);
+ (FingerprintManager.RemovalCallback) invocation.getArguments()[1];
+ callback.onRemovalSucceeded(null, 0);
return null;
}
- }).when(mFingerprintManager).remove(any(), eq(userId), any());
+ }).when(mFingerprintManager).removeAll(eq(userId), any());
// Hardware must be detected and templates must be enrolled
@@ -277,13 +274,12 @@
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
- Face face = (Face) invocation.getArguments()[0];
FaceManager.RemovalCallback callback =
- (FaceManager.RemovalCallback) invocation.getArguments()[2];
- callback.onRemovalSucceeded(face, 0);
+ (FaceManager.RemovalCallback) invocation.getArguments()[1];
+ callback.onRemovalSucceeded(null, 0);
return null;
}
- }).when(mFaceManager).remove(any(), eq(userId), any());
+ }).when(mFaceManager).removeAll(eq(userId), any());
}
@After
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 4a42940..5d60a89 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -208,4 +208,9 @@
parcel.recycle();
}
}
-}
+
+ @Override
+ void setKeystorePassword(byte[] password, int userHandle) {
+
+ }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
index 2b9a05c..efa1b04 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
@@ -20,11 +20,16 @@
import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.os.Handler;
+import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -94,4 +99,11 @@
public int checkCallingOrSelfPermission(String permission) {
return PackageManager.PERMISSION_GRANTED;
}
+
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver,
+ UserHandle user, IntentFilter filter, String broadcastPermission,
+ Handler scheduler) {
+ return null;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index 49a54ec..aecc794 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -112,14 +112,13 @@
private MockableRebootEscrowInjected mInjected;
private RebootEscrowManager mService;
private SecretKey mAesKey;
+ private MockInjector mMockInjector;
public interface MockableRebootEscrowInjected {
int getBootCount();
long getCurrentTimeMillis();
- boolean forceServerBased();
-
void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete);
}
@@ -127,11 +126,12 @@
static class MockInjector extends RebootEscrowManager.Injector {
private final IRebootEscrow mRebootEscrow;
private final ResumeOnRebootServiceConnection mServiceConnection;
- private final RebootEscrowProviderInterface mRebootEscrowProvider;
+ private final RebootEscrowProviderInterface mDefaultRebootEscrowProvider;
private final UserManager mUserManager;
private final MockableRebootEscrowInjected mInjected;
private final RebootEscrowKeyStoreManager mKeyStoreManager;
- private final boolean mServerBased;
+ private boolean mServerBased;
+ private RebootEscrowProviderInterface mRebootEscrowProviderInUse;
MockInjector(Context context, UserManager userManager,
IRebootEscrow rebootEscrow,
@@ -149,7 +149,7 @@
return mRebootEscrow;
}
};
- mRebootEscrowProvider = new RebootEscrowProviderHalImpl(halInjector);
+ mDefaultRebootEscrowProvider = new RebootEscrowProviderHalImpl(halInjector);
mUserManager = userManager;
mKeyStoreManager = keyStoreManager;
mInjected = injected;
@@ -166,7 +166,8 @@
mServerBased = true;
RebootEscrowProviderServerBasedImpl.Injector injector =
new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection);
- mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(storage, injector);
+ mDefaultRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(
+ storage, injector);
mUserManager = userManager;
mKeyStoreManager = keyStoreManager;
mInjected = injected;
@@ -184,15 +185,23 @@
@Override
public boolean serverBasedResumeOnReboot() {
- if (mInjected.forceServerBased()) {
- return true;
- }
return mServerBased;
}
@Override
+ public RebootEscrowProviderInterface createRebootEscrowProviderIfNeeded() {
+ mRebootEscrowProviderInUse = mDefaultRebootEscrowProvider;
+ return mRebootEscrowProviderInUse;
+ }
+
+ @Override
public RebootEscrowProviderInterface getRebootEscrowProvider() {
- return mRebootEscrowProvider;
+ return mRebootEscrowProviderInUse;
+ }
+
+ @Override
+ public void clearRebootEscrowProvider() {
+ mRebootEscrowProviderInUse = null;
}
@Override
@@ -264,13 +273,15 @@
when(mCallbacks.isUserSecure(NONSECURE_SECONDARY_USER_ID)).thenReturn(false);
when(mCallbacks.isUserSecure(SECURE_SECONDARY_USER_ID)).thenReturn(true);
mInjected = mock(MockableRebootEscrowInjected.class);
- mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow,
- mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
+ mMockInjector = new MockInjector(mContext, mUserManager, mRebootEscrow,
+ mKeyStoreManager, mStorage, mInjected);
+ mService = new RebootEscrowManager(mMockInjector, mCallbacks, mStorage);
}
private void setServerBasedRebootEscrowProvider() throws Exception {
- mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager,
- mServiceConnection, mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
+ mMockInjector = new MockInjector(mContext, mUserManager, mServiceConnection,
+ mKeyStoreManager, mStorage, mInjected);
+ mService = new RebootEscrowManager(mMockInjector, mCallbacks, mStorage);
}
@Test
@@ -317,6 +328,7 @@
doThrow(ServiceSpecificException.class).when(mRebootEscrow).storeKey(any());
mService.clearRebootEscrow();
verify(mRebootEscrow).storeKey(eq(new byte[32]));
+ assertNull(mMockInjector.getRebootEscrowProvider());
}
@Test
@@ -785,7 +797,7 @@
assertNull(
mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
// Change the provider to server based, expect the reboot to fail
- when(mInjected.forceServerBased()).thenReturn(true);
+ mMockInjector.mServerBased = true;
assertEquals(ARM_REBOOT_ERROR_PROVIDER_MISMATCH, mService.armRebootEscrowIfNeeded());
assertNull(
mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 352832b..da6c30e 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1093,7 +1093,7 @@
// first, pretend that wifi network comes online. no policy active,
// which means we shouldn't push limit to interface.
snapshots = List.of(buildWifi());
- when(mConnManager.getAllNetworkStateSnapshot()).thenReturn(snapshots);
+ when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
mPolicyListener.expect().onMeteredIfacesChanged(any());
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
@@ -1101,7 +1101,7 @@
// now change cycle to be on 15th, and test in early march, to verify we
// pick cycle day in previous month.
- when(mConnManager.getAllNetworkStateSnapshot()).thenReturn(snapshots);
+ when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
// pretend that 512 bytes total have happened
stats = new NetworkStats(getElapsedRealtime(), 1)
@@ -1362,7 +1362,7 @@
.insertEntry(TEST_IFACE, 0L, 0L, 0L, 0L);
{
- when(mConnManager.getAllNetworkStateSnapshot()).thenReturn(snapshots);
+ when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
currentTimeMillis())).thenReturn(stats.getTotalBytes());
@@ -1485,7 +1485,7 @@
}
private PersistableBundle setupUpdateMobilePolicyCycleTests() throws RemoteException {
- when(mConnManager.getAllNetworkStateSnapshot())
+ when(mConnManager.getAllNetworkStateSnapshots())
.thenReturn(new ArrayList<NetworkStateSnapshot>());
setupTelephonySubscriptionManagers(FAKE_SUB_ID, FAKE_SUBSCRIBER_ID);
@@ -1498,7 +1498,7 @@
@Test
public void testUpdateMobilePolicyCycleWithNullConfig() throws RemoteException {
- when(mConnManager.getAllNetworkStateSnapshot())
+ when(mConnManager.getAllNetworkStateSnapshots())
.thenReturn(new ArrayList<NetworkStateSnapshot>());
setupTelephonySubscriptionManagers(FAKE_SUB_ID, FAKE_SUBSCRIBER_ID);
@@ -2089,7 +2089,7 @@
TEST_NETWORK,
buildNetworkCapabilities(TEST_SUB_ID, roaming),
buildLinkProperties(TEST_IFACE), TEST_IMSI, TYPE_MOBILE));
- when(mConnManager.getAllNetworkStateSnapshot()).thenReturn(snapshots);
+ when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
}
private void expectDefaultCarrierConfig() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
index d888b92..876c845 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
@@ -37,7 +37,7 @@
@JvmStatic
@Parameterized.Parameters(name = "deferRebuild {0}")
- fun parameters() = arrayOf(true, false)
+ fun parameters() = arrayOf(/*true, */false)
}
private lateinit var mapper: OverlayReferenceMapper
@@ -55,11 +55,17 @@
fun targetWithOverlay() {
val target = mockTarget()
val overlay = mockOverlay()
- val existing = mapper.addInOrder(overlay)
+ val existing = mapper.addInOrder(overlay) {
+ assertThat(it).isEmpty()
+ }
assertEmpty()
- mapper.addInOrder(target, existing = existing)
+ mapper.addInOrder(target, existing = existing) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay))
- mapper.remove(target)
+ mapper.remove(target) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
assertEmpty()
}
@@ -78,22 +84,34 @@
)
)
)
- val existing = mapper.addInOrder(overlay0, overlay1)
+ val existing = mapper.addInOrder(overlay0, overlay1) {
+ assertThat(it).isEmpty()
+ }
assertEmpty()
- mapper.addInOrder(target, existing = existing)
+ mapper.addInOrder(target, existing = existing) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay0, overlay1))
- mapper.remove(overlay0)
+ mapper.remove(overlay0) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay1))
- mapper.remove(target)
+ mapper.remove(target) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
assertEmpty()
}
@Test
fun targetWithoutOverlay() {
val target = mockTarget()
- mapper.addInOrder(target)
+ mapper.addInOrder(target) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
- mapper.remove(target)
+ mapper.remove(target) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
assertEmpty()
}
@@ -101,11 +119,17 @@
fun overlayWithTarget() {
val target = mockTarget()
val overlay = mockOverlay()
- val existing = mapper.addInOrder(target)
+ val existing = mapper.addInOrder(target) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
- mapper.addInOrder(overlay, existing = existing)
+ mapper.addInOrder(overlay, existing = existing) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay))
- mapper.remove(overlay)
+ mapper.remove(overlay) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
}
@@ -122,34 +146,52 @@
)
)
)
- mapper.addInOrder(target0, target1, overlay)
+ mapper.addInOrder(target0, target1, overlay) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
assertMapping(ACTOR_PACKAGE_NAME to setOf(target0, target1, overlay))
- mapper.remove(target0)
+ mapper.remove(target0) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
assertMapping(ACTOR_PACKAGE_NAME to setOf(target1, overlay))
- mapper.remove(target1)
+ mapper.remove(target1) {
+ assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+ }
assertEmpty()
}
@Test
fun overlayWithoutTarget() {
val overlay = mockOverlay()
- mapper.addInOrder(overlay)
+ mapper.addInOrder(overlay) {
+ assertThat(it).isEmpty()
+ }
// An overlay can only have visibility exposed through its target
assertEmpty()
- mapper.remove(overlay)
+ mapper.remove(overlay) {
+ assertThat(it).isEmpty()
+ }
assertEmpty()
}
private fun OverlayReferenceMapper.addInOrder(
vararg pkgs: AndroidPackage,
- existing: MutableMap<String, AndroidPackage> = mutableMapOf()
- ) = pkgs.fold(existing) { map, pkg ->
- addPkg(pkg, map)
- map[pkg.packageName] = pkg
- return@fold map
+ existing: MutableMap<String, AndroidPackage> = mutableMapOf(),
+ assertion: (changedPackages: Set<String>) -> Unit
+ ): MutableMap<String, AndroidPackage> {
+ val changedPackages = mutableSetOf<String>()
+ pkgs.forEach {
+ changedPackages += addPkg(it, existing)
+ existing[it.packageName] = it
+ }
+ assertion(changedPackages)
+ return existing
}
- private fun OverlayReferenceMapper.remove(pkg: AndroidPackage) = removePkg(pkg.packageName)
+ private fun OverlayReferenceMapper.remove(
+ pkg: AndroidPackage,
+ assertion: (changedPackages: Set<String>) -> Unit
+ ) = assertion(removePkg(pkg.packageName))
private fun assertMapping(vararg pairs: Pair<String, Set<AndroidPackage>>) {
val expected = pairs.associate { it }
diff --git a/services/tests/servicestests/src/com/android/server/om/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/om/TEST_MAPPING
index 8070bd1..558e259 100644
--- a/services/tests/servicestests/src/com/android/server/om/TEST_MAPPING
+++ b/services/tests/servicestests/src/com/android/server/om/TEST_MAPPING
@@ -7,6 +7,14 @@
"include-filter": "com.android.server.om."
}
]
+ },
+ {
+ "name": "PackageManagerServiceHostTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.pm.test.OverlayActorVisibilityTest"
+ }
+ ]
}
]
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 7709edb..6c1e915b 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -135,24 +135,41 @@
private static final String PARENT_NOTIFICATION_CHANNEL_ID = "test";
private static final long MILLIS_PER_MINUTE = 1000L * 60L;
- @Mock private Context mContext;
- @Mock private ShortcutServiceInternal mShortcutServiceInternal;
- @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal;
- @Mock private PackageManagerInternal mPackageManagerInternal;
- @Mock private NotificationManagerInternal mNotificationManagerInternal;
- @Mock private UserManager mUserManager;
- @Mock private PackageManager mPackageManager;
- @Mock private TelephonyManager mTelephonyManager;
- @Mock private TelecomManager mTelecomManager;
- @Mock private ContentResolver mContentResolver;
- @Mock private JobScheduler mJobScheduler;
- @Mock private StatusBarNotification mStatusBarNotification;
- @Mock private Notification mNotification;
- @Mock private AlarmManager mAlarmManager;
+ @Mock
+ private Context mContext;
+ @Mock
+ private ShortcutServiceInternal mShortcutServiceInternal;
+ @Mock
+ private UsageStatsManagerInternal mUsageStatsManagerInternal;
+ @Mock
+ private PackageManagerInternal mPackageManagerInternal;
+ @Mock
+ private NotificationManagerInternal mNotificationManagerInternal;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private TelecomManager mTelecomManager;
+ @Mock
+ private ContentResolver mContentResolver;
+ @Mock
+ private JobScheduler mJobScheduler;
+ @Mock
+ private StatusBarNotification mStatusBarNotification;
+ @Mock
+ private Notification mNotification;
+ @Mock
+ private AlarmManager mAlarmManager;
- @Captor private ArgumentCaptor<ShortcutChangeCallback> mShortcutChangeCallbackCaptor;
- @Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
- @Captor private ArgumentCaptor<Integer> mQueryFlagsCaptor;
+ @Captor
+ private ArgumentCaptor<ShortcutChangeCallback> mShortcutChangeCallbackCaptor;
+ @Captor
+ private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+ @Captor
+ private ArgumentCaptor<Integer> mQueryFlagsCaptor;
private ScheduledExecutorService mExecutorService;
private NotificationChannel mNotificationChannel;
@@ -556,6 +573,7 @@
TEST_SHORTCUT_ID_2,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut2);
+ mLooper.dispatchAll();
ConversationChannel conversationChannel2 = mDataManager.getConversation(TEST_PKG_NAME,
USER_ID_PRIMARY,
TEST_SHORTCUT_ID_2);
@@ -583,6 +601,7 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
+ mLooper.dispatchAll();
PeopleService.ConversationsListener listener = mock(
PeopleService.ConversationsListener.class);
mDataManager.addConversationsListener(listener);
@@ -631,7 +650,7 @@
public void testGetConversation() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
- TEST_SHORTCUT_ID)).isNull();
+ TEST_SHORTCUT_ID)).isNull();
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
@@ -781,16 +800,25 @@
@Test
public void testShortcutAddedOrUpdated() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ PeopleService.ConversationsListener listener = mock(
+ PeopleService.ConversationsListener.class);
+ mDataManager.addConversationsListener(listener);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mShortcutChangeCallback.onShortcutsAddedOrUpdated(TEST_PKG_NAME,
Collections.singletonList(shortcut), UserHandle.of(USER_ID_PRIMARY));
+ mLooper.dispatchAll();
List<ConversationInfo> conversations = getConversationsInPrimary();
assertEquals(1, conversations.size());
assertEquals(TEST_SHORTCUT_ID, conversations.get(0).getShortcutId());
+ ArgumentCaptor<List<ConversationChannel>> capturedConversation = ArgumentCaptor.forClass(
+ List.class);
+ verify(listener, times(1)).onConversationsUpdate(capturedConversation.capture());
+ ConversationChannel result = Iterables.getOnlyElement(capturedConversation.getValue());
+ assertEquals(result.getShortcutInfo().getId(), TEST_SHORTCUT_ID);
}
@Test
@@ -978,8 +1006,13 @@
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs1);
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs3);
+ mLooper.dispatchAll();
+ PeopleService.ConversationsListener listener = mock(
+ PeopleService.ConversationsListener.class);
+ mDataManager.addConversationsListener(listener);
mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+ mLooper.dispatchAll();
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.doesNotContain(cs1);
@@ -987,6 +1020,13 @@
.contains(cs2);
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.contains(cs3);
+ ArgumentCaptor<List<ConversationChannel>> capturedConversation = ArgumentCaptor.forClass(
+ List.class);
+ verify(listener, times(1)).onConversationsUpdate(capturedConversation.capture());
+ List<ConversationChannel> results = capturedConversation.getValue();
+ ConversationChannel result = Iterables.getOnlyElement(capturedConversation.getValue());
+ // CHeck cs1 has been removed and only cs2 and cs3 remain.
+ assertThat(result.getStatuses()).containsExactly(cs2, cs3);
}
@Test
@@ -1236,13 +1276,23 @@
ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
+ mLooper.dispatchAll();
+ PeopleService.ConversationsListener listener = mock(
+ PeopleService.ConversationsListener.class);
+ mDataManager.addConversationsListener(listener);
mDataManager.clearStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2.getId());
+ mLooper.dispatchAll();
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.contains(cs);
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.doesNotContain(cs2);
+ ArgumentCaptor<List<ConversationChannel>> capturedConversation = ArgumentCaptor.forClass(
+ List.class);
+ verify(listener, times(1)).onConversationsUpdate(capturedConversation.capture());
+ ConversationChannel result = Iterables.getOnlyElement(capturedConversation.getValue());
+ assertThat(result.getStatuses()).containsExactly(cs);
}
@Test
@@ -1257,11 +1307,21 @@
ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
+ mLooper.dispatchAll();
+ PeopleService.ConversationsListener listener = mock(
+ PeopleService.ConversationsListener.class);
+ mDataManager.addConversationsListener(listener);
mDataManager.clearStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID);
+ mLooper.dispatchAll();
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.isEmpty();
+ ArgumentCaptor<List<ConversationChannel>> capturedConversation = ArgumentCaptor.forClass(
+ List.class);
+ verify(listener, times(1)).onConversationsUpdate(capturedConversation.capture());
+ ConversationChannel result = Iterables.getOnlyElement(capturedConversation.getValue());
+ assertThat(result.getStatuses()).isEmpty();
}
@Test
@@ -1403,7 +1463,7 @@
.setLongLived(true)
.setIntent(new Intent("TestIntent"));
if (person != null) {
- builder.setPersons(new Person[] {person});
+ builder.setPersons(new Person[]{person});
}
return builder.build();
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 9f428c7..67dd055 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -799,10 +799,18 @@
simulateAddBasicAndroid(appsFilter);
appsFilter.onSystemReady();
- PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_APPID);
+ // Packages must be added in actor -> overlay -> target order so that the implicit
+ // visibility of the actor into the overlay can be tested
+
+ PackageSetting actorSetting = simulateAddPackage(appsFilter, actor, DUMMY_ACTOR_APPID);
PackageSetting overlaySetting =
simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_APPID);
- PackageSetting actorSetting = simulateAddPackage(appsFilter, actor, DUMMY_ACTOR_APPID);
+
+ // Actor can not see overlay (yet)
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSetting,
+ overlaySetting, SYSTEM_USER));
+
+ PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_APPID);
// Actor can see both target and overlay
assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSetting,
@@ -821,6 +829,12 @@
actorSetting, SYSTEM_USER));
assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_APPID, overlaySetting,
actorSetting, SYSTEM_USER));
+
+ appsFilter.removePackage(targetSetting);
+
+ // Actor loses visibility to the overlay via removal of the target
+ assertTrue(appsFilter.shouldFilterApplication(DUMMY_ACTOR_APPID, actorSetting,
+ overlaySetting, SYSTEM_USER));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 198fb4f..b9f70da 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -125,6 +125,7 @@
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -2598,6 +2599,13 @@
}).when(mMockPackageManagerInternal).getHomeActivitiesAsUser(any(List.class), eq(userId));
}
+ protected void prepareIntentActivities(ComponentName cn) {
+ when(mMockPackageManagerInternal.queryIntentActivities(
+ anyOrNull(Intent.class), anyStringOrNull(), anyInt(), anyInt(), anyInt()))
+ .thenReturn(Collections.singletonList(
+ ri(cn.getPackageName(), cn.getClassName(), false, 0)));
+ }
+
protected static ComponentName cn(String packageName, String name) {
return new ComponentName(packageName, name);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java
index eceb17a..e92c849 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest10.java
@@ -159,6 +159,7 @@
public void testStartConfigActivity_defaultLauncher() {
LauncherActivityInfo info = setupMockActivityInfo();
+ prepareIntentActivities(info.getComponentName());
setDefaultLauncher(USER_0, LAUNCHER_1);
runWithCaller(LAUNCHER_1, USER_0, () ->
assertNotNull(mLauncherApps.getShortcutConfigActivityIntent(info))
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index c11ac3a..6722fff 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -213,7 +213,7 @@
mAssistants.loadDefaultsFromConfig();
assertEquals(new ArraySet<>(Arrays.asList(oldDefaultComponent)),
mAssistants.getDefaultComponents());
- assertNull(mAssistants.getDefaultFromConfig());
+ assertNull(mAssistants.mDefaultFromConfig);
// Test loadDefaultFromConfig(false) only updates the mDefaultFromConfig
when(mContext.getResources().getString(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
index 809b6d5..182848b4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
@@ -123,6 +123,7 @@
for (long i = cal.getTimeInMillis(); i >= 5; i--) {
File file = mock(File.class);
when(file.getName()).thenReturn(String.valueOf(i));
+ when(file.getAbsolutePath()).thenReturn(String.valueOf(i));
AtomicFile af = new AtomicFile(file);
expectedFiles.add(af);
mDataBase.mHistoryFiles.addLast(af);
@@ -133,6 +134,7 @@
for (int i = 5; i >= 0; i--) {
File file = mock(File.class);
when(file.getName()).thenReturn(String.valueOf(cal.getTimeInMillis() - i));
+ when(file.getAbsolutePath()).thenReturn(String.valueOf(cal.getTimeInMillis() - i));
AtomicFile af = new AtomicFile(file);
mDataBase.mHistoryFiles.addLast(af);
}
@@ -158,6 +160,7 @@
for (long i = cal.getTimeInMillis(); i >= 5; i--) {
File file = mock(File.class);
when(file.getName()).thenReturn(i + ".bak");
+ when(file.getAbsolutePath()).thenReturn(i + ".bak");
AtomicFile af = new AtomicFile(file);
mDataBase.mHistoryFiles.addLast(af);
}
@@ -415,4 +418,36 @@
assertThat(mDataBase.mBuffer).isNotEqualTo(nh);
verify(mAlarmManager, times(1)).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
}
+
+ @Test
+ public void testRemoveFilePathFromHistory_hasMatch() throws Exception {
+ for (int i = 0; i < 5; i++) {
+ AtomicFile af = mock(AtomicFile.class);
+ when(af.getBaseFile()).thenReturn(new File(mRootDir, "af" + i));
+ mDataBase.mHistoryFiles.addLast(af);
+ }
+ // Baseline size of history files
+ assertThat(mDataBase.mHistoryFiles.size()).isEqualTo(5);
+
+ // Remove only file number 3
+ String filePathToRemove = new File(mRootDir, "af3").getAbsolutePath();
+ mDataBase.removeFilePathFromHistory(filePathToRemove);
+ assertThat(mDataBase.mHistoryFiles.size()).isEqualTo(4);
+ }
+
+ @Test
+ public void testRemoveFilePathFromHistory_noMatch() throws Exception {
+ for (int i = 0; i < 5; i++) {
+ AtomicFile af = mock(AtomicFile.class);
+ when(af.getBaseFile()).thenReturn(new File(mRootDir, "af" + i));
+ mDataBase.mHistoryFiles.addLast(af);
+ }
+ // Baseline size of history files
+ assertThat(mDataBase.mHistoryFiles.size()).isEqualTo(5);
+
+ // Attempt to remove a filename that doesn't exist, expect nothing to break or change
+ String filePathToRemove = new File(mRootDir, "af.thisfileisfake").getAbsolutePath();
+ mDataBase.removeFilePathFromHistory(filePathToRemove);
+ assertThat(mDataBase.mHistoryFiles.size()).isEqualTo(5);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 87efaa2..a810acc 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -5057,7 +5057,8 @@
}
@Test
- public void testToastRateLimiterCanPreventShowCallForCustomToast() throws Exception {
+ public void testToastRateLimiterWontPreventShowCallForCustomToastWhenInForeground()
+ throws Exception {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
@@ -5075,30 +5076,7 @@
INotificationManager nmService = (INotificationManager) mService.mService;
nmService.enqueueToast(testPackage, token, callback, 2000, 0);
- verify(callback, times(0)).show(any());
- }
-
- @Test
- public void testCustomToastRateLimiterAllowsLimitAvoidanceWithPermission() throws Exception {
- final String testPackage = "testPackageName";
- assertEquals(0, mService.mToastQueue.size());
- mService.isSystemUid = false;
- setToastRateIsWithinQuota(false); // rate limit reached
- // Avoids rate limiting.
- setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, true);
-
- // package is not suspended
- when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
- .thenReturn(false);
-
- setAppInForegroundForToasts(mUid, true);
-
- Binder token = new Binder();
- ITransientNotification callback = mock(ITransientNotification.class);
- INotificationManager nmService = (INotificationManager) mService.mService;
-
- nmService.enqueueToast(testPackage, token, callback, 2000, 0);
- verify(callback).show(any());
+ verify(callback, times(1)).show(any());
}
@Test
@@ -5206,12 +5184,14 @@
}
@Test
- public void testToastRateLimiterCanPreventShowCallForTextToast() throws Exception {
+ public void testToastRateLimiterCanPreventShowCallForTextToast_whenInBackground()
+ throws Exception {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(false); // rate limit reached
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
+ setAppInForegroundForToasts(mUid, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5226,12 +5206,35 @@
}
@Test
+ public void testToastRateLimiterWontPreventShowCallForTextToast_whenInForeground()
+ throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+ setToastRateIsWithinQuota(false); // rate limit reached
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
+ setAppInForegroundForToasts(mUid, true);
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ Binder token = new Binder();
+ INotificationManager nmService = (INotificationManager) mService.mService;
+
+ nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null);
+ verify(mStatusBar, times(1))
+ .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any());
+ }
+
+ @Test
public void testTextToastRateLimiterAllowsLimitAvoidanceWithPermission() throws Exception {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(false); // rate limit reached
setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, true);
+ setAppInForegroundForToasts(mUid, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5750,11 +5753,12 @@
}
@Test
- public void testNASSettingUpgrade_userSetNull_showOnBoarding() throws RemoteException {
+ public void testNASSettingUpgrade_userSetNull_noOnBoarding() throws RemoteException {
ComponentName newDefaultComponent = ComponentName.unflattenFromString("package/Component1");
TestableNotificationManagerService service = spy(mService);
int userId = 11;
setUsers(new int[]{userId});
+ when(mUm.getProfileIds(userId, false)).thenReturn(new int[]{userId});
setNASMigrationDone(false, userId);
when(mAssistants.getDefaultFromConfig())
.thenReturn(newDefaultComponent);
@@ -5763,29 +5767,29 @@
when(mAssistants.hasUserSet(userId)).thenReturn(true);
service.migrateDefaultNASShowNotificationIfNecessary();
- assertFalse(service.isNASMigrationDone(userId));
- verify(service, times(1)).createNASUpgradeNotification(eq(userId));
- verify(mAssistants, times(0)).resetDefaultFromConfig();
+ assertTrue(service.isNASMigrationDone(userId));
+ verify(service, times(0)).createNASUpgradeNotification(eq(userId));
+ verify(mAssistants, times(1)).clearDefaults();
+ }
- //Test user clear data before enable/disable from onboarding notification
- ArrayMap<Boolean, ArrayList<ComponentName>> changedListeners =
- generateResetComponentValues();
- when(mListeners.resetComponents(anyString(), anyInt())).thenReturn(changedListeners);
- ArrayMap<Boolean, ArrayList<ComponentName>> changes = new ArrayMap<>();
- changes.put(true, new ArrayList(Arrays.asList(newDefaultComponent)));
- changes.put(false, new ArrayList());
- when(mAssistants.resetComponents(anyString(), anyInt())).thenReturn(changes);
+ @Test
+ public void testNASSettingUpgrade_userSetSameDefault_noOnBoarding() throws RemoteException {
+ ComponentName defaultComponent = ComponentName.unflattenFromString("package/Component1");
+ TestableNotificationManagerService service = spy(mService);
+ int userId = 11;
+ setUsers(new int[]{userId});
+ when(mUm.getProfileIds(userId, false)).thenReturn(new int[]{userId});
+ setNASMigrationDone(false, userId);
+ when(mAssistants.getDefaultFromConfig())
+ .thenReturn(defaultComponent);
+ when(mAssistants.getAllowedComponents(anyInt()))
+ .thenReturn(new ArrayList(Arrays.asList(defaultComponent)));
+ when(mAssistants.hasUserSet(userId)).thenReturn(true);
- //Clear data
- service.getBinderService().clearData("package", userId, false);
- //Test migrate flow again
service.migrateDefaultNASShowNotificationIfNecessary();
-
- //The notification should be still there
- assertFalse(service.isNASMigrationDone(userId));
- verify(service, times(2)).createNASUpgradeNotification(eq(userId));
- verify(mAssistants, times(0)).resetDefaultFromConfig();
- assertEquals(null, service.getApprovedAssistant(userId));
+ assertTrue(service.isNASMigrationDone(userId));
+ verify(service, times(0)).createNASUpgradeNotification(eq(userId));
+ verify(mAssistants, times(1)).resetDefaultFromConfig();
}
@Test
@@ -5839,6 +5843,9 @@
int userId1 = 11;
int userId2 = 12;
setUsers(new int[]{userId1, userId2});
+ when(mUm.getProfileIds(userId1, false)).thenReturn(new int[]{userId1});
+ when(mUm.getProfileIds(userId2, false)).thenReturn(new int[]{userId2});
+
setNASMigrationDone(false, userId1);
setNASMigrationDone(false, userId2);
when(mAssistants.getDefaultComponents())
@@ -5865,6 +5872,43 @@
}
@Test
+ public void testNASSettingUpgrade_multiProfile() throws RemoteException {
+ ComponentName oldDefaultComponent = ComponentName.unflattenFromString("package/Component1");
+ ComponentName newDefaultComponent = ComponentName.unflattenFromString("package/Component2");
+ TestableNotificationManagerService service = spy(mService);
+ int userId1 = 11;
+ int userId2 = 12; //work profile
+ setUsers(new int[]{userId1, userId2});
+ when(mUm.isManagedProfile(userId2)).thenReturn(true);
+ when(mUm.getProfileIds(userId1, false)).thenReturn(new int[]{userId1, userId2});
+
+ setNASMigrationDone(false, userId1);
+ setNASMigrationDone(false, userId2);
+ when(mAssistants.getDefaultComponents())
+ .thenReturn(new ArraySet<>(Arrays.asList(oldDefaultComponent)));
+ when(mAssistants.getDefaultFromConfig())
+ .thenReturn(newDefaultComponent);
+ //Both profiles: need onboarding
+ when(mAssistants.getAllowedComponents(userId1))
+ .thenReturn(Arrays.asList(oldDefaultComponent));
+ when(mAssistants.getAllowedComponents(userId2))
+ .thenReturn(Arrays.asList(oldDefaultComponent));
+
+ when(mAssistants.hasUserSet(userId1)).thenReturn(true);
+ when(mAssistants.hasUserSet(userId2)).thenReturn(true);
+
+ service.migrateDefaultNASShowNotificationIfNecessary();
+ assertFalse(service.isNASMigrationDone(userId1));
+ assertFalse(service.isNASMigrationDone(userId2));
+
+ // only user1 get notification
+ verify(service, times(1)).createNASUpgradeNotification(eq(userId1));
+ verify(service, times(0)).createNASUpgradeNotification(eq(userId2));
+ }
+
+
+
+ @Test
public void testNASSettingUpgrade_clearDataAfterMigrationIsDone() throws RemoteException {
ComponentName defaultComponent = ComponentName.unflattenFromString("package/Component");
TestableNotificationManagerService service = spy(mService);
@@ -5895,15 +5939,21 @@
}
@Test
- public void testNASUpgradeNotificationDisableBroadcast() {
- int userId = 11;
- setUsers(new int[]{userId});
+ public void testNASUpgradeNotificationDisableBroadcast_multiProfile() {
+ int userId1 = 11;
+ int userId2 = 12;
+ setUsers(new int[]{userId1, userId2});
+ when(mUm.isManagedProfile(userId2)).thenReturn(true);
+ when(mUm.getProfileIds(userId1, false)).thenReturn(new int[]{userId1, userId2});
+
TestableNotificationManagerService service = spy(mService);
- setNASMigrationDone(false, userId);
+ setNASMigrationDone(false, userId1);
+ setNASMigrationDone(false, userId2);
- simulateNASUpgradeBroadcast(ACTION_DISABLE_NAS, userId);
+ simulateNASUpgradeBroadcast(ACTION_DISABLE_NAS, userId1);
- assertTrue(service.isNASMigrationDone(userId));
+ assertTrue(service.isNASMigrationDone(userId1));
+ assertTrue(service.isNASMigrationDone(userId2));
// User disabled the NAS from notification, the default stored in xml should be null
// rather than the new default
verify(mAssistants, times(1)).clearDefaults();
@@ -5911,7 +5961,7 @@
//No more notification after disabled
service.migrateDefaultNASShowNotificationIfNecessary();
- verify(service, times(0)).createNASUpgradeNotification(eq(userId));
+ verify(service, times(0)).createNASUpgradeNotification(anyInt());
}
@Test
@@ -5919,6 +5969,8 @@
int userId1 = 11;
int userId2 = 12;
setUsers(new int[]{userId1, userId2});
+ when(mUm.getProfileIds(userId1, false)).thenReturn(new int[]{userId1});
+
TestableNotificationManagerService service = spy(mService);
setNASMigrationDone(false, userId1);
setNASMigrationDone(false, userId2);
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 124f6dd..8cede6d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2358,21 +2358,6 @@
}
@Test
- public void testAddRemoveRace() {
- registerTestStartingWindowOrganizer();
- // There was once a race condition between adding and removing starting windows
- final ActivityRecord appToken = new ActivityBuilder(mAtm).setCreateTask(true).build();
- for (int i = 0; i < 1000; i++) {
- appToken.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
- appToken.removeStartingWindow();
- waitUntilHandlersIdle();
- assertNoStartingWindow(appToken);
- }
- }
-
- @Test
public void testTransferStartingWindow() {
registerTestStartingWindowOrganizer();
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 0afd39f..2f52352 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1971,7 +1971,8 @@
// test misc display overrides
assertEquals(ignoreOrientationRequests, testDisplayContent.mIgnoreOrientationRequest);
- assertEquals(fixedOrientationLetterboxRatio, mWm.getFixedOrientationLetterboxAspectRatio(),
+ assertEquals(fixedOrientationLetterboxRatio,
+ mWm.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(),
0 /* delta */);
}
@@ -2011,7 +2012,8 @@
// test misc display overrides
assertEquals(ignoreOrientationRequests, testDisplayContent.mIgnoreOrientationRequest);
- assertEquals(fixedOrientationLetterboxRatio, mWm.getFixedOrientationLetterboxAspectRatio(),
+ assertEquals(fixedOrientationLetterboxRatio,
+ mWm.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(),
0 /* delta */);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 5d6a5c0..73404eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -641,7 +641,6 @@
@Test
public void testVisibleTasks_excludedFromRecents() {
- mRecentTasks.setOnlyTestVisibleRange();
mRecentTasks.setParameters(-1 /* min */, 4 /* max */, -1 /* ms */);
Task excludedTask1 = createTaskBuilder(".ExcludedTask1")
@@ -650,15 +649,26 @@
Task excludedTask2 = createTaskBuilder(".ExcludedTask2")
.setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
.build();
+ Task detachedExcludedTask = createTaskBuilder(".DetachedExcludedTask")
+ .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ .build();
+ // Move home to front so other task can satisfy the condition in RecentTasks#isTrimmable.
+ mRootWindowContainer.getDefaultTaskDisplayArea().getRootHomeTask().moveToFront("test");
+ // Avoid Task#autoRemoveFromRecents when removing from parent.
+ detachedExcludedTask.setHasBeenVisible(true);
+ detachedExcludedTask.removeImmediately();
+ assertFalse(detachedExcludedTask.isAttached());
+
+ mRecentTasks.add(detachedExcludedTask);
mRecentTasks.add(excludedTask1);
mRecentTasks.add(mTasks.get(0));
mRecentTasks.add(mTasks.get(1));
mRecentTasks.add(mTasks.get(2));
mRecentTasks.add(excludedTask2);
- // The last excluded task should be trimmed, while the first-most excluded task should not
- triggerTrimAndAssertTrimmed(excludedTask1);
+ // Except the first-most excluded task, other excluded tasks should be trimmed.
+ triggerTrimAndAssertTrimmed(excludedTask1, detachedExcludedTask);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 153fd3a..23d57b8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -518,6 +518,7 @@
verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
mDefaultDisplay.mDisplayId, false);
verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
+ verify(transaction).setLayer(navToken.getSurfaceControl(), Integer.MAX_VALUE);
final WindowContainer parent = navToken.getParent();
final NavBarFadeAnimationController navBarFadeAnimationController =
@@ -526,6 +527,7 @@
mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
mDefaultDisplay.mDisplayId, true);
+ verify(transaction).setLayer(navToken.getSurfaceControl(), 0);
verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
verify(navBarFadeAnimationController).fadeWindowToken(true);
}
@@ -543,6 +545,7 @@
verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
mDefaultDisplay.mDisplayId, false);
verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
+ verify(transaction).setLayer(navToken.getSurfaceControl(), Integer.MAX_VALUE);
final WindowContainer parent = navToken.getParent();
final NavBarFadeAnimationController navBarFadeAnimationController =
@@ -551,6 +554,7 @@
mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
mDefaultDisplay.mDisplayId, true);
+ verify(transaction).setLayer(navToken.getSurfaceControl(), 0);
verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean());
}
@@ -579,6 +583,7 @@
verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
mDefaultDisplay.mDisplayId, false);
verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
+ verify(transaction).setLayer(navToken.getSurfaceControl(), Integer.MAX_VALUE);
final WindowContainer parent = navToken.getParent();
final NavBarFadeAnimationController navBarFadeAnimationController =
@@ -591,6 +596,7 @@
mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
mDefaultDisplay.mDisplayId, true);
+ verify(transaction).setLayer(navToken.getSurfaceControl(), 0);
verify(mockController).setOnShowRunnable(any());
verify(transaction, times(0)).reparent(navToken.getSurfaceControl(),
parent.getSurfaceControl());
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 7c2cfab..95b7443 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -335,11 +335,11 @@
final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, mActivity, "window");
assertEquals(window, mActivity.findMainWindow());
- assertTrue(mActivity.isLetterboxed(mActivity.findMainWindow()));
+ assertTrue(mActivity.mLetterboxUiController.isLetterboxed(mActivity.findMainWindow()));
window.mAttrs.flags |= FLAG_SHOW_WALLPAPER;
- assertFalse(mActivity.isLetterboxed(mActivity.findMainWindow()));
+ assertFalse(mActivity.mLetterboxUiController.isLetterboxed(mActivity.findMainWindow()));
}
@Test
@@ -1023,7 +1023,7 @@
// Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed
// orientation letterbox.
- mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(1.1f);
+ mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
mActivity.info.setMinAspectRatio(3);
prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT);
@@ -1054,7 +1054,7 @@
// Portrait fixed app with max aspect ratio lower that aspect ratio override for fixed
// orientation letterbox.
- mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(3);
+ mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(3);
prepareUnresizable(mActivity, /* maxAspect= */ 2, SCREEN_ORIENTATION_PORTRAIT);
final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
@@ -1085,7 +1085,7 @@
// Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed
// orientation letterbox.
final float fixedOrientationLetterboxAspectRatio = 1.1f;
- mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(
+ mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(
fixedOrientationLetterboxAspectRatio);
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
@@ -1515,6 +1515,129 @@
assertEquals(primarySplitBounds, letterboxedBounds);
}
+ @Test
+ public void testUpdateResolvedBoundsHorizontalPosition_left() {
+ // Display configured as (2800, 1400).
+ assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
+ /* letterboxHorizontalPositionMultiplier */ 0.0f,
+ // At launch.
+ /* fixedOrientationLetterbox */ new Rect(0, 0, 700, 1400),
+ // After 90 degree rotation.
+ /* sizeCompatUnscaled */ new Rect(0, 0, 700, 1400),
+ // After the display is resized to (700, 1400).
+ /* sizeCompatScaled */ new Rect(0, 0, 350, 700));
+ }
+
+ @Test
+ public void testUpdateResolvedBoundsHorizontalPosition_center() {
+ // Display configured as (2800, 1400).
+ assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
+ /* letterboxHorizontalPositionMultiplier */ 0.5f,
+ // At launch.
+ /* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
+ // After 90 degree rotation.
+ /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
+ // After the display is resized to (700, 1400).
+ /* sizeCompatScaled */ new Rect(525, 0, 875, 700));
+ }
+
+ @Test
+ public void testUpdateResolvedBoundsHorizontalPosition_invalidMultiplier_defaultToCenter() {
+ // Display configured as (2800, 1400).
+
+ // Below 0.0.
+ assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
+ /* letterboxHorizontalPositionMultiplier */ -1.0f,
+ // At launch.
+ /* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
+ // After 90 degree rotation.
+ /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
+ // After the display is resized to (700, 1400).
+ /* sizeCompatScaled */ new Rect(525, 0, 875, 700));
+
+ // Above 1.0
+ assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
+ /* letterboxHorizontalPositionMultiplier */ 2.0f,
+ // At launch.
+ /* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
+ // After 90 degree rotation.
+ /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
+ // After the display is resized to (700, 1400).
+ /* sizeCompatScaled */ new Rect(525, 0, 875, 700));
+ }
+
+ @Test
+ public void testUpdateResolvedBoundsHorizontalPosition_right() {
+ // Display configured as (2800, 1400).
+ assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
+ /* letterboxHorizontalPositionMultiplier */ 1.0f,
+ // At launch.
+ /* fixedOrientationLetterbox */ new Rect(2100, 0, 2800, 1400),
+ // After 90 degree rotation.
+ /* sizeCompatUnscaled */ new Rect(700, 0, 1400, 1400),
+ // After the display is resized to (700, 1400).
+ /* sizeCompatScaled */ new Rect(1050, 0, 1400, 700));
+ }
+
+ private void assertHorizontalPositionForDifferentDisplayConfigsForPortraitActivity(
+ float letterboxHorizontalPositionMultiplier, Rect fixedOrientationLetterbox,
+ Rect sizeCompatUnscaled, Rect sizeCompatScaled) {
+ // Set up a display in landscape and ignoring orientation request.
+ setUpDisplaySizeWithApp(2800, 1400);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
+ letterboxHorizontalPositionMultiplier);
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ assertEquals(fixedOrientationLetterbox, mActivity.getBounds());
+
+ // Rotate to put activity in size compat mode.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+ assertTrue(mActivity.inSizeCompatMode());
+ // Activity is in size compat mode but not scaled.
+ assertEquals(sizeCompatUnscaled, mActivity.getBounds());
+
+ // Force activity to scaled down for size compat mode.
+ resizeDisplay(mTask.mDisplayContent, 700, 1400);
+
+ assertTrue(mActivity.inSizeCompatMode());
+ assertScaled();
+ assertEquals(sizeCompatScaled, mActivity.getBounds());
+ }
+
+ @Test
+ public void testUpdateResolvedBoundsHorizontalPosition_activityFillParentWidth() {
+ // When activity width equals parent width, multiplier shouldn't have any effect.
+ assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
+ /* letterboxHorizontalPositionMultiplier */ 0.0f);
+ assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
+ /* letterboxHorizontalPositionMultiplier */ 0.5f);
+ assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
+ /* letterboxHorizontalPositionMultiplier */ 1.0f);
+ }
+
+ private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
+ float letterboxHorizontalPositionMultiplier) {
+ // Set up a display in landscape and ignoring orientation request.
+ setUpDisplaySizeWithApp(2800, 1400);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
+ letterboxHorizontalPositionMultiplier);
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ assertFitted();
+
+ // Rotate to put activity in size compat mode.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+ assertTrue(mActivity.inSizeCompatMode());
+ // Activity is in size compat mode but not scaled.
+ assertEquals(new Rect(0, 0, 1400, 700), mActivity.getBounds());
+ }
+
private static WindowState addWindowToActivity(ActivityRecord activity) {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
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 f3616da6c..619aee6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import android.annotation.NonNull;
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.Region;
@@ -253,4 +255,14 @@
public SurfaceControl.Transaction unsetFixedTransformHint(@NonNull SurfaceControl sc) {
return this;
}
+
+ @Override
+ public SurfaceControl.Transaction setBuffer(SurfaceControl sc, GraphicBuffer buffer) {
+ return this;
+ }
+
+ @Override
+ public SurfaceControl.Transaction setColorSpace(SurfaceControl sc, ColorSpace colorSpace) {
+ return this;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index dd0c9e6..d9aa871 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -23,6 +23,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
@@ -50,7 +51,13 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.view.IWindowSessionCallback;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.View;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -250,4 +257,31 @@
eq(clientToken), eq(windowToken), anyInt(), eq(TYPE_INPUT_METHOD),
eq(windowToken.mOptions));
}
+
+ @Test
+ public void testAddWindowWithSubWindowTypeByWindowContext() {
+ spyOn(mWm.mWindowContextListenerController);
+
+ final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD, mDefaultDisplay);
+ final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
+ @Override
+ public void onAnimatorScaleChanged(float v) throws RemoteException {}
+ });
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ TYPE_APPLICATION_ATTACHED_DIALOG);
+ params.token = windowToken.token;
+ final IBinder windowContextToken = new Binder();
+ params.setWindowContextToken(windowContextToken);
+ doReturn(true).when(mWm.mWindowContextListenerController)
+ .hasListener(eq(windowContextToken));
+ doReturn(TYPE_INPUT_METHOD).when(mWm.mWindowContextListenerController)
+ .getWindowType(eq(windowContextToken));
+
+ mWm.addWindow(session, new TestIWindow(), params, View.VISIBLE, DEFAULT_DISPLAY,
+ UserHandle.USER_SYSTEM, new InsetsState(), null, new InsetsState(),
+ new InsetsSourceControl[0]);
+
+ verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(any(),
+ any(), anyInt(), anyInt(), any());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 5bafbbd..2d4e4ef 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -101,6 +101,7 @@
import com.android.internal.policy.AttributeCache;
import com.android.internal.util.ArrayUtils;
+import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.runner.Description;
@@ -207,11 +208,26 @@
// Ensure letterbox aspect ratio is not overridden on any device target.
// {@link com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}, is set
// on some device form factors.
- mAtm.mWindowManager.setFixedOrientationLetterboxAspectRatio(0);
+ mAtm.mWindowManager.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(0);
+ // Ensure letterbox position multiplier is not overridden on any device target.
+ // {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier},
+ // may be set on some device form factors.
+ mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(0.5f);
checkDeviceSpecificOverridesNotApplied();
}
+ @After
+ public void tearDown() throws Exception {
+ // Revert back to device overrides.
+ mAtm.mWindowManager.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(
+ mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio));
+ mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
+ mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier));
+ }
+
/**
* Check that device-specific overrides are not applied. Only need to check once during entire
* test run for each case: global overrides, default display, and test display.
@@ -219,7 +235,8 @@
private void checkDeviceSpecificOverridesNotApplied() {
// Check global overrides
if (!sGlobalOverridesChecked) {
- assertEquals(0, mWm.getFixedOrientationLetterboxAspectRatio(), 0 /* delta */);
+ assertEquals(0, mWm.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(),
+ 0 /* delta */);
sGlobalOverridesChecked = true;
}
// Check display-specific overrides
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 2e692e6..7f24c36 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -2139,6 +2139,12 @@
Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
+ "forceRestart=" + forceRestart);
}
+ if (mCurrentGadgetHalVersion < UsbManager.GADGET_HAL_V1_2) {
+ if ((functions & UsbManager.FUNCTION_NCM) != 0) {
+ Slog.e(TAG, "Could not set unsupported function for the GadgetHal");
+ return;
+ }
+ }
if (mCurrentFunctions != functions
|| !mCurrentFunctionsApplied
|| forceRestart) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index bcfb302..ba7aaab 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -24,6 +24,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
+import android.content.ContentCaptureOptions;
import android.content.Context;
import android.content.Intent;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
@@ -34,10 +35,12 @@
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SharedMemory;
import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordDetectionService;
@@ -47,6 +50,7 @@
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.util.Pair;
import android.util.Slog;
+import android.view.contentcapture.IContentCaptureManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
@@ -135,6 +139,7 @@
return;
}
updateStateWithCallbackLocked(options, sharedMemory, callback);
+ updateContentCaptureManager();
}
private void updateStateWithCallbackLocked(PersistableBundle options,
@@ -193,6 +198,15 @@
});
}
+ private void updateContentCaptureManager() {
+ IBinder b = ServiceManager
+ .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
+ IContentCaptureManager binderService = IContentCaptureManager.Stub.asInterface(b);
+ mRemoteHotwordDetectionService.post(
+ service -> service.updateContentCaptureManager(binderService,
+ new ContentCaptureOptions(null)));
+ }
+
private boolean isBound() {
synchronized (mLock) {
return mBound;
@@ -434,7 +448,8 @@
}
try {
AudioRecord audioRecord = new AudioRecord(
- new AudioAttributes.Builder().setHotwordModeEnabled(true).build(),
+ new AudioAttributes.Builder()
+ .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD).build(),
audioFormat,
getBufferSizeInBytes(
audioFormat.getSampleRate(),
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 4886789..1953af4 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1011,6 +1011,7 @@
* Enable READ_PHONE_STATE protection on APIs querying and notifying call state, such as
* {@code TelecomManager#getCallState}, {@link TelephonyManager#getCallStateForSubscription()},
* and {@link android.telephony.TelephonyCallback.CallStateListener}.
+ * @hide
*/
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index 32533cb..0d6cd5a 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -760,6 +760,7 @@
private static void assignExclusiveSmsPermissionsToSystemApp(Context context,
PackageManager packageManager, AppOpsManager appOps, String packageName,
boolean sigatureMatch) {
+ if (packageName == null) return;
// First check package signature matches the caller's package signature.
// Since this class is only used internally by the system, this check makes sure
// the package signature matches system signature.
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 96e715e..1d7a476 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -62,6 +62,7 @@
switch (transportType) {
case TRANSPORT_TYPE_WWAN: return "WWAN";
case TRANSPORT_TYPE_WLAN: return "WLAN";
+ case TRANSPORT_TYPE_INVALID: return "INVALID";
default: return Integer.toString(transportType);
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index b914972..8b9fc0f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4216,7 +4216,6 @@
* it will override the framework default.
* @hide
*/
- @SystemApi
public static final String KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY =
KEY_PREFIX + "publish_service_desc_feature_tag_map_override_string_array";
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index bde62fb..ac01afa 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -134,7 +134,7 @@
*
* Range [0, 15] for each CQI.
*/
- private List<Integer> mCsiCqiReport;;
+ private List<Integer> mCsiCqiReport;
private int mSsRsrp;
private int mSsRsrq;
private int mSsSinr;
@@ -172,13 +172,13 @@
* @hide
*/
public CellSignalStrengthNr(int csiRsrp, int csiRsrq, int csiSinr, int csiCqiTableIndex,
- List<Integer> csiCqiReport, int ssRsrp, int ssRsrq, int ssSinr) {
+ List<Byte> csiCqiReport, int ssRsrp, int ssRsrq, int ssSinr) {
mCsiRsrp = inRangeOrUnavailable(csiRsrp, -140, -44);
mCsiRsrq = inRangeOrUnavailable(csiRsrq, -20, -3);
mCsiSinr = inRangeOrUnavailable(csiSinr, -23, 23);
mCsiCqiTableIndex = inRangeOrUnavailable(csiCqiTableIndex, 1, 3);
- mCsiCqiReport = csiCqiReport.stream()
- .map(cqi -> new Integer(inRangeOrUnavailable(cqi.intValue(), 1, 3)))
+ mCsiCqiReport = csiCqiReport.stream()
+ .map(cqi -> new Integer(inRangeOrUnavailable(Byte.toUnsignedInt(cqi), 1, 3)))
.collect(Collectors.toList());
mSsRsrp = inRangeOrUnavailable(ssRsrp, -140, -44);
mSsRsrq = inRangeOrUnavailable(ssRsrq, -43, 20);
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index c8ed82c..4d5b6ac 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -1055,6 +1055,20 @@
*/
public static final int HANDOVER_FAILED = 0x10006;
+ /**
+ * Enterprise setup failure: duplicate CID in DataCallResponse.
+ *
+ * @hide
+ */
+ public static final int DUPLICATE_CID = 0x10007;
+
+ /**
+ * Enterprise setup failure: no default data connection set up yet.
+ *
+ * @hide
+ */
+ public static final int NO_DEFAULT_DATA = 0x10008;
+
private static final Map<Integer, String> sFailCauseMap;
static {
sFailCauseMap = new HashMap<>();
@@ -1426,6 +1440,8 @@
sFailCauseMap.put(IPV6_PREFIX_UNAVAILABLE, "IPV6_PREFIX_UNAVAILABLE");
sFailCauseMap.put(HANDOFF_PREFERENCE_CHANGED, "HANDOFF_PREFERENCE_CHANGED");
sFailCauseMap.put(SLICE_REJECTED, "SLICE_REJECTED");
+ sFailCauseMap.put(MATCH_ALL_RULE_NOT_ALLOWED, "MATCH_ALL_RULE_NOT_ALLOWED");
+ sFailCauseMap.put(ALL_MATCHING_RULES_FAILED, "ALL_MATCHING_RULES_FAILED");
sFailCauseMap.put(IWLAN_PDN_CONNECTION_REJECTION, "IWLAN_PDN_CONNECTION_REJECTION");
sFailCauseMap.put(IWLAN_MAX_CONNECTION_REACHED, "IWLAN_MAX_CONNECTION_REACHED");
sFailCauseMap.put(IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION,
@@ -1481,6 +1497,9 @@
sFailCauseMap.put(UNACCEPTABLE_NETWORK_PARAMETER,
"UNACCEPTABLE_NETWORK_PARAMETER");
sFailCauseMap.put(LOST_CONNECTION, "LOST_CONNECTION");
+ sFailCauseMap.put(HANDOVER_FAILED, "HANDOVER_FAILED");
+ sFailCauseMap.put(DUPLICATE_CID, "DUPLICATE_CID");
+ sFailCauseMap.put(NO_DEFAULT_DATA, "NO_DEFAULT_DATA");
}
private DataFailCause() {
@@ -1580,6 +1599,9 @@
add(RADIO_NOT_AVAILABLE);
add(UNACCEPTABLE_NETWORK_PARAMETER);
add(SIGNAL_LOST);
+ add(DUPLICATE_CID);
+ add(MATCH_ALL_RULE_NOT_ALLOWED);
+ add(ALL_MATCHING_RULES_FAILED);
}
};
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d8ac082..2616ec8 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2785,7 +2785,8 @@
* @param subId the subscriber this override applies to.
* @param overrideUnmetered set if the billing relationship should be
* considered unmetered.
- * @param networkTypes the network types this override applies to.
+ * @param networkTypes the network types this override applies to. If no
+ * network types are specified, override values will be ignored.
* {@see TelephonyManager#getAllNetworkTypes()}
* @param timeoutMillis the timeout after which the requested override will
* be automatically cleared, or {@code 0} to leave in the
@@ -2849,7 +2850,8 @@
* @param subId the subscriber this override applies to.
* @param overrideCongested set if the subscription should be considered
* congested.
- * @param networkTypes the network types this override applies to.
+ * @param networkTypes the network types this override applies to. If no
+ * network types are specified, override values will be ignored.
* {@see TelephonyManager#getAllNetworkTypes()}
* @param timeoutMillis the timeout after which the requested override will
* be automatically cleared, or {@code 0} to leave in the
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f0771be..8475cab 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -64,7 +64,6 @@
import android.os.ParcelUuid;
import android.os.Parcelable;
import android.os.PersistableBundle;
-import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemProperties;
@@ -90,7 +89,7 @@
import android.telephony.VisualVoicemailService.VisualVoicemailTask;
import android.telephony.data.ApnSetting;
import android.telephony.data.ApnSetting.MvnoType;
-import android.telephony.data.SlicingConfig;
+import android.telephony.data.NetworkSlicingConfig;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
import android.telephony.gba.UaSecurityProtocolIdentifier;
@@ -428,10 +427,6 @@
return null;
}
- private boolean isSystemProcess() {
- return Process.myUid() == Process.SYSTEM_UID;
- }
-
/**
* Post a runnable to the BackgroundThread.
*
@@ -4205,19 +4200,12 @@
try {
IPhoneSubInfo info = getSubscriberInfoService();
if (info == null) {
- Rlog.e(TAG, "IMSI error: Subscriber Info is null");
- if (!isSystemProcess()) {
- throw new RuntimeException("IMSI error: Subscriber Info is null");
- }
- return;
+ throw new RuntimeException("IMSI error: Subscriber Info is null");
}
int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
info.resetCarrierKeysForImsiEncryption(subId, mContext.getOpPackageName());
} catch (RemoteException ex) {
- Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex);
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ Rlog.e(TAG, "Telephony#getCarrierInfoForImsiEncryption RemoteException" + ex);
}
}
@@ -4885,8 +4873,9 @@
* Return the set of IMSIs that should be considered "merged together" for data usage
* purposes. This API merges IMSIs based on subscription grouping: IMSI of those in the same
* group will all be returned.
- * Return the current IMSI if there is no subscription group. See
- * {@link SubscriptionManager#createSubscriptionGroup(List)} for the definition of a group.
+ * Return the current IMSI if there is no subscription group, see
+ * {@link SubscriptionManager#createSubscriptionGroup(List)} for the definition of a group,
+ * otherwise return an empty array if there is a failure.
*
* @hide
*/
@@ -5611,17 +5600,11 @@
try {
final ITelephony telephony = getITelephony();
if (telephony == null) {
- if (!isSystemProcess()) {
- throw new RuntimeException("Telephony service unavailable");
- }
return;
}
telephony.sendDialerSpecialCode(mContext.getOpPackageName(), inputCode);
} catch (RemoteException ex) {
- // This could happen if binder process crashes.
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ Rlog.e(TAG, "Telephony#sendDialerSpecialCode RemoteException" + ex);
}
}
@@ -9940,9 +9923,7 @@
throw new IllegalStateException("telephony service is null.");
}
} catch (RemoteException ex) {
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ Rlog.e(TAG, "Telephony#getMobileProvisioningUrl RemoteException" + ex);
}
return null;
}
@@ -10094,14 +10075,15 @@
/**
* Sets the roaming mode for CDMA phone to the given mode {@code mode}. If the phone is not
- * CDMA capable, this method does nothing.
+ * CDMA capable, this method throws an IllegalStateException.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
* @param mode CDMA roaming mode.
* @throws SecurityException if the caller does not have the permission.
- * @throws IllegalStateException if the Telephony process or radio is not currently available.
+ * @throws IllegalStateException if the Telephony process or radio is not currently available,
+ * the device is not CDMA capable, or the request fails.
*
* @see #CDMA_ROAMING_MODE_RADIO_DEFAULT
* @see #CDMA_ROAMING_MODE_HOME
@@ -10117,7 +10099,9 @@
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setCdmaRoamingMode(@CdmaRoamingMode int mode) {
- if (getPhoneType() != PHONE_TYPE_CDMA) return;
+ if (getPhoneType() != PHONE_TYPE_CDMA) {
+ throw new IllegalStateException("Phone does not support CDMA.");
+ }
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -10199,11 +10183,12 @@
/**
* Sets the subscription mode for CDMA phone to the given mode {@code mode}. If the phone is not
- * CDMA capable, this method does nothing.
+ * CDMA capable, this method throws an IllegalStateException.
*
* @param mode CDMA subscription mode.
* @throws SecurityException if the caller does not have the permission.
- * @throws IllegalStateException if the Telephony process is not currently available.
+ * @throws IllegalStateException if the Telephony process or radio is not currently available,
+ * the device is not CDMA capable, or the request fails.
*
* @see #CDMA_SUBSCRIPTION_UNKNOWN
* @see #CDMA_SUBSCRIPTION_RUIM_SIM
@@ -10218,7 +10203,9 @@
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setCdmaSubscriptionMode(@CdmaSubscription int mode) {
- if (getPhoneType() != PHONE_TYPE_CDMA) return;
+ if (getPhoneType() != PHONE_TYPE_CDMA) {
+ throw new IllegalStateException("Phone does not support CDMA.");
+ }
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -13926,9 +13913,7 @@
return service.isDataEnabledForApn(apnType, getSubId(), pkgForDebug);
}
} catch (RemoteException ex) {
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ Rlog.e(TAG, "Telephony#isDataEnabledForApn RemoteException" + ex);
}
return false;
}
@@ -13948,9 +13933,7 @@
return service.isApnMetered(apnType, getSubId());
}
} catch (RemoteException ex) {
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ Rlog.e(TAG, "Telephony#isApnMetered RemoteException" + ex);
}
return true;
}
@@ -14010,9 +13993,7 @@
service.setSystemSelectionChannels(specifiers, getSubId(), aidlConsumer);
}
} catch (RemoteException ex) {
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ Rlog.e(TAG, "Telephony#setSystemSelectionChannels RemoteException" + ex);
}
}
@@ -14040,9 +14021,7 @@
throw new IllegalStateException("telephony service is null.");
}
} catch (RemoteException ex) {
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ Rlog.e(TAG, "Telephony#getSystemSelectionChannels RemoteException" + ex);
}
return new ArrayList<>();
}
@@ -14071,9 +14050,7 @@
return service.isMvnoMatched(getSubId(), mvnoType, mvnoMatchData);
}
} catch (RemoteException ex) {
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ Rlog.e(TAG, "Telephony#matchesCurrentSimOperator RemoteException" + ex);
}
return false;
}
@@ -14474,10 +14451,7 @@
service.setMobileDataPolicyEnabled(getSubId(), policy, enabled);
}
} catch (RemoteException ex) {
- // This could happen if binder process crashes.
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ Rlog.e(TAG, "Telephony#setMobileDataPolicyEnabled RemoteException" + ex);
}
}
@@ -14497,10 +14471,7 @@
return service.isMobileDataPolicyEnabled(getSubId(), policy);
}
} catch (RemoteException ex) {
- // This could happen if binder process crashes.
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ Rlog.e(TAG, "Telephony#isMobileDataPolicyEnabled RemoteException" + ex);
}
return false;
}
@@ -15004,9 +14975,7 @@
throw new IllegalStateException("telephony service is null.");
}
} catch (RemoteException ex) {
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ Rlog.e(TAG, "Telephony#getEquivalentHomePlmns RemoteException" + ex);
}
return Collections.emptyList();
@@ -15064,13 +15033,20 @@
public static final String CAPABILITY_SLICING_CONFIG_SUPPORTED =
"CAPABILITY_SLICING_CONFIG_SUPPORTED";
- /** @hide */
+ /**
+ * A list of the radio interface capability values with public valid constants.
+ *
+ * Here is a related list for the systemapi-only valid constants:
+ * CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE
+ * CAPABILITY_ALLOWED_NETWORK_TYPES_USED
+ * CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE
+ * CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING
+ *
+ * @hide
+ * TODO(b/185508047): Doc generation for mixed public/systemapi StringDefs formats badly.
+ */
@Retention(RetentionPolicy.SOURCE)
@StringDef(prefix = "CAPABILITY_", value = {
- CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE,
- CAPABILITY_ALLOWED_NETWORK_TYPES_USED,
- CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE,
- CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING,
CAPABILITY_SLICING_CONFIG_SUPPORTED,
})
public @interface RadioInterfaceCapability {}
@@ -15082,10 +15058,7 @@
*
* @param capability the name of the capability to check for
* @return the availability of the capability
- *
- * @hide
*/
- @SystemApi
public boolean isRadioInterfaceCapabilitySupported(
@NonNull @RadioInterfaceCapability String capability) {
try {
@@ -15098,9 +15071,7 @@
throw new IllegalStateException("telephony service is null.");
}
} catch (RemoteException ex) {
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
+ Rlog.e(TAG, "Telephony#isRadioInterfaceCapabilitySupported RemoteException" + ex);
}
return false;
}
@@ -15669,7 +15640,7 @@
* Exception that may be supplied to the callback in {@link #getNetworkSlicingConfiguration} if
* something goes awry.
*/
- public static class SlicingException extends Exception {
+ public static class NetworkSlicingException extends Exception {
/**
* Getting the current slicing configuration successfully. Used internally only.
* @hide
@@ -15678,11 +15649,13 @@
/**
* The system timed out waiting for a response from the Radio.
+ * @hide
*/
public static final int ERROR_TIMEOUT = 1;
/**
* The modem returned a failure.
+ * @hide
*/
public static final int ERROR_MODEM_ERROR = 2;
@@ -15692,20 +15665,44 @@
ERROR_MODEM_ERROR,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface SlicingError {}
+ public @interface NetworkSlicingError {}
private final int mErrorCode;
- public SlicingException(@SlicingError int errorCode) {
+ /** @hide */
+ public NetworkSlicingException(@NetworkSlicingError int errorCode) {
mErrorCode = errorCode;
}
- /**
- * Fetches the error code associated with this exception.
- * @return An error code.
- */
- public @SlicingError int getErrorCode() {
- return mErrorCode;
+ @Override
+ public String toString() {
+ switch (mErrorCode) {
+ case ERROR_TIMEOUT: return "ERROR_TIMEOUT";
+ case ERROR_MODEM_ERROR: return "ERROR_MODEM_ERROR";
+ default: return "UNDEFINED";
+ }
+ }
+ }
+
+ /**
+ * Exception that is supplied to the callback in {@link #getNetworkSlicingConfiguration} if the
+ * system timed out waiting for a response from the Radio.
+ */
+ public class TimeoutException extends NetworkSlicingException {
+ /** @hide */
+ public TimeoutException(int errorCode) {
+ super(errorCode);
+ }
+ }
+
+ /**
+ * Exception that is supplied to the callback in {@link #getNetworkSlicingConfiguration} if the
+ * modem returned a failure.
+ */
+ public class ModemErrorException extends NetworkSlicingException {
+ /** @hide */
+ public ModemErrorException(int errorCode) {
+ super(errorCode);
}
}
@@ -15736,7 +15733,7 @@
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void getNetworkSlicingConfiguration(
@NonNull @CallbackExecutor Executor executor,
- @NonNull OutcomeReceiver<SlicingConfig, SlicingException> callback) {
+ @NonNull OutcomeReceiver<NetworkSlicingConfig, NetworkSlicingException> callback) {
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
@@ -15748,12 +15745,17 @@
telephony.getSlicingConfig(new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle result) {
- if (resultCode != SlicingException.SUCCESS) {
+ if (resultCode == NetworkSlicingException.ERROR_TIMEOUT) {
executor.execute(() -> callback.onError(
- new SlicingException(resultCode)));
+ new TimeoutException(resultCode)));
+ return;
+ } else if (resultCode == NetworkSlicingException.ERROR_MODEM_ERROR) {
+ executor.execute(() -> callback.onError(
+ new ModemErrorException(resultCode)));
return;
}
- SlicingConfig slicingConfig =
+
+ NetworkSlicingConfig slicingConfig =
result.getParcelable(KEY_SLICING_CONFIG_HANDLE);
executor.execute(() -> callback.onResult(slicingConfig));
}
diff --git a/telephony/java/android/telephony/data/SlicingConfig.aidl b/telephony/java/android/telephony/data/NetworkSlicingConfig.aidl
similarity index 95%
rename from telephony/java/android/telephony/data/SlicingConfig.aidl
rename to telephony/java/android/telephony/data/NetworkSlicingConfig.aidl
index ad93d8c..cd4a8f1e4 100644
--- a/telephony/java/android/telephony/data/SlicingConfig.aidl
+++ b/telephony/java/android/telephony/data/NetworkSlicingConfig.aidl
@@ -16,4 +16,4 @@
package android.telephony.data;
-parcelable SlicingConfig;
+parcelable NetworkSlicingConfig;
diff --git a/telephony/java/android/telephony/data/SlicingConfig.java b/telephony/java/android/telephony/data/NetworkSlicingConfig.java
similarity index 83%
rename from telephony/java/android/telephony/data/SlicingConfig.java
rename to telephony/java/android/telephony/data/NetworkSlicingConfig.java
index 990e4d2..dec787f 100644
--- a/telephony/java/android/telephony/data/SlicingConfig.java
+++ b/telephony/java/android/telephony/data/NetworkSlicingConfig.java
@@ -28,22 +28,22 @@
/**
* Represents a slicing configuration
*/
-public final class SlicingConfig implements Parcelable {
+public final class NetworkSlicingConfig implements Parcelable {
private final List<UrspRule> mUrspRules;
private final List<NetworkSliceInfo> mSliceInfo;
- public SlicingConfig() {
+ public NetworkSlicingConfig() {
mUrspRules = new ArrayList<UrspRule>();
mSliceInfo = new ArrayList<NetworkSliceInfo>();
}
/** @hide */
- public SlicingConfig(android.hardware.radio.V1_6.SlicingConfig sc) {
+ public NetworkSlicingConfig(android.hardware.radio.V1_6.SlicingConfig sc) {
this(sc.urspRules, sc.sliceInfo);
}
/** @hide */
- public SlicingConfig(List<android.hardware.radio.V1_6.UrspRule> urspRules,
+ public NetworkSlicingConfig(List<android.hardware.radio.V1_6.UrspRule> urspRules,
List<android.hardware.radio.V1_6.SliceInfo> sliceInfo) {
mUrspRules = new ArrayList<UrspRule>();
for (android.hardware.radio.V1_6.UrspRule ur : urspRules) {
@@ -69,7 +69,7 @@
}
/** @hide */
- public SlicingConfig(Parcel p) {
+ public NetworkSlicingConfig(Parcel p) {
mUrspRules = p.createTypedArrayList(UrspRule.CREATOR);
mSliceInfo = p.createTypedArrayList(NetworkSliceInfo.CREATOR);
}
@@ -96,16 +96,16 @@
dest.writeTypedList(mSliceInfo, flags);
}
- public static final @NonNull Parcelable.Creator<SlicingConfig> CREATOR =
- new Parcelable.Creator<SlicingConfig>() {
+ public static final @NonNull Parcelable.Creator<NetworkSlicingConfig> CREATOR =
+ new Parcelable.Creator<NetworkSlicingConfig>() {
@Override
- public SlicingConfig createFromParcel(Parcel source) {
- return new SlicingConfig(source);
+ public NetworkSlicingConfig createFromParcel(Parcel source) {
+ return new NetworkSlicingConfig(source);
}
@Override
- public SlicingConfig[] newArray(int size) {
- return new SlicingConfig[size];
+ public NetworkSlicingConfig[] newArray(int size) {
+ return new NetworkSlicingConfig[size];
}
};
@@ -118,7 +118,7 @@
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- SlicingConfig that = (SlicingConfig) o;
+ NetworkSlicingConfig that = (NetworkSlicingConfig) o;
return mUrspRules.size() == that.mUrspRules.size()
&& mUrspRules.containsAll(that.mUrspRules)
&& mSliceInfo.size() == that.mSliceInfo.size()
diff --git a/telephony/java/android/telephony/data/QosBearerFilter.java b/telephony/java/android/telephony/data/QosBearerFilter.java
index 6c1c653..5642549 100644
--- a/telephony/java/android/telephony/data/QosBearerFilter.java
+++ b/telephony/java/android/telephony/data/QosBearerFilter.java
@@ -31,7 +31,6 @@
import java.util.List;
import java.util.Objects;
-
/**
* Class that stores QOS filter parameters as defined in
* 3gpp 24.008 10.5.6.12 and 3gpp 24.501 9.11.4.13.
diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.java b/telephony/java/android/telephony/data/TrafficDescriptor.java
index d813bc5..f400a5e 100644
--- a/telephony/java/android/telephony/data/TrafficDescriptor.java
+++ b/telephony/java/android/telephony/data/TrafficDescriptor.java
@@ -21,6 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -31,11 +32,11 @@
*/
public final class TrafficDescriptor implements Parcelable {
private final String mDnn;
- private final String mOsAppId;
+ private final byte[] mOsAppId;
private TrafficDescriptor(@NonNull Parcel in) {
mDnn = in.readString();
- mOsAppId = in.readString();
+ mOsAppId = in.createByteArray();
}
/**
@@ -45,14 +46,15 @@
*
* @hide
*/
- public TrafficDescriptor(String dnn, String osAppId) {
+ public TrafficDescriptor(String dnn, byte[] osAppId) {
mDnn = dnn;
mOsAppId = osAppId;
}
/**
* DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003.
- * @return the DNN of this traffic descriptor.
+ * @return the DNN of this traffic descriptor if one is included by the network, null
+ * otherwise.
*/
public @Nullable String getDataNetworkName() {
return mDnn;
@@ -60,10 +62,11 @@
/**
* OsAppId is the app id as defined in 3GPP TS 24.526 Section 5.2, and it identifies a traffic
- * category.
- * @return the OS App ID of this traffic descriptor.
+ * category. It includes the OS Id component of the field as defined in the specs.
+ * @return the OS App ID of this traffic descriptor if one is included by the network, null
+ * otherwise.
*/
- public @Nullable String getOsAppId() {
+ public @Nullable byte[] getOsAppId() {
return mOsAppId;
}
@@ -80,7 +83,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mDnn);
- dest.writeString(mOsAppId);
+ dest.writeByteArray(mOsAppId);
}
public static final @NonNull Parcelable.Creator<TrafficDescriptor> CREATOR =
@@ -101,7 +104,7 @@
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TrafficDescriptor that = (TrafficDescriptor) o;
- return Objects.equals(mDnn, that.mDnn) && Objects.equals(mOsAppId, that.mOsAppId);
+ return Objects.equals(mDnn, that.mDnn) && Arrays.equals(mOsAppId, that.mOsAppId);
}
@Override
@@ -121,10 +124,12 @@
* .setDnn("")
* .build();
* </code></pre>
+ *
+ * @hide
*/
public static final class Builder {
private String mDnn = null;
- private String mOsAppId = null;
+ private byte[] mOsAppId = null;
/**
* Default constructor for Builder.
@@ -144,12 +149,12 @@
}
/**
- * Set the OS App ID.
+ * Set the OS App ID (including OS Id as defind in the specs).
*
* @return The same instance of the builder.
*/
@NonNull
- public Builder setOsAppId(@NonNull String osAppId) {
+ public Builder setOsAppId(@NonNull byte[] osAppId) {
this.mOsAppId = osAppId;
return this;
}
diff --git a/telephony/java/android/telephony/data/UrspRule.java b/telephony/java/android/telephony/data/UrspRule.java
index e2c47fd..fbe1999 100644
--- a/telephony/java/android/telephony/data/UrspRule.java
+++ b/telephony/java/android/telephony/data/UrspRule.java
@@ -84,8 +84,8 @@
android.hardware.radio.V1_6.TrafficDescriptor td) {
String dnn = td.dnn.getDiscriminator() == OptionalDnn.hidl_discriminator.noinit
? null : td.dnn.value();
- String osAppId = td.osAppId.getDiscriminator() == OptionalOsAppId.hidl_discriminator.noinit
- ? null : new String(arrayListToPrimitiveArray(td.osAppId.value().osAppId));
+ byte[] osAppId = td.osAppId.getDiscriminator() == OptionalOsAppId.hidl_discriminator.noinit
+ ? null : arrayListToPrimitiveArray(td.osAppId.value().osAppId);
TrafficDescriptor.Builder builder = new TrafficDescriptor.Builder();
if (dnn != null) {
builder.setDataNetworkName(dnn);
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
index 8762b6a..0d63f7b 100644
--- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -501,6 +501,10 @@
* {@link SipMessage} was using the latest configuration during creation and not a stale
* configuration due to race conditions between the configuration being updated and the RCS
* application not receiving the updated configuration before generating a new message.
+ * <p>
+ * The version number should be a positive number that starts at 0 and increments sequentially
+ * as new {@link SipDelegateImsConfiguration} instances are created to update the IMS
+ * configuration state.
*
* @return the version number associated with this {@link SipDelegateImsConfiguration}.
*/
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
index ec85995..2d230a7 100644
--- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
@@ -23,6 +23,7 @@
import android.app.blob.LeaseInfo;
import android.content.Context;
import android.content.res.Resources;
+import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -56,6 +57,16 @@
}
}
+ public static void writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input)
+ throws IOException {
+ try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(input)) {
+ try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
+ session.openWrite(0, -1))) {
+ FileUtils.copy(in, out);
+ }
+ }
+ }
+
public static void writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input,
long lengthBytes) throws IOException {
try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(input)) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 0593615..a540dff 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -23,7 +23,7 @@
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_LAYER_NAME
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_WINDOW_NAME
-val LAUNCHER_TITLE = arrayOf("Wallpaper", "Launcher", "com.google.android.googlequicksearchbox")
+val HOME_WINDOW_TITLE = arrayOf("Wallpaper", "Launcher")
fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() {
assertWm {
@@ -41,23 +41,23 @@
assertWm {
this.showsAppWindowOnTop(testApp.getPackage())
.then()
- .showsAppWindowOnTop(*LAUNCHER_TITLE)
+ .showsAppWindowOnTop(*HOME_WINDOW_TITLE)
}
}
fun FlickerTestParameter.launcherWindowBecomesVisible() {
assertWm {
- this.hidesBelowAppWindow(*LAUNCHER_TITLE)
+ this.hidesBelowAppWindow(*HOME_WINDOW_TITLE)
.then()
- .showsBelowAppWindow(*LAUNCHER_TITLE)
+ .showsBelowAppWindow(*HOME_WINDOW_TITLE)
}
}
fun FlickerTestParameter.launcherWindowBecomesInvisible() {
assertWm {
- this.showsBelowAppWindow(*LAUNCHER_TITLE)
+ this.showsBelowAppWindow(*HOME_WINDOW_TITLE)
.then()
- .hidesBelowAppWindow(*LAUNCHER_TITLE)
+ .hidesBelowAppWindow(*HOME_WINDOW_TITLE)
}
}
@@ -179,7 +179,7 @@
fun FlickerTestParameter.appLayerReplacesLauncher(appName: String) {
assertLayers {
- this.isVisible(*LAUNCHER_TITLE)
+ this.isVisible(*HOME_WINDOW_TITLE)
.then()
.isVisible(appName)
}
@@ -190,7 +190,7 @@
this.isVisible(testApp.getPackage())
.then()
.isInvisible(testApp.getPackage())
- .isVisible(*LAUNCHER_TITLE)
+ .isVisible(*HOME_WINDOW_TITLE)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index c92d40c..db91eb2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -16,12 +16,14 @@
package com.android.server.wm.flicker.close
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -44,6 +46,30 @@
}
}
+ @FlakyTest(bugId = 185401242)
+ @Test
+ override fun launcherLayerReplacesApp() {
+ super.launcherLayerReplacesApp()
+ }
+
+ @FlakyTest(bugId = 185401242)
+ @Test
+ override fun launcherReplacesAppWindowAsTopWindow() {
+ super.launcherReplacesAppWindowAsTopWindow()
+ }
+
+ @FlakyTest(bugId = 185401242)
+ @Test
+ override fun launcherWindowBecomesVisible() {
+ super.launcherWindowBecomesVisible()
+ }
+
+ @FlakyTest(bugId = 185401242)
+ @Test
+ override fun noUncoveredRegions() {
+ super.noUncoveredRegions()
+ }
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 0bae8f6..3bd19ea 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -51,6 +51,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 185400889)
class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
index 6a7309c..01e34d9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
@@ -18,11 +18,11 @@
import android.platform.helpers.IAppHelper
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.LAUNCHER_TITLE
+import com.android.server.wm.flicker.HOME_WINDOW_TITLE
fun FlickerTestParameter.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper) {
assertWm {
- this.showsAppWindowOnTop(*LAUNCHER_TITLE)
+ this.showsAppWindowOnTop(*HOME_WINDOW_TITLE)
.then()
.showsAppWindowOnTop("Snapshot", testApp.getPackage())
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 1c97be3..ad7ee30 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -98,6 +98,12 @@
super.focusChanges()
}
+ @FlakyTest(bugId = 185400889)
+ @Test
+ override fun noUncoveredRegions() {
+ super.noUncoveredRegions()
+ }
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 742a2d1..a353c59 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.rotation
-import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -69,13 +68,13 @@
super.statusBarLayerIsAlwaysVisible()
}
- @FlakyTest
+ @FlakyTest(bugId = 185400889)
@Test
override fun noUncoveredRegions() {
super.noUncoveredRegions()
}
- @Presubmit
+ @FlakyTest(bugId = 185400889)
@Test
fun appLayerAlwaysVisible() {
testSpec.assertLayers {
@@ -83,7 +82,7 @@
}
}
- @Presubmit
+ @FlakyTest(bugId = 185400889)
@Test
fun appLayerRotates() {
testSpec.assertLayers {
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
index f6d9a73..487c856 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
@@ -56,6 +56,7 @@
CanvasProperty<Float> mY;
CanvasProperty<Float> mRadius;
CanvasProperty<Float> mProgress;
+ CanvasProperty<Float> mNoisePhase;
CanvasProperty<Paint> mPaint;
RuntimeShader mRuntimeShader;
@@ -99,6 +100,7 @@
mY = CanvasProperty.createFloat(200.0f);
mRadius = CanvasProperty.createFloat(150.0f);
mProgress = CanvasProperty.createFloat(0.0f);
+ mNoisePhase = CanvasProperty.createFloat(0.0f);
Paint p = new Paint();
p.setAntiAlias(true);
@@ -115,7 +117,8 @@
if (canvas.isHardwareAccelerated()) {
RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
- recordingCanvas.drawRipple(mX, mY, mRadius, mPaint, mProgress, mRuntimeShader);
+ recordingCanvas.drawRipple(mX, mY, mRadius, mPaint, mProgress, mNoisePhase,
+ mRuntimeShader);
}
}
@@ -141,6 +144,9 @@
mProgress, mToggle ? 1.0f : 0.0f));
mRunningAnimations.add(new RenderNodeAnimator(
+ mNoisePhase, DURATION));
+
+ mRunningAnimations.add(new RenderNodeAnimator(
mPaint, RenderNodeAnimator.PAINT_ALPHA, 64.0f));
// Will be "chained" to run after the above
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 0bb0337..642b19e 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -527,6 +527,33 @@
}
@Test
+ public void testExpireSession_Phase1_Install() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ Install.single(TestApp.A1).commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ Install.single(TestApp.A2).setEnableRollback().setStaged().commit();
+ }
+
+ @Test
+ public void testExpireSession_Phase2_VerifyInstall() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+ rm.getAvailableRollbacks(), TestApp.A);
+ assertThat(rollback).isNotNull();
+ assertThat(rollback).packagesContainsExactly(Rollback.from(TestApp.A2).to(TestApp.A1));
+ assertThat(rollback.isStaged()).isTrue();
+ }
+
+ @Test
+ public void testExpireSession_Phase3_VerifyRollback() throws Exception {
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ RollbackInfo rollback = getUniqueRollbackInfoForPackage(
+ rm.getAvailableRollbacks(), TestApp.A);
+ assertThat(rollback).isNotNull();
+ }
+
+ @Test
public void hasMainlineModule() throws Exception {
String pkgName = getModuleMetadataPackageName();
boolean existed = InstrumentationRegistry.getInstrumentation().getContext()
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 304567a3..1aa5c24 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -40,7 +40,9 @@
import org.junit.runner.RunWith;
import java.io.File;
+import java.time.Instant;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -582,6 +584,27 @@
runPhase("testWatchdogMonitorsAcrossReboots_Phase3_VerifyRollback");
}
+ /**
+ * Tests an available rollback shouldn't be deleted when its session expires.
+ */
+ @Test
+ public void testExpireSession() throws Exception {
+ runPhase("testExpireSession_Phase1_Install");
+ getDevice().reboot();
+ runPhase("testExpireSession_Phase2_VerifyInstall");
+
+ // Advance system clock by 7 days to expire the staged session
+ Instant t1 = Instant.ofEpochMilli(getDevice().getDeviceDate());
+ Instant t2 = t1.plusMillis(TimeUnit.DAYS.toMillis(7));
+ runAsRoot(() -> getDevice().setDate(Date.from(t2)));
+
+ // Somehow we need to wait for a while before reboot. Otherwise the change to the
+ // system clock will be reset after reboot.
+ Thread.sleep(3000);
+ getDevice().reboot();
+ runPhase("testExpireSession_Phase3_VerifyRollback");
+ }
+
private void pushTestApex() throws Exception {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
diff --git a/tests/net/common/java/ParseExceptionTest.kt b/tests/net/common/java/ParseExceptionTest.kt
new file mode 100644
index 0000000..f17715a
--- /dev/null
+++ b/tests/net/common/java/ParseExceptionTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.net.ParseException
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertNull
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ParseExceptionTest {
+ @Test
+ fun testConstructor_WithCause() {
+ val testMessage = "Test message"
+ val base = Exception("Test")
+ val exception = ParseException(testMessage, base)
+
+ assertEquals(testMessage, exception.response)
+ assertEquals(base, exception.cause)
+ }
+
+ @Test
+ fun testConstructor_NoCause() {
+ val testMessage = "Test message"
+ val exception = ParseException(testMessage)
+
+ assertEquals(testMessage, exception.response)
+ assertNull(exception.cause)
+ }
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
index 454d5b5..2b45b3d 100644
--- a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
+++ b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
@@ -44,6 +44,9 @@
setSubscriberId("MySubId")
setPartialConnectivityAcceptable(false)
setUnvalidatedConnectivityAcceptable(true)
+ if (isAtLeastS()) {
+ setBypassableVpn(true)
+ }
}.build()
if (isAtLeastS()) {
// From S, the config will have 12 items
@@ -66,6 +69,7 @@
if (isAtLeastS()) {
setNat64DetectionEnabled(false)
setProvisioningNotificationEnabled(false)
+ setBypassableVpn(true)
}
}.build()
@@ -78,6 +82,7 @@
if (isAtLeastS()) {
assertFalse(config.isNat64DetectionEnabled())
assertFalse(config.isProvisioningNotificationEnabled())
+ assertTrue(config.isBypassableVpn())
} else {
assertTrue(config.isNat64DetectionEnabled())
assertTrue(config.isProvisioningNotificationEnabled())
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 6d852bf..9b74583 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -312,7 +312,7 @@
.addCapability(NET_CAPABILITY_EIMS)
.addCapability(NET_CAPABILITY_NOT_METERED);
if (isAtLeastS()) {
- netCap.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+ netCap.setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2));
netCap.setUids(uids);
}
if (isAtLeastR()) {
@@ -642,16 +642,16 @@
assertTrue(nc2.appliesToUid(22));
// Verify the subscription id list can be combined only when they are equal.
- nc1.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
- nc2.setSubIds(Set.of(TEST_SUBID2));
+ nc1.setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+ nc2.setSubscriptionIds(Set.of(TEST_SUBID2));
assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
- nc2.setSubIds(Set.of());
+ nc2.setSubscriptionIds(Set.of());
assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
- nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+ nc2.setSubscriptionIds(Set.of(TEST_SUBID2, TEST_SUBID1));
nc2.combineCapabilities(nc1);
- assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubIds());
+ assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubscriptionIds());
}
}
@@ -806,20 +806,20 @@
assertEquals(nc1, nc2);
if (isAtLeastS()) {
- assertThrows(NullPointerException.class, () -> nc1.setSubIds(null));
- nc1.setSubIds(Set.of());
+ assertThrows(NullPointerException.class, () -> nc1.setSubscriptionIds(null));
+ nc1.setSubscriptionIds(Set.of());
nc2.set(nc1);
assertEquals(nc1, nc2);
- nc1.setSubIds(Set.of(TEST_SUBID1));
+ nc1.setSubscriptionIds(Set.of(TEST_SUBID1));
nc2.set(nc1);
assertEquals(nc1, nc2);
- nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+ nc2.setSubscriptionIds(Set.of(TEST_SUBID2, TEST_SUBID1));
nc2.set(nc1);
assertEquals(nc1, nc2);
- nc2.setSubIds(Set.of(TEST_SUBID3, TEST_SUBID2));
+ nc2.setSubscriptionIds(Set.of(TEST_SUBID3, TEST_SUBID2));
assertNotEquals(nc1, nc2);
}
}
@@ -908,8 +908,8 @@
// satisfy these requests.
final NetworkCapabilities nc = new NetworkCapabilities.Builder()
.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
- .setSubIds(new ArraySet<>(subIds)).build();
- assertEquals(new ArraySet<>(subIds), nc.getSubIds());
+ .setSubscriptionIds(new ArraySet<>(subIds)).build();
+ assertEquals(new ArraySet<>(subIds), nc.getSubscriptionIds());
return nc;
}
@@ -921,11 +921,11 @@
final NetworkCapabilities ncWithoutRequestedIds = capsWithSubIds(TEST_SUBID3);
final NetworkRequest requestWithoutId = new NetworkRequest.Builder().build();
- assertEmpty(requestWithoutId.networkCapabilities.getSubIds());
+ assertEmpty(requestWithoutId.networkCapabilities.getSubscriptionIds());
final NetworkRequest requestWithIds = new NetworkRequest.Builder()
- .setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2)).build();
+ .setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2)).build();
assertEquals(Set.of(TEST_SUBID1, TEST_SUBID2),
- requestWithIds.networkCapabilities.getSubIds());
+ requestWithIds.networkCapabilities.getSubscriptionIds());
assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutId));
assertTrue(requestWithIds.canBeSatisfiedBy(ncWithOtherIds));
@@ -1138,8 +1138,8 @@
if (isAtLeastS()) {
final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
- .setSubIds(Set.of(TEST_SUBID1)).build();
- assertEquals(Set.of(TEST_SUBID1), nc2.getSubIds());
+ .setSubscriptionIds(Set.of(TEST_SUBID1)).build();
+ assertEquals(Set.of(TEST_SUBID1), nc2.getSubscriptionIds());
}
}
}
diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp
index 730ef3b..39c424e 100644
--- a/tests/net/integration/Android.bp
+++ b/tests/net/integration/Android.bp
@@ -62,6 +62,7 @@
// Utilities for testing framework code both in integration and unit tests.
java_library {
name: "frameworks-net-integration-testutils",
+ defaults: ["framework-connectivity-test-defaults"],
srcs: ["util/**/*.java", "util/**/*.kt"],
static_libs: [
"androidx.annotation_annotation",
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 6cbdd25..19f8843 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -384,7 +384,7 @@
eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
eq(testPkgName), eq(testAttributionTag));
- manager.requestBackgroundNetwork(request, handler, callback);
+ manager.requestBackgroundNetwork(request, callback, handler);
verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities),
eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
eq(testPkgName), eq(testAttributionTag));
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 47e4b5e..7ebed39 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1791,7 +1791,7 @@
assertNull(mCm.getActiveNetworkForUid(Process.myUid()));
// Test getAllNetworks()
assertEmpty(mCm.getAllNetworks());
- assertEmpty(mCm.getAllNetworkStateSnapshot());
+ assertEmpty(mCm.getAllNetworkStateSnapshots());
}
/**
@@ -2668,25 +2668,6 @@
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- // Bring up wifi with a score of 70.
- // Cell is lingered because it would not satisfy any request, even if it validated.
- mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- mWiFiNetworkAgent.adjustScore(50);
- mWiFiNetworkAgent.connect(false); // Score: 70
- callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
- defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
-
- // Tear down wifi.
- mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
- defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
- assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
-
// Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
// it's arguably correct to linger it, since it was the default network before it validated.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -2991,11 +2972,17 @@
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- // BUG: the network will no longer linger, even though it's validated and outscored.
- // TODO: fix this.
mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
mEthernetNetworkAgent.connect(true);
+ // BUG: with the legacy int-based scoring code, the network will no longer linger, even
+ // though it's validated and outscored.
+ // The new policy-based scoring code fixes this.
+ // TODO: remove the line below and replace with the three commented lines when
+ // the policy-based scoring code is turned on.
callback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent);
+ // callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
+ // callback.expectCallback(CallbackEntry.LOSING, mWiFiNetworkAgent);
+ // callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
callback.assertNoCallback();
@@ -4313,7 +4300,7 @@
final TestNetworkCallback cellBgCallback = new TestNetworkCallback();
mCm.requestBackgroundNetwork(new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR).build(),
- mCsHandlerThread.getThreadHandler(), cellBgCallback);
+ cellBgCallback, mCsHandlerThread.getThreadHandler());
// Make callbacks for monitoring.
final NetworkRequest request = new NetworkRequest.Builder().build();
@@ -9768,7 +9755,8 @@
return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(),
nc, new NetworkScore.Builder().setLegacyInt(0).build(),
mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0,
- INVALID_UID, mQosCallbackTracker, new ConnectivityService.Dependencies());
+ INVALID_UID, TEST_LINGER_DELAY_MS, mQosCallbackTracker,
+ new ConnectivityService.Dependencies());
}
@Test
@@ -12028,7 +12016,7 @@
}
@Test
- public void testGetAllNetworkStateSnapshot() throws Exception {
+ public void testGetAllNetworkStateSnapshots() throws Exception {
verifyNoNetwork();
// Setup test cellular network with specified LinkProperties and NetworkCapabilities,
@@ -12052,7 +12040,7 @@
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp, cellNcTemplate);
mCellNetworkAgent.connect(true);
cellCb.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
- List<NetworkStateSnapshot> snapshots = mCm.getAllNetworkStateSnapshot();
+ List<NetworkStateSnapshot> snapshots = mCm.getAllNetworkStateSnapshots();
assertLength(1, snapshots);
// Compose the expected cellular snapshot for verification.
@@ -12074,7 +12062,7 @@
mWiFiNetworkAgent.getNetwork(), wifiNc, new LinkProperties(), null,
ConnectivityManager.TYPE_WIFI);
- snapshots = mCm.getAllNetworkStateSnapshot();
+ snapshots = mCm.getAllNetworkStateSnapshots();
assertLength(2, snapshots);
assertContainsAll(snapshots, cellSnapshot, wifiSnapshot);
@@ -12083,20 +12071,20 @@
// temporary shortage of connectivity of a connected network.
mCellNetworkAgent.suspend();
waitForIdle();
- snapshots = mCm.getAllNetworkStateSnapshot();
+ snapshots = mCm.getAllNetworkStateSnapshots();
assertLength(1, snapshots);
assertEquals(wifiSnapshot, snapshots.get(0));
// Disconnect wifi, verify the snapshots contain nothing.
mWiFiNetworkAgent.disconnect();
waitForIdle();
- snapshots = mCm.getAllNetworkStateSnapshot();
+ snapshots = mCm.getAllNetworkStateSnapshots();
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertLength(0, snapshots);
mCellNetworkAgent.resume();
waitForIdle();
- snapshots = mCm.getAllNetworkStateSnapshot();
+ snapshots = mCm.getAllNetworkStateSnapshots();
assertLength(1, snapshots);
assertEquals(cellSnapshot, snapshots.get(0));
@@ -12601,12 +12589,12 @@
public void testSubIdsClearedWithoutNetworkFactoryPermission() throws Exception {
mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED);
final NetworkCapabilities nc = new NetworkCapabilities();
- nc.setSubIds(Collections.singleton(Process.myUid()));
+ nc.setSubscriptionIds(Collections.singleton(Process.myUid()));
final NetworkCapabilities result =
mService.networkCapabilitiesRestrictedForCallerPermissions(
nc, Process.myPid(), Process.myUid());
- assertTrue(result.getSubIds().isEmpty());
+ assertTrue(result.getSubscriptionIds().isEmpty());
}
@Test
@@ -12615,17 +12603,17 @@
final Set<Integer> subIds = Collections.singleton(Process.myUid());
final NetworkCapabilities nc = new NetworkCapabilities();
- nc.setSubIds(subIds);
+ nc.setSubscriptionIds(subIds);
final NetworkCapabilities result =
mService.networkCapabilitiesRestrictedForCallerPermissions(
nc, Process.myPid(), Process.myUid());
- assertEquals(subIds, result.getSubIds());
+ assertEquals(subIds, result.getSubscriptionIds());
}
private NetworkRequest getRequestWithSubIds() {
return new NetworkRequest.Builder()
- .setSubIds(Collections.singleton(Process.myUid()))
+ .setSubscriptionIds(Collections.singleton(Process.myUid()))
.build();
}
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 116d755e..36e229d 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -71,6 +71,8 @@
static final int LOW_DAILY_LIMIT = 2;
static final int HIGH_DAILY_LIMIT = 1000;
+ private static final int TEST_LINGER_DELAY_MS = 400;
+
LingerMonitor mMonitor;
@Mock ConnectivityService mConnService;
@@ -366,7 +368,7 @@
NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(),
mCtx, null, new NetworkAgentConfig.Builder().build(), mConnService, mNetd,
- mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(),
+ mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), TEST_LINGER_DELAY_MS,
mQosCallbackTracker, new ConnectivityService.Dependencies());
nai.everValidated = true;
return nai;
diff --git a/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java b/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java
index 2333718..43b80e4 100644
--- a/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnControlPlaneIkeConfigTest.java
@@ -22,12 +22,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.Network;
import android.net.ipsec.ike.ChildSaProposal;
import android.net.ipsec.ike.IkeFqdnIdentification;
import android.net.ipsec.ike.IkeSaProposal;
@@ -56,20 +51,13 @@
.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC)
.build();
- Context mockContext = mock(Context.class);
- ConnectivityManager mockConnectManager = mock(ConnectivityManager.class);
- doReturn(mockConnectManager)
- .when(mockContext)
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- doReturn(mock(Network.class)).when(mockConnectManager).getActiveNetwork();
-
final String serverHostname = "192.0.2.100";
final String testLocalId = "test.client.com";
final String testRemoteId = "test.server.com";
final byte[] psk = "psk".getBytes();
IKE_PARAMS =
- new IkeSessionParams.Builder(mockContext)
+ new IkeSessionParams.Builder()
.setServerHostname(serverHostname)
.addSaProposal(ikeProposal)
.setLocalIdentification(new IkeFqdnIdentification(testLocalId))
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 9a66343..907cb46 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -696,7 +696,7 @@
.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
.addTransportType(transport);
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- ncBuilder.setSubIds(Collections.singleton(subId));
+ ncBuilder.setSubscriptionIds(Collections.singleton(subId));
}
return ncBuilder;
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
index b592000..8289e85 100644
--- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -153,21 +153,19 @@
verify(mConnectivityManager)
.requestBackgroundNetwork(
eq(getWifiRequest(expectedSubIds)),
- any(),
- any(NetworkBringupCallback.class));
+ any(NetworkBringupCallback.class),
+ any());
for (final int subId : expectedSubIds) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
eq(getCellRequestForSubId(subId)),
- any(),
- any(NetworkBringupCallback.class));
+ any(NetworkBringupCallback.class), any());
}
verify(mConnectivityManager)
.requestBackgroundNetwork(
eq(getRouteSelectionRequest(expectedSubIds)),
- any(),
- any(RouteSelectionCallback.class));
+ any(RouteSelectionCallback.class), any());
}
@Test
@@ -191,7 +189,7 @@
private NetworkRequest getWifiRequest(Set<Integer> netCapsSubIds) {
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .setSubIds(netCapsSubIds)
+ .setSubscriptionIds(netCapsSubIds)
.build();
}
@@ -203,7 +201,7 @@
}
private NetworkRequest getRouteSelectionRequest(Set<Integer> netCapsSubIds) {
- return getExpectedRequestBase().setSubIds(netCapsSubIds).build();
+ return getExpectedRequestBase().setSubscriptionIds(netCapsSubIds).build();
}
private NetworkRequest.Builder getExpectedRequestBase() {
@@ -267,8 +265,8 @@
verify(mConnectivityManager)
.requestBackgroundNetwork(
eq(getRouteSelectionRequest(INITIAL_SUB_IDS)),
- any(),
- mRouteSelectionCallbackCaptor.capture());
+ mRouteSelectionCallbackCaptor.capture(),
+ any());
RouteSelectionCallback cb = mRouteSelectionCallbackCaptor.getValue();
cb.onAvailable(mNetwork);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
index bfe8c73..acc8bf9 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -58,8 +58,7 @@
ArgumentCaptor.forClass(IkeSessionParams.class);
verify(mDeps).newIkeSession(any(), paramsCaptor.capture(), any(), any(), any());
assertEquals(
- TEST_UNDERLYING_NETWORK_RECORD_1.network,
- paramsCaptor.getValue().getConfiguredNetwork());
+ TEST_UNDERLYING_NETWORK_RECORD_1.network, paramsCaptor.getValue().getNetwork());
}
@Test
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index f0b7595..1d308df 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -4,6 +4,7 @@
import copy
import glob
from os import path
+import re
import sys
from xml.etree import ElementTree
@@ -199,8 +200,9 @@
class FontRecord(object):
- def __init__(self, name, scripts, variant, weight, style, fallback_for, font):
+ def __init__(self, name, psName, scripts, variant, weight, style, fallback_for, font):
self.name = name
+ self.psName = psName
self.scripts = scripts
self.variant = variant
self.weight = weight
@@ -236,6 +238,7 @@
assert variant in {None, 'elegant', 'compact'}, (
'Unexpected value for variant: %s' % variant)
+ trim_re = re.compile(r"^[ \n\r\t]*(.+)[ \n\r\t]*$")
for family in families:
name = family.get('name')
variant = family.get('variant')
@@ -251,6 +254,10 @@
assert child.tag == 'font', (
'Unknown tag <%s>' % child.tag)
font_file = child.text.rstrip()
+
+ m = trim_re.match(font_file)
+ font_file = m.group(1)
+
weight = int(child.get('weight'))
assert weight % 100 == 0, (
'Font weight "%d" is not a multiple of 100.' % weight)
@@ -270,11 +277,12 @@
if index:
index = int(index)
- if not path.exists(path.join(_fonts_dir, font_file)):
+ if not path.exists(path.join(_fonts_dir, m.group(1))):
continue # Missing font is a valid case. Just ignore the missing font files.
record = FontRecord(
name,
+ child.get('postScriptName'),
frozenset(scripts),
variant,
weight,
@@ -664,6 +672,37 @@
break
assert_font_supports_none_of_chars(record.font, cjk_punctuation, name)
+def getPostScriptName(font):
+ font_file, index = font
+ font_path = path.join(_fonts_dir, font_file)
+ if index is not None:
+ # Use the first font file in the collection for resolving post script name.
+ ttf = ttLib.TTFont(font_path, fontNumber=0)
+ else:
+ ttf = ttLib.TTFont(font_path)
+
+ nameTable = ttf['name']
+ for name in nameTable.names:
+ if (name.nameID == 6 and name.platformID == 3 and name.platEncID == 1
+ and name.langID == 0x0409):
+ return str(name)
+
+def check_canonical_name():
+ for record in _all_fonts:
+ file_name, index = record.font
+
+ psName = getPostScriptName(record.font)
+ if record.psName:
+ # If fonts element has postScriptName attribute, it should match with the PostScript
+ # name in the name table.
+ assert psName == record.psName, ('postScriptName attribute %s should match with %s' % (
+ record.psName, psName))
+ else:
+ # If fonts element doesn't have postScriptName attribute, the file name should match
+ # with the PostScript name in the name table.
+ assert psName == file_name[:-4], ('file name %s should match with %s' % (
+ file_name, psName))
+
def main():
global _fonts_dir
@@ -682,6 +721,8 @@
check_cjk_punctuation()
+ check_canonical_name()
+
check_emoji = sys.argv[2]
if check_emoji == 'true':
ucd_path = sys.argv[3]
diff --git a/tools/hiddenapi/checksorted_sha.sh b/tools/hiddenapi/checksorted_sha.sh
deleted file mode 100755
index 72fb867..0000000
--- a/tools/hiddenapi/checksorted_sha.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-set -e
-LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
-git show --name-only --pretty=format: $1 | grep "hiddenapi/hiddenapi-.*txt" | while read file; do
- diff <(git show $1:$file) <(git show $1:$file | $LOCAL_DIR/sort_api.sh ) || {
- echo -e "\e[1m\e[31m$file $1 is not sorted or contains duplicates. To sort it correctly:\e[0m"
- echo -e "\e[33m${LOCAL_DIR}/sort_api.sh $PWD/$file\e[0m"
- exit 1
- }
-done
diff --git a/tools/hiddenapi/sort_api.sh b/tools/hiddenapi/sort_api.sh
deleted file mode 100755
index 710da40..0000000
--- a/tools/hiddenapi/sort_api.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-set -e
-if [ -z "$1" ]; then
- source_list=/dev/stdin
- dest_list=/dev/stdout
-else
- source_list="$1"
- dest_list="$1"
-fi
-# Load the file
-readarray A < "$source_list"
-# Sort
-IFS=$'\n'
-# Stash away comments
-C=( $(grep -E '^#' <<< "${A[*]}" || :) )
-A=( $(grep -v -E '^#' <<< "${A[*]}" || :) )
-# Sort entries
-A=( $(LC_COLLATE=C sort -f <<< "${A[*]}") )
-A=( $(uniq <<< "${A[*]}") )
-# Concatenate comments and entries
-A=( ${C[*]} ${A[*]} )
-unset IFS
-# Dump array back into the file
-if [ ${#A[@]} -ne 0 ]; then
- printf '%s\n' "${A[@]}" > "$dest_list"
-fi
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index da0571ba..3b75660 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -117,13 +117,13 @@
/**
* Interface used to listen country code event
*/
- public interface CountryCodeChangeListener {
+ public interface CountryCodeChangedListener {
/**
* Called when country code changed.
*
- * @param countryCode A new country code which is 2-Character alphanumeric.
+ * @param countryCode An ISO-3166-alpha2 country code which is 2-Character alphanumeric.
*/
- void onChanged(@NonNull String countryCode);
+ void onCountryCodeChanged(@NonNull String countryCode);
}
/**
@@ -163,27 +163,27 @@
/** @hide */
@VisibleForTesting
public class WificondEventHandler extends IWificondEventCallback.Stub {
- private Map<CountryCodeChangeListener, Executor> mCountryCodeChangeListenerHolder =
+ private Map<CountryCodeChangedListener, Executor> mCountryCodeChangedListenerHolder =
new HashMap<>();
/**
- * Register CountryCodeChangeListener with pid.
+ * Register CountryCodeChangedListener with pid.
*
* @param executor The Executor on which to execute the callbacks.
* @param listener listener for country code changed events.
*/
- public void registerCountryCodeChangeListener(Executor executor,
- CountryCodeChangeListener listener) {
- mCountryCodeChangeListenerHolder.put(listener, executor);
+ public void registerCountryCodeChangedListener(Executor executor,
+ CountryCodeChangedListener listener) {
+ mCountryCodeChangedListenerHolder.put(listener, executor);
}
/**
- * Unregister CountryCodeChangeListener with pid.
+ * Unregister CountryCodeChangedListener with pid.
*
* @param listener listener which registered country code changed events.
*/
- public void unregisterCountryCodeChangeListener(CountryCodeChangeListener listener) {
- mCountryCodeChangeListenerHolder.remove(listener);
+ public void unregisterCountryCodeChangedListener(CountryCodeChangedListener listener) {
+ mCountryCodeChangedListenerHolder.remove(listener);
}
@Override
@@ -191,8 +191,8 @@
Log.d(TAG, "OnRegDomainChanged " + countryCode);
final long token = Binder.clearCallingIdentity();
try {
- mCountryCodeChangeListenerHolder.forEach((listener, executor) -> {
- executor.execute(() -> listener.onChanged(countryCode));
+ mCountryCodeChangedListenerHolder.forEach((listener, executor) -> {
+ executor.execute(() -> listener.onCountryCodeChanged(countryCode));
});
} finally {
Binder.restoreCallingIdentity(token);
@@ -1240,25 +1240,25 @@
* @param listener listener for country code changed events.
* @return true on success, false on failure.
*/
- public boolean registerCountryCodeChangeListener(@NonNull @CallbackExecutor Executor executor,
- @NonNull CountryCodeChangeListener listener) {
+ public boolean registerCountryCodeChangedListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull CountryCodeChangedListener listener) {
if (!retrieveWificondAndRegisterForDeath()) {
return false;
}
Log.d(TAG, "registerCountryCodeEventListener called");
- mWificondEventHandler.registerCountryCodeChangeListener(executor, listener);
+ mWificondEventHandler.registerCountryCodeChangedListener(executor, listener);
return true;
}
/**
- * Unregister CountryCodeChangeListener with pid.
+ * Unregister CountryCodeChangedListener with pid.
*
* @param listener listener which registered country code changed events.
*/
- public void unregisterCountryCodeChangeListener(@NonNull CountryCodeChangeListener listener) {
+ public void unregisterCountryCodeChangedListener(@NonNull CountryCodeChangedListener listener) {
Log.d(TAG, "unregisterCountryCodeEventListener called");
- mWificondEventHandler.unregisterCountryCodeChangeListener(listener);
+ mWificondEventHandler.unregisterCountryCodeChangedListener(listener);
}
/**
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
index 98a0042..3fb2301 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
@@ -98,9 +98,9 @@
@Mock
private WifiNl80211Manager.PnoScanRequestCallback mPnoScanRequestCallback;
@Mock
- private WifiNl80211Manager.CountryCodeChangeListener mCountryCodeChangeListener;
+ private WifiNl80211Manager.CountryCodeChangedListener mCountryCodeChangedListener;
@Mock
- private WifiNl80211Manager.CountryCodeChangeListener mCountryCodeChangeListener2;
+ private WifiNl80211Manager.CountryCodeChangedListener mCountryCodeChangedListener2;
@Mock
private Context mContext;
private TestLooper mLooper;
@@ -768,25 +768,25 @@
}
/**
- * Ensures callback works after register CountryCodeChangeListener.
+ * Ensures callback works after register CountryCodeChangedListener.
*/
@Test
- public void testCountryCodeChangeListenerInvocation() throws Exception {
- assertTrue(mWificondControl.registerCountryCodeChangeListener(
- Runnable::run, mCountryCodeChangeListener));
- assertTrue(mWificondControl.registerCountryCodeChangeListener(
- Runnable::run, mCountryCodeChangeListener2));
+ public void testCountryCodeChangedListenerInvocation() throws Exception {
+ assertTrue(mWificondControl.registerCountryCodeChangedListener(
+ Runnable::run, mCountryCodeChangedListener));
+ assertTrue(mWificondControl.registerCountryCodeChangedListener(
+ Runnable::run, mCountryCodeChangedListener2));
mWificondEventHandler.OnRegDomainChanged(TEST_COUNTRY_CODE);
- verify(mCountryCodeChangeListener).onChanged(TEST_COUNTRY_CODE);
- verify(mCountryCodeChangeListener2).onChanged(TEST_COUNTRY_CODE);
+ verify(mCountryCodeChangedListener).onCountryCodeChanged(TEST_COUNTRY_CODE);
+ verify(mCountryCodeChangedListener2).onCountryCodeChanged(TEST_COUNTRY_CODE);
- reset(mCountryCodeChangeListener);
- reset(mCountryCodeChangeListener2);
- mWificondControl.unregisterCountryCodeChangeListener(mCountryCodeChangeListener2);
+ reset(mCountryCodeChangedListener);
+ reset(mCountryCodeChangedListener2);
+ mWificondControl.unregisterCountryCodeChangedListener(mCountryCodeChangedListener2);
mWificondEventHandler.OnRegDomainChanged(TEST_COUNTRY_CODE);
- verify(mCountryCodeChangeListener).onChanged(TEST_COUNTRY_CODE);
- verify(mCountryCodeChangeListener2, never()).onChanged(TEST_COUNTRY_CODE);
+ verify(mCountryCodeChangedListener).onCountryCodeChanged(TEST_COUNTRY_CODE);
+ verify(mCountryCodeChangedListener2, never()).onCountryCodeChanged(TEST_COUNTRY_CODE);
}
/**