Merge "Move rotation info to display level."
diff --git a/Android.bp b/Android.bp
index afdd832..1c16e46 100644
--- a/Android.bp
+++ b/Android.bp
@@ -466,6 +466,7 @@
"android.hardware.cas-V1.2-java",
"android.hardware.contexthub-V1.0-java",
"android.hardware.contexthub-V1.1-java",
+ "android.hardware.contexthub-V1.2-java",
"android.hardware.gnss-V1.0-java",
"android.hardware.gnss-V2.1-java",
"android.hardware.health-V1.0-java-constants",
@@ -482,6 +483,7 @@
"android.hardware.thermal-V2.0-java",
"android.hardware.tv.input-V1.0-java-constants",
"android.hardware.tv.tuner-V1.0-java-constants",
+ "android.hardware.tv.tuner-V1.1-java-constants",
"android.hardware.usb-V1.0-java-constants",
"android.hardware.usb-V1.1-java-constants",
"android.hardware.usb-V1.2-java-constants",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 05bf7b6..9604466 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -70,6 +70,7 @@
"android.hardware.thermal-V2.0-java",
"android.hardware.tv.input-V1.0-java-constants",
"android.hardware.tv.tuner-V1.0-java-constants",
+ "android.hardware.tv.tuner-V1.1-java-constants",
"android.hardware.usb-V1.0-java-constants",
"android.hardware.usb-V1.1-java-constants",
"android.hardware.usb.gadget-V1.0-java",
@@ -121,7 +122,6 @@
droidstubs {
name: "api-stubs-docs",
defaults: ["metalava-full-api-stubs-default"],
- removed_dex_api_filename: "removed-dex.txt",
arg_files: [
"core/res/AndroidManifest.xml",
],
@@ -142,12 +142,6 @@
baseline_file: "api/lint-baseline.txt",
},
},
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/public/api",
- dest: "android.txt",
- },
- jdiff_enabled: true,
}
droidstubs {
@@ -188,7 +182,6 @@
droidstubs {
name: "system-api-stubs-docs",
defaults: ["metalava-full-api-stubs-default"],
- removed_dex_api_filename: "system-removed-dex.txt",
arg_files: [
"core/res/AndroidManifest.xml",
],
@@ -209,12 +202,6 @@
baseline_file: "api/system-lint-baseline.txt",
},
},
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/system/api",
- dest: "android.txt",
- },
- jdiff_enabled: true,
}
droidstubs {
@@ -242,11 +229,15 @@
droidstubs {
name: "test-api-stubs-docs",
- defaults: ["metalava-full-api-stubs-default"],
+ defaults: ["metalava-non-updatable-api-stubs-default"],
arg_files: [
"core/res/AndroidManifest.xml",
],
- args: metalava_framework_docs_args + " --show-annotation android.annotation.TestApi",
+ args: metalava_framework_docs_args
+ + " --show-annotation android.annotation.TestApi"
+ + " --show-for-stub-purposes-annotation android.annotation.SystemApi\\("
+ + "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS"
+ + "\\)",
check_api: {
current: {
api_file: "api/test-current.txt",
@@ -294,11 +285,6 @@
baseline_file: "api/module-lib-lint-baseline.txt",
},
},
- dist: {
- targets: ["sdk", "win_sdk"],
- dir: "apistubs/android/module-lib/api",
- dest: "android.txt",
- },
}
droidstubs {
@@ -367,6 +353,7 @@
srcs: [ ":api-stubs-docs-non-updatable" ],
static_libs: [
"conscrypt.module.public.api.stubs",
+ "framework-appsearch.stubs",
"framework-graphics.stubs",
"framework-media.stubs",
"framework-mediaprovider.stubs",
@@ -435,7 +422,21 @@
java_library_static {
name: "android_test_stubs_current",
srcs: [ ":test-api-stubs-docs" ],
- static_libs: [ "private-stub-annotations-jar" ],
+ static_libs: [
+ // Modules do not have test APIs, but we want to include their SystemApis, like we include
+ // the SystemApi of framework-non-updatable-sources.
+ "conscrypt.module.public.api.stubs",
+ "framework-appsearch.stubs.system",
+ "framework-graphics.stubs.system",
+ "framework-media.stubs.system",
+ "framework-mediaprovider.stubs.system",
+ "framework-permission.stubs.system",
+ "framework-sdkextensions.stubs.system",
+ "framework-statsd.stubs.system",
+ "framework-tethering.stubs.system",
+ "framework-wifi.stubs.system",
+ "private-stub-annotations-jar",
+ ],
defaults: [
"android_defaults_stubs_current",
"android_stubs_dists_default",
diff --git a/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java b/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java
new file mode 100644
index 0000000..c62269e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.text;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Supplier;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class TextUtilsPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ public static final String TEMPLATE = "Template that combines %s and %d together";
+
+ public String mVar1 = "example";
+ public int mVar2 = 42;
+
+ /**
+ * Measure overhead of formatting a string via {@link String#format}.
+ */
+ @Test
+ public void timeFormatUpstream() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ String res = String.format(TEMPLATE, mVar1, mVar2);
+ }
+ }
+
+ /**
+ * Measure overhead of formatting a string via
+ * {@link TextUtils#formatSimple}.
+ */
+ @Test
+ public void timeFormatLocal() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ String res = TextUtils.formatSimple(TEMPLATE, mVar1, mVar2);
+ }
+ }
+
+ /**
+ * Measure overhead of formatting a string inline.
+ */
+ @Test
+ public void timeFormatInline() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ String res = "Template that combines " + mVar1 + " and " + mVar2 + " together";
+ }
+ }
+
+ /**
+ * Measure overhead of a passing null-check that uses a lambda to
+ * communicate a custom error message.
+ */
+ @Test
+ public void timeFormat_Skip_Lambda() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ requireNonNull(this, () -> {
+ return String.format(TEMPLATE, mVar1, mVar2);
+ });
+ }
+ }
+
+ /**
+ * Measure overhead of a passing null-check that uses varargs to communicate
+ * a custom error message.
+ */
+ @Test
+ public void timeFormat_Skip_Varargs() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ requireNonNull(this, TEMPLATE, mVar1, mVar2);
+ }
+ }
+
+ private static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
+ return obj;
+ }
+
+ private static <T> T requireNonNull(T obj, String format, Object... args) {
+ return obj;
+ }
+}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index ecd1499..1be68f5 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -135,7 +135,6 @@
final int mHeight;
final Point mOutSurfaceSize = new Point();
final SurfaceControl mOutSurfaceControl;
- final SurfaceControl mOutBlastSurfaceControl = new SurfaceControl();
final IntSupplier mViewVisibility;
@@ -158,7 +157,7 @@
session.relayout(mWindow, mParams, mWidth, mHeight,
mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrames,
mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, mOutControls,
- mOutSurfaceSize, mOutBlastSurfaceControl);
+ mOutSurfaceSize);
}
}
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
index dc75825..98daa66 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -22,6 +22,8 @@
import android.os.Parcelable;
import android.util.ArrayMap;
+import com.android.internal.util.Preconditions;
+
import java.util.Collections;
import java.util.Map;
@@ -33,11 +35,11 @@
* @param <ValueType> The type of result objects associated with the keys.
* @hide
*/
-public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
+public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
@NonNull private final Map<KeyType, ValueType> mSuccesses;
@NonNull private final Map<KeyType, AppSearchResult<ValueType>> mFailures;
- private AppSearchBatchResult(
+ AppSearchBatchResult(
@NonNull Map<KeyType, ValueType> successes,
@NonNull Map<KeyType, AppSearchResult<ValueType>> failures) {
mSuccesses = successes;
@@ -61,8 +63,8 @@
}
/**
- * Returns a {@link Map} of all successful keys mapped to the successful {@link ValueType}
- * values they produced.
+ * Returns a {@link Map} of all successful keys mapped to the successful
+ * {@link AppSearchResult}s they produced.
*
* <p>The values of the {@link Map} will not be {@code null}.
*/
@@ -82,6 +84,22 @@
return mFailures;
}
+ /**
+ * Asserts that this {@link AppSearchBatchResult} has no failures.
+ * @hide
+ */
+ public void checkSuccess() {
+ if (!isSuccess()) {
+ throw new IllegalStateException("AppSearchBatchResult has failures: " + this);
+ }
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "{\n successes: " + mSuccesses + "\n failures: " + mFailures + "\n}";
+ }
+
@Override
public int describeContents() {
return 0;
@@ -112,16 +130,18 @@
public static final class Builder<KeyType, ValueType> {
private final Map<KeyType, ValueType> mSuccesses = new ArrayMap<>();
private final Map<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();
-
- /** Creates a new {@link Builder} for this {@link AppSearchBatchResult}. */
- public Builder() {}
+ private boolean mBuilt = false;
/**
* Associates the {@code key} with the given successful return value.
*
* <p>Any previous mapping for a key, whether success or failure, is deleted.
*/
- public Builder setSuccess(@NonNull KeyType key, @Nullable ValueType result) {
+ @NonNull
+ public Builder<KeyType, ValueType> setSuccess(
+ @NonNull KeyType key, @Nullable ValueType result) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkNotNull(key);
return setResult(key, AppSearchResult.newSuccessfulResult(result));
}
@@ -130,10 +150,13 @@
*
* <p>Any previous mapping for a key, whether success or failure, is deleted.
*/
- public Builder setFailure(
+ @NonNull
+ public Builder<KeyType, ValueType> setFailure(
@NonNull KeyType key,
@AppSearchResult.ResultCode int resultCode,
@Nullable String errorMessage) {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ Preconditions.checkNotNull(key);
return setResult(key, AppSearchResult.newFailedResult(resultCode, errorMessage));
}
@@ -143,7 +166,11 @@
* <p>Any previous mapping for a key, whether success or failure, is deleted.
*/
@NonNull
- public Builder setResult(@NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) {
+ 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);
if (result.isSuccess()) {
mSuccesses.put(key, result.getResultValue());
mFailures.remove(key);
@@ -157,6 +184,8 @@
/** Builds an {@link AppSearchBatchResult} from the contents of this {@link Builder}. */
@NonNull
public AppSearchBatchResult<KeyType, ValueType> build() {
+ Preconditions.checkState(!mBuilt, "Builder has already been used");
+ mBuilt = true;
return new AppSearchBatchResult<>(mSuccesses, mFailures);
}
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
index 7f38348..979eab9 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -32,9 +32,12 @@
* @param <ValueType> The type of result object for successful calls.
* @hide
*/
-public class AppSearchResult<ValueType> implements Parcelable {
- /** Result codes from {@link AppSearchManager} methods. */
- @IntDef(prefix = {"RESULT_"}, value = {
+public final class AppSearchResult<ValueType> implements Parcelable {
+ /**
+ * Result codes from {@link AppSearchManager} methods.
+ * @hide
+ */
+ @IntDef(value = {
RESULT_OK,
RESULT_UNKNOWN_ERROR,
RESULT_INTERNAL_ERROR,
@@ -120,15 +123,18 @@
}
/**
- * Returns the returned value associated with this result.
+ * Returns the result value associated with this result, if it was successful.
*
- * <p>If {@link #isSuccess} is {@code false}, the result value is always {@code null}. The value
- * may be {@code null} even if {@link #isSuccess} is {@code true}. See the documentation of the
- * particular {@link AppSearchManager} call producing this {@link AppSearchResult} for what is
- * returned by {@link #getResultValue}.
+ * <p>See the documentation of the particular {@link AppSearchManager} call producing this
+ * {@link AppSearchResult} for what is placed in the result value by that call.
+ *
+ * @throws IllegalStateException if this {@link AppSearchResult} is not successful.
*/
@Nullable
public ValueType getResultValue() {
+ if (!isSuccess()) {
+ throw new IllegalStateException("AppSearchResult is a failure: " + this);
+ }
return mResultValue;
}
@@ -146,14 +152,14 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AppSearchResult)) {
return false;
}
- AppSearchResult<?> otherResult = (AppSearchResult) other;
+ AppSearchResult<?> otherResult = (AppSearchResult<?>) other;
return mResultCode == otherResult.mResultCode
&& Objects.equals(mResultValue, otherResult.mResultValue)
&& Objects.equals(mErrorMessage, otherResult.mErrorMessage);
@@ -168,9 +174,9 @@
@NonNull
public String toString() {
if (isSuccess()) {
- return "AppSearchResult [SUCCESS]: " + mResultValue;
+ return "[SUCCESS]: " + mResultValue;
}
- return "AppSearchResult [FAILURE(" + mResultCode + ")]: " + mErrorMessage;
+ return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage;
}
@Override
diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java
index 00f6e75..d490469 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java
@@ -54,6 +54,10 @@
mResultCode = resultCode;
}
+ public @AppSearchResult.ResultCode int getResultCode() {
+ return mResultCode;
+ }
+
/**
* Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult}
*/
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index c1e6b0f..60f7005 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -69,9 +69,7 @@
private static AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
throws AppSearchException {
File appSearchDir = getAppSearchDir(context, userId);
- AppSearchImpl appSearchImpl = new AppSearchImpl(appSearchDir);
- appSearchImpl.initialize();
- return appSearchImpl;
+ return AppSearchImpl.create(appSearchDir);
}
private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java
index 462f458..642378d 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java
@@ -18,7 +18,6 @@
import android.util.Log;
-import android.annotation.AnyThread;
import com.android.internal.annotations.GuardedBy;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +26,7 @@
import android.annotation.WorkerThread;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.exceptions.AppSearchException;
+import com.android.internal.util.Preconditions;
import com.google.android.icing.IcingSearchEngine;
import com.google.android.icing.proto.DeleteByNamespaceResultProto;
@@ -58,7 +58,6 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -66,8 +65,7 @@
* Manages interaction with the native IcingSearchEngine and other components to implement AppSearch
* functionality.
*
- * <p>Callers should call {@link #initialize} before using the AppSearchImpl instance. Never create
- * two instances using the same folder.
+ * <p>Never create two instances using the same folder.
*
* <p>A single instance of {@link AppSearchImpl} can support all databases. Schemas and documents
* are physically saved together in {@link IcingSearchEngine}, but logically isolated:
@@ -106,9 +104,7 @@
static final int CHECK_OPTIMIZE_INTERVAL = 100;
private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
- private final CountDownLatch mInitCompleteLatch = new CountDownLatch(1);
- private final File mIcingDir;
- private IcingSearchEngine mIcingSearchEngine;
+ private final IcingSearchEngine mIcingSearchEngine;
// The map contains schemaTypes and namespaces for all database. All values in the map have
// been already added database name prefix.
@@ -121,33 +117,24 @@
*/
private int mOptimizeIntervalCount = 0;
- /** Creates an instance of {@link AppSearchImpl} which writes data to the given folder. */
- @AnyThread
- public AppSearchImpl(@NonNull File icingDir) {
- mIcingDir = icingDir;
+ /**
+ * Creates and initializes an instance of {@link AppSearchImpl} which writes data to the given
+ * folder.
+ */
+ @NonNull
+ public static AppSearchImpl create(@NonNull File icingDir) throws AppSearchException {
+ Preconditions.checkNotNull(icingDir);
+ return new AppSearchImpl(icingDir);
}
- /**
- * Initializes the underlying IcingSearchEngine.
- *
- * <p>This method belongs to mutate group.
- *
- * @throws AppSearchException on IcingSearchEngine error.
- */
- public void initialize() throws AppSearchException {
- if (isInitialized()) {
- return;
- }
+ private AppSearchImpl(@NonNull File icingDir) throws AppSearchException {
boolean isReset = false;
mReadWriteLock.writeLock().lock();
try {
- // We synchronize here because we don't want to call IcingSearchEngine.initialize() more
- // than once. It's unnecessary and can be a costly operation.
- if (isInitialized()) {
- return;
- }
+ // We synchronize here because we don't want to call IcingSearchEngine.initialize() more
+ // than once. It's unnecessary and can be a costly operation.
IcingSearchEngineOptions options = IcingSearchEngineOptions.newBuilder()
- .setBaseDir(mIcingDir.getAbsolutePath()).build();
+ .setBaseDir(icingDir.getAbsolutePath()).build();
mIcingSearchEngine = new IcingSearchEngine(options);
InitializeResultProto initializeResultProto = mIcingSearchEngine.initialize();
@@ -170,7 +157,8 @@
for (String qualifiedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
addToMap(mNamespaceMap, getDatabaseName(qualifiedNamespace), qualifiedNamespace);
}
- mInitCompleteLatch.countDown();
+ // TODO(b/155939114): It's possible to optimize after init, which would reduce the time
+ // to when we're able to serve queries. Consider moving this optimize call out.
if (!isReset) {
checkForOptimize(/* force= */ true);
}
@@ -179,12 +167,6 @@
}
}
- /** Checks if the internal state of {@link AppSearchImpl} has been initialized. */
- @AnyThread
- public boolean isInitialized() {
- return mInitCompleteLatch.getCount() == 0;
- }
-
/**
* Updates the AppSearch schema for this app.
*
@@ -195,12 +177,9 @@
* @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents
* which do not comply with the new schema will be deleted.
* @throws AppSearchException on IcingSearchEngine error.
- * @throws InterruptedException if the current thread was interrupted during execution.
*/
public void setSchema(@NonNull String databaseName, @NonNull SchemaProto origSchema,
- boolean forceOverride) throws AppSearchException, InterruptedException {
- awaitInitialized();
-
+ boolean forceOverride) throws AppSearchException {
SchemaProto schemaProto = getSchemaProto();
SchemaProto.Builder existingSchemaBuilder = schemaProto.toBuilder();
@@ -212,10 +191,32 @@
SetSchemaResultProto setSchemaResultProto;
mReadWriteLock.writeLock().lock();
try {
- setSchemaResultProto = mIcingSearchEngine.setSchema(existingSchemaBuilder.build(),
- forceOverride);
- checkSuccess(setSchemaResultProto.getStatus());
+ // Apply schema
+ setSchemaResultProto =
+ mIcingSearchEngine.setSchema(existingSchemaBuilder.build(), forceOverride);
+
+ // Determine whether it succeeded.
+ try {
+ checkSuccess(setSchemaResultProto.getStatus());
+ } catch (AppSearchException e) {
+ // Improve the error message by merging in information about incompatible types.
+ if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
+ || setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0) {
+ String newMessage = e.getMessage()
+ + "\n Deleted types: "
+ + setSchemaResultProto.getDeletedSchemaTypesList()
+ + "\n Incompatible types: "
+ + setSchemaResultProto.getIncompatibleSchemaTypesList();
+ throw new AppSearchException(e.getResultCode(), newMessage, e.getCause());
+ } else {
+ throw e;
+ }
+ }
+
+ // Update derived data structures.
mSchemaMap.put(databaseName, newTypeNames);
+
+ // Determine whether to schedule an immediate optimize.
if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
|| (setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0
&& forceOverride)) {
@@ -237,12 +238,9 @@
* @param databaseName The databaseName this document resides in.
* @param document The document to index.
* @throws AppSearchException on IcingSearchEngine error.
- * @throws InterruptedException if the current thread was interrupted during execution.
*/
public void putDocument(@NonNull String databaseName, @NonNull DocumentProto document)
- throws AppSearchException, InterruptedException {
- awaitInitialized();
-
+ throws AppSearchException {
DocumentProto.Builder documentBuilder = document.toBuilder();
rewriteDocumentTypes(getDatabasePrefix(databaseName), documentBuilder, /*add=*/ true);
@@ -270,12 +268,10 @@
* @param uri The URI of the document to get.
* @return The Document contents, or {@code null} if no such URI exists in the system.
* @throws AppSearchException on IcingSearchEngine error.
- * @throws InterruptedException if the current thread was interrupted during execution.
*/
@Nullable
public DocumentProto getDocument(@NonNull String databaseName, @NonNull String namespace,
- @NonNull String uri) throws AppSearchException, InterruptedException {
- awaitInitialized();
+ @NonNull String uri) throws AppSearchException {
GetResultProto getResultProto;
mReadWriteLock.readLock().lock();
try {
@@ -303,16 +299,13 @@
* @return The results of performing this search The proto might have no {@code results} if no
* documents matched the query.
* @throws AppSearchException on IcingSearchEngine error.
- * @throws InterruptedException if the current thread was interrupted during execution.
*/
@NonNull
public SearchResultProto query(
@NonNull String databaseName,
@NonNull SearchSpecProto searchSpec,
@NonNull ResultSpecProto resultSpec,
- @NonNull ScoringSpecProto scoringSpec) throws AppSearchException, InterruptedException {
- awaitInitialized();
-
+ @NonNull ScoringSpecProto scoringSpec) throws AppSearchException {
SearchSpecProto.Builder searchSpecBuilder = searchSpec.toBuilder();
SearchResultProto searchResultProto;
mReadWriteLock.readLock().lock();
@@ -347,13 +340,10 @@
* @param nextPageToken The token of pre-loaded results of previously executed query.
* @return The next page of results of previously executed query.
* @throws AppSearchException on IcingSearchEngine error.
- * @throws InterruptedException if the current thread was interrupted during execution.
*/
@NonNull
public SearchResultProto getNextPage(@NonNull String databaseName, long nextPageToken)
- throws AppSearchException, InterruptedException {
- awaitInitialized();
-
+ throws AppSearchException {
SearchResultProto searchResultProto = mIcingSearchEngine.getNextPage(nextPageToken);
checkSuccess(searchResultProto.getStatus());
if (searchResultProto.getResultsCount() == 0) {
@@ -367,8 +357,7 @@
* @param nextPageToken The token of pre-loaded results of previously executed query to be
* Invalidated.
*/
- public void invalidateNextPageToken(long nextPageToken) throws InterruptedException {
- awaitInitialized();
+ public void invalidateNextPageToken(long nextPageToken) {
mIcingSearchEngine.invalidateNextPageToken(nextPageToken);
}
@@ -381,12 +370,9 @@
* @param namespace Namespace of the document to remove.
* @param uri URI of the document to remove.
* @throws AppSearchException on IcingSearchEngine error.
- * @throws InterruptedException if the current thread was interrupted during execution.
*/
public void remove(@NonNull String databaseName, @NonNull String namespace,
- @NonNull String uri) throws AppSearchException, InterruptedException {
- awaitInitialized();
-
+ @NonNull String uri) throws AppSearchException {
String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace;
DeleteResultProto deleteResultProto;
mReadWriteLock.writeLock().lock();
@@ -407,12 +393,9 @@
* @param databaseName The databaseName that contains documents of schemaType.
* @param schemaType The schemaType of documents to remove.
* @throws AppSearchException on IcingSearchEngine error.
- * @throws InterruptedException if the current thread was interrupted during execution.
*/
public void removeByType(@NonNull String databaseName, @NonNull String schemaType)
- throws AppSearchException, InterruptedException {
- awaitInitialized();
-
+ throws AppSearchException {
String qualifiedType = getDatabasePrefix(databaseName) + schemaType;
DeleteBySchemaTypeResultProto deleteBySchemaTypeResultProto;
mReadWriteLock.writeLock().lock();
@@ -437,12 +420,9 @@
* @param databaseName The databaseName that contains documents of namespace.
* @param namespace The namespace of documents to remove.
* @throws AppSearchException on IcingSearchEngine error.
- * @throws InterruptedException if the current thread was interrupted during execution.
*/
public void removeByNamespace(@NonNull String databaseName, @NonNull String namespace)
- throws AppSearchException, InterruptedException {
- awaitInitialized();
-
+ throws AppSearchException {
String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace;
DeleteByNamespaceResultProto deleteByNamespaceResultProto;
mReadWriteLock.writeLock().lock();
@@ -469,11 +449,9 @@
*
* @param databaseName The databaseName to remove all documents from.
* @throws AppSearchException on IcingSearchEngine error.
- * @throws InterruptedException if the current thread was interrupted during execution.
*/
public void removeAll(@NonNull String databaseName)
- throws AppSearchException, InterruptedException {
- awaitInitialized();
+ throws AppSearchException {
mReadWriteLock.writeLock().lock();
try {
Set<String> existingNamespaces = mNamespaceMap.get(databaseName);
@@ -733,15 +711,6 @@
}
/**
- * Waits for the instance to become initialized.
- *
- * @throws InterruptedException if the current thread was interrupted during waiting.
- */
- private void awaitInitialized() throws InterruptedException {
- mInitCompleteLatch.await();
- }
-
- /**
* Checks the given status code and throws an {@link AppSearchException} if code is an error.
*
* @throws AppSearchException on error codes.
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
index 1d07e88..8f94273 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.os.UserHandle;
import android.text.format.TimeMigrationUtils;
import android.util.Slog;
@@ -34,8 +35,8 @@
static Resources getPackageResources(@NonNull Context context,
@NonNull String packageName, int userId) {
try {
- return context.getPackageManager()
- .getResourcesForApplicationAsUser(packageName, userId);
+ return context.createContextAsUser(UserHandle.of(userId), /* flags */ 0)
+ .getPackageManager().getResourcesForApplication(packageName);
} catch (PackageManager.NameNotFoundException e) {
Slog.d(TAG, "Unknown package in user " + userId + ": "
+ packageName, e);
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 1a81587..81f22fe 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -2179,7 +2179,7 @@
if (getContext().getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
mLocationRequest = new LocationRequest.Builder(/*intervalMillis=*/ 0)
- .setQuality(LocationRequest.ACCURACY_FINE)
+ .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
.setMaxUpdates(1)
.build();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
index f672e4b..45ea233 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
@@ -16,10 +16,13 @@
package com.android.server.alarm;
+import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
+import static com.android.server.alarm.AlarmManagerService.clampPositive;
+
import android.app.AlarmManager;
import android.app.IAlarmListener;
import android.app.PendingIntent;
@@ -32,8 +35,28 @@
import java.text.SimpleDateFormat;
import java.util.Date;
+/**
+ * Class to describe an alarm that is used to the set the kernel timer that returns when the timer
+ * expires. The timer will wake up the device if the alarm is a "wakeup" alarm.
+ */
class Alarm {
+ private static final int NUM_POLICIES = 2;
+ /**
+ * Index used to store the time the alarm was requested to expire. To be used with
+ * {@link #setPolicyElapsed(int, long)}
+ */
+ public static final int REQUESTER_POLICY_INDEX = 0;
+ /**
+ * Index used to store the earliest time the alarm can expire based on app-standby policy.
+ * To be used with {@link #setPolicyElapsed(int, long)}
+ */
+ public static final int APP_STANDBY_POLICY_INDEX = 1;
+
public final int type;
+ /**
+ * The original trigger time supplied by the caller. This can be in the elapsed or rtc time base
+ * depending on the type of this alarm
+ */
public final long origWhen;
public final boolean wakeup;
public final PendingIntent operation;
@@ -47,42 +70,40 @@
public final int creatorUid;
public final String packageName;
public final String sourcePackage;
+ public final long windowLength;
+ public final long repeatInterval;
public int count;
- public long when;
- public long windowLength;
- public long whenElapsed; // 'when' in the elapsed time base
- public long maxWhenElapsed; // also in the elapsed time base
- // Expected alarm expiry time before app standby deferring is applied.
- public long expectedWhenElapsed;
- public long expectedMaxWhenElapsed;
- public long repeatInterval;
+ /** The earliest time this alarm is eligible to fire according to each policy */
+ private long[] mPolicyWhenElapsed;
+ /** The ultimate delivery time to be used for this alarm */
+ private long mWhenElapsed;
+ private long mMaxWhenElapsed;
public AlarmManagerService.PriorityClass priorityClass;
- Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
- long _interval, PendingIntent _op, IAlarmListener _rec, String _listenerTag,
- WorkSource _ws, int _flags, AlarmManager.AlarmClockInfo _info,
- int _uid, String _pkgName) {
- type = _type;
- origWhen = _when;
- wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP
- || _type == AlarmManager.RTC_WAKEUP;
- when = _when;
- whenElapsed = _whenElapsed;
- expectedWhenElapsed = _whenElapsed;
- windowLength = _windowLength;
- maxWhenElapsed = expectedMaxWhenElapsed = AlarmManagerService.clampPositive(_maxWhen);
- repeatInterval = _interval;
- operation = _op;
- listener = _rec;
- listenerTag = _listenerTag;
- statsTag = makeTag(_op, _listenerTag, _type);
- workSource = _ws;
- flags = _flags;
- alarmClock = _info;
- uid = _uid;
- packageName = _pkgName;
+ Alarm(int type, long when, long requestedWhenElapsed, long windowLength, long interval,
+ PendingIntent op, IAlarmListener rec, String listenerTag, WorkSource ws, int flags,
+ AlarmManager.AlarmClockInfo info, int uid, String pkgName) {
+ this.type = type;
+ origWhen = when;
+ wakeup = type == AlarmManager.ELAPSED_REALTIME_WAKEUP
+ || type == AlarmManager.RTC_WAKEUP;
+ mPolicyWhenElapsed = new long[NUM_POLICIES];
+ mPolicyWhenElapsed[REQUESTER_POLICY_INDEX] = requestedWhenElapsed;
+ mWhenElapsed = requestedWhenElapsed;
+ this.windowLength = windowLength;
+ mMaxWhenElapsed = clampPositive(requestedWhenElapsed + windowLength);
+ repeatInterval = interval;
+ operation = op;
+ listener = rec;
+ this.listenerTag = listenerTag;
+ statsTag = makeTag(op, listenerTag, type);
+ workSource = ws;
+ this.flags = flags;
+ alarmClock = info;
+ this.uid = uid;
+ packageName = pkgName;
sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName;
- creatorUid = (operation != null) ? operation.getCreatorUid() : uid;
+ creatorUid = (operation != null) ? operation.getCreatorUid() : this.uid;
}
public static String makeTag(PendingIntent pi, String tag, int type) {
@@ -91,13 +112,6 @@
return (pi != null) ? pi.getTag(alarmString) : (alarmString + tag);
}
- public AlarmManagerService.WakeupEvent makeWakeupEvent(long nowRTC) {
- return new AlarmManagerService.WakeupEvent(nowRTC, creatorUid,
- (operation != null)
- ? operation.getIntent().getAction()
- : ("<listener>:" + listenerTag));
- }
-
// Returns true if either matches
public boolean matches(PendingIntent pi, IAlarmListener rec) {
return (operation != null)
@@ -109,6 +123,65 @@
return packageName.equals(sourcePackage);
}
+ /**
+ * Get the earliest time this alarm is allowed to expire based on the given policy.
+ *
+ * @param policyIndex The index of the policy. One of [{@link #REQUESTER_POLICY_INDEX},
+ * {@link #APP_STANDBY_POLICY_INDEX}].
+ */
+ public long getPolicyElapsed(int policyIndex) {
+ return mPolicyWhenElapsed[policyIndex];
+ }
+
+ /**
+ * Get the earliest time that this alarm should be delivered to the requesting app.
+ */
+ public long getWhenElapsed() {
+ return mWhenElapsed;
+ }
+
+ /**
+ * Get the latest time that this alarm should be delivered to the requesting app. Will be equal
+ * to {@link #getWhenElapsed()} in case this is an exact alarm.
+ */
+ public long getMaxWhenElapsed() {
+ return mMaxWhenElapsed;
+ }
+
+ /**
+ * Set the earliest time this alarm can expire based on the passed policy index.
+ *
+ * @return {@code true} if this change resulted in a change in the ultimate delivery time (or
+ * time window in the case of inexact alarms) of this alarm.
+ * @see #getWhenElapsed()
+ * @see #getMaxWhenElapsed()
+ * @see #getPolicyElapsed(int)
+ */
+ public boolean setPolicyElapsed(int policyIndex, long policyElapsed) {
+ mPolicyWhenElapsed[policyIndex] = policyElapsed;
+ return updateWhenElapsed();
+ }
+
+ /**
+ * @return {@code true} if either {@link #mWhenElapsed} or {@link #mMaxWhenElapsed} changes
+ * due to this call.
+ */
+ private boolean updateWhenElapsed() {
+ final long oldWhenElapsed = mWhenElapsed;
+ mWhenElapsed = 0;
+ for (int i = 0; i < NUM_POLICIES; i++) {
+ mWhenElapsed = Math.max(mWhenElapsed, mPolicyWhenElapsed[i]);
+ }
+
+ final long oldMaxWhenElapsed = mMaxWhenElapsed;
+ // windowLength should always be >= 0 here.
+ final long maxRequestedElapsed = clampPositive(
+ mPolicyWhenElapsed[REQUESTER_POLICY_INDEX] + windowLength);
+ mMaxWhenElapsed = Math.max(maxRequestedElapsed, mWhenElapsed);
+
+ return (oldWhenElapsed != mWhenElapsed) || (oldMaxWhenElapsed != mMaxWhenElapsed);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
@@ -116,11 +189,11 @@
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" type ");
sb.append(type);
- sb.append(" when ");
- sb.append(when);
+ sb.append(" origWhen ");
+ sb.append(origWhen);
sb.append(" ");
sb.append(" whenElapsed ");
- sb.append(whenElapsed);
+ sb.append(getWhenElapsed());
sb.append(" ");
sb.append(sourcePackage);
sb.append('}');
@@ -136,30 +209,46 @@
dump(ipw, nowELAPSED, sdf);
}
+ private static String policyIndexToString(int index) {
+ switch (index) {
+ case REQUESTER_POLICY_INDEX:
+ return "requester";
+ case APP_STANDBY_POLICY_INDEX:
+ return "app_standby";
+ default:
+ return "unknown";
+ }
+ }
+
+ public static String typeToString(int type) {
+ switch (type) {
+ case RTC:
+ return "RTC";
+ case RTC_WAKEUP:
+ return "RTC_WAKEUP";
+ case ELAPSED_REALTIME:
+ return "ELAPSED";
+ case ELAPSED_REALTIME_WAKEUP:
+ return "ELAPSED_WAKEUP";
+ default:
+ return "--unknown--";
+ }
+ }
+
public void dump(IndentingPrintWriter ipw, long nowELAPSED, SimpleDateFormat sdf) {
final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
ipw.print("tag=");
ipw.println(statsTag);
ipw.print("type=");
- ipw.print(type);
- ipw.print(" expectedWhenElapsed=");
- TimeUtils.formatDuration(expectedWhenElapsed, nowELAPSED, ipw);
- ipw.print(" expectedMaxWhenElapsed=");
- TimeUtils.formatDuration(expectedMaxWhenElapsed, nowELAPSED, ipw);
- ipw.print(" whenElapsed=");
- TimeUtils.formatDuration(whenElapsed, nowELAPSED, ipw);
- ipw.print(" maxWhenElapsed=");
- TimeUtils.formatDuration(maxWhenElapsed, nowELAPSED, ipw);
- ipw.print(" when=");
+ ipw.print(typeToString(type));
+ ipw.print(" origWhen=");
if (isRtc) {
- ipw.print(sdf.format(new Date(when)));
+ ipw.print(sdf.format(new Date(origWhen)));
} else {
- TimeUtils.formatDuration(when, nowELAPSED, ipw);
+ TimeUtils.formatDuration(origWhen, nowELAPSED, ipw);
}
- ipw.println();
-
- ipw.print("window=");
+ ipw.print(" window=");
TimeUtils.formatDuration(windowLength, ipw);
ipw.print(" repeatInterval=");
ipw.print(repeatInterval);
@@ -168,6 +257,19 @@
ipw.print(" flags=0x");
ipw.println(Integer.toHexString(flags));
+ ipw.print("policyWhenElapsed:");
+ for (int i = 0; i < NUM_POLICIES; i++) {
+ ipw.print(" " + policyIndexToString(i) + "=");
+ TimeUtils.formatDuration(mPolicyWhenElapsed[i], nowELAPSED, ipw);
+ }
+ ipw.println();
+
+ ipw.print("whenElapsed=");
+ TimeUtils.formatDuration(getWhenElapsed(), nowELAPSED, ipw);
+ ipw.print(" maxWhenElapsed=");
+ TimeUtils.formatDuration(mMaxWhenElapsed, nowELAPSED, ipw);
+ ipw.println();
+
if (alarmClock != null) {
ipw.println("Alarm clock:");
@@ -177,9 +279,10 @@
ipw.print(" showIntent=");
ipw.println(alarmClock.getShowIntent());
}
- ipw.print("operation=");
- ipw.println(operation);
-
+ if (operation != null) {
+ ipw.print("operation=");
+ ipw.println(operation);
+ }
if (listener != null) {
ipw.print("listener=");
ipw.println(listener.asBinder());
@@ -191,7 +294,7 @@
proto.write(AlarmProto.TAG, statsTag);
proto.write(AlarmProto.TYPE, type);
- proto.write(AlarmProto.TIME_UNTIL_WHEN_ELAPSED_MS, whenElapsed - nowElapsed);
+ proto.write(AlarmProto.TIME_UNTIL_WHEN_ELAPSED_MS, getWhenElapsed() - nowElapsed);
proto.write(AlarmProto.WINDOW_LENGTH_MS, windowLength);
proto.write(AlarmProto.REPEAT_INTERVAL_MS, repeatInterval);
proto.write(AlarmProto.COUNT, count);
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 05910a5..82819da 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -26,6 +26,9 @@
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.os.UserHandle.USER_SYSTEM;
+import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
+import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
+
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.Activity;
@@ -727,9 +730,9 @@
}
// within each class, sort by nominal delivery time
- if (lhs.whenElapsed < rhs.whenElapsed) {
+ if (lhs.getWhenElapsed() < rhs.getWhenElapsed()) {
return -1;
- } else if (lhs.whenElapsed > rhs.whenElapsed) {
+ } else if (lhs.getWhenElapsed() > rhs.getWhenElapsed()) {
return 1;
}
@@ -798,9 +801,12 @@
this(context, new Injector(context));
}
+ private static boolean isRtc(int type) {
+ return (type == RTC || type == RTC_WAKEUP);
+ }
+
private long convertToElapsed(long when, int type) {
- final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
- if (isRtc) {
+ if (isRtc(type)) {
when -= mInjector.getCurrentTimeMillis() - mInjector.getElapsedRealtime();
}
return when;
@@ -823,13 +829,29 @@
}
// The RTC clock has moved arbitrarily, so we need to recalculate all the RTC alarm deliveries.
- void reevaluateRtcAlarms(final long nowElapsed) {
+ void reevaluateRtcAlarms() {
synchronized (mLock) {
- final ArrayList<Alarm> rtcAlarms = mAlarmStore.remove(a -> (a.type == RTC
- || a.type == RTC_WAKEUP));
- for (final Alarm a : rtcAlarms) {
- restoreAlarmLocked(a, nowElapsed);
- setImplLocked(a);
+ boolean changed = mAlarmStore.updateAlarmDeliveries(a -> {
+ if (!isRtc(a.type)) {
+ return false;
+ }
+ return restoreRequestedTime(a);
+ });
+
+ if (mNextWakeFromIdle != null && isRtc(mNextWakeFromIdle.type)) {
+ // The next wake from idle got updated due to the rtc time change, implying we need
+ // to update the time we have to come out of idle too.
+ changed |= mAlarmStore.updateAlarmDeliveries(a -> {
+ if (a != mPendingIdleUntil) {
+ return false;
+ }
+ return adjustIdleUntilTime(a);
+ });
+ }
+
+ if (changed) {
+ rescheduleKernelAlarmsLocked();
+ // Only time shifted, so the next alarm clock will not change
}
}
}
@@ -844,7 +866,7 @@
boolean reorderAlarmsBasedOnStandbyBuckets(ArraySet<Pair<String, Integer>> targetPackages) {
final long start = mStatLogger.getTime();
- final boolean changed = mAlarmStore.recalculateAlarmDeliveries(a -> {
+ final boolean changed = mAlarmStore.updateAlarmDeliveries(a -> {
final Pair<String, Integer> packageUser =
Pair.create(a.sourcePackage, UserHandle.getUserId(a.creatorUid));
if (targetPackages != null && !targetPackages.contains(packageUser)) {
@@ -857,23 +879,8 @@
return changed;
}
- private void restoreAlarmLocked(Alarm a, long nowElapsed) {
- a.when = a.origWhen;
- long whenElapsed = convertToElapsed(a.when, a.type);
- final long maxElapsed;
- if (a.windowLength == AlarmManager.WINDOW_EXACT) {
- // Exact
- maxElapsed = whenElapsed;
- } else {
- // Not exact. Preserve any explicit window, otherwise recalculate
- // the window based on the alarm's new futurity. Note that this
- // reflects a policy of preferring timely to deferred delivery.
- maxElapsed = (a.windowLength > 0)
- ? clampPositive(whenElapsed + a.windowLength)
- : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
- }
- a.expectedWhenElapsed = a.whenElapsed = whenElapsed;
- a.expectedMaxWhenElapsed = a.maxWhenElapsed = maxElapsed;
+ private boolean restoreRequestedTime(Alarm a) {
+ return a.setPolicyElapsed(REQUESTER_POLICY_INDEX, convertToElapsed(a.origWhen, a.type));
}
static long clampPositive(long val) {
@@ -973,14 +980,17 @@
// Recurring alarms may have passed several alarm intervals while the
// alarm was kept pending. Send the appropriate trigger count.
if (alarm.repeatInterval > 0) {
- alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
+ alarm.count += (nowELAPSED - alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX))
+ / alarm.repeatInterval;
// Also schedule its next recurrence
final long delta = alarm.count * alarm.repeatInterval;
- final long nextElapsed = alarm.expectedWhenElapsed + delta;
- setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
- maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
- alarm.repeatInterval, alarm.operation, null, null, alarm.flags,
- alarm.workSource, alarm.alarmClock, alarm.uid, alarm.packageName);
+ final long nextElapsed = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX) + delta;
+ final long nextMaxElapsed = maxTriggerTime(nowELAPSED, nextElapsed,
+ alarm.repeatInterval);
+ setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
+ nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
+ null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
+ alarm.packageName);
// Kernel alarms will be rescheduled as needed in setImplLocked
}
}
@@ -1026,18 +1036,10 @@
if (mPendingWhileIdleAlarms.size() > 0) {
ArrayList<Alarm> alarms = mPendingWhileIdleAlarms;
mPendingWhileIdleAlarms = new ArrayList<>();
- final long nowElapsed = mInjector.getElapsedRealtime();
for (int i = alarms.size() - 1; i >= 0; i--) {
- Alarm a = alarms.get(i);
- restoreAlarmLocked(a, nowElapsed);
- setImplLocked(a);
+ setImplLocked(alarms.get(i));
}
}
-
- // Reschedule everything.
- rescheduleKernelAlarmsLocked();
- updateNextAlarmClockLocked();
-
}
static final class InFlight {
@@ -1449,6 +1451,11 @@
}
}
+ 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) {
@@ -1515,17 +1522,17 @@
Slog.w(TAG, errorMsg);
throw new IllegalStateException(errorMsg);
}
- setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed,
- interval, operation, directReceiver, listenerTag, flags, workSource,
- alarmClock, callingUid, callingPackage);
+ setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, interval, operation,
+ directReceiver, listenerTag, flags, workSource, alarmClock, callingUid,
+ callingPackage);
}
}
private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
- long maxWhen, long interval, PendingIntent operation, IAlarmListener directReceiver,
+ long interval, PendingIntent operation, IAlarmListener directReceiver,
String listenerTag, int flags, WorkSource workSource,
AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage) {
- Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval,
+ final Alarm a = new Alarm(type, when, whenElapsed, windowLength, interval,
operation, directReceiver, listenerTag, workSource, flags, alarmClock,
callingUid, callingPackage);
if (mActivityManagerInternal.isAppStartModeDisabled(callingUid, callingPackage)) {
@@ -1560,72 +1567,55 @@
}
/**
- * Adjusts the idle-until alarm delivery time based on the upcoming wake-from-idle alarm.
+ * An alarm with {@link AlarmManager#FLAG_IDLE_UNTIL} is a special alarm that will put the
+ * system into idle until it goes off. We need to pull it earlier if there are existing alarms
+ * that have requested to bring us out of idle at an earlier time.
*
* @param alarm The alarm to adjust
* @return true if the alarm delivery time was updated.
*/
private boolean adjustIdleUntilTime(Alarm alarm) {
- if ((alarm.flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) {
+ if ((alarm.flags & AlarmManager.FLAG_IDLE_UNTIL) == 0) {
return false;
}
- // This is a special alarm that will put the system into idle until it goes off.
- // The caller has given the time they want this to happen at, however we need
- // to pull that earlier if there are existing alarms that have requested to
- // bring us out of idle at an earlier time.
- if (mNextWakeFromIdle != null && alarm.whenElapsed > mNextWakeFromIdle.whenElapsed) {
- alarm.when = alarm.whenElapsed = alarm.maxWhenElapsed = mNextWakeFromIdle.whenElapsed;
+ restoreRequestedTime(alarm);
+ long triggerBeforeFuzz = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX);
+ if (mNextWakeFromIdle != null && triggerBeforeFuzz > mNextWakeFromIdle.getWhenElapsed()) {
+ triggerBeforeFuzz = mNextWakeFromIdle.getWhenElapsed();
}
// Add fuzz to make the alarm go off some time before the actual desired time.
- final long nowElapsed = mInjector.getElapsedRealtime();
- final int fuzz = fuzzForDuration(alarm.whenElapsed - nowElapsed);
+ final int fuzz = fuzzForDuration(alarm.getWhenElapsed() - mInjector.getElapsedRealtime());
+ final int delta;
if (fuzz > 0) {
if (mRandom == null) {
mRandom = new Random();
}
- final int delta = mRandom.nextInt(fuzz);
- alarm.whenElapsed -= delta;
- if (false) {
- Slog.d(TAG, "Alarm when: " + alarm.whenElapsed);
- Slog.d(TAG, "Delta until alarm: " + (alarm.whenElapsed - nowElapsed));
- Slog.d(TAG, "Applied fuzz: " + fuzz);
- Slog.d(TAG, "Final delta: " + delta);
- Slog.d(TAG, "Final when: " + alarm.whenElapsed);
- }
- alarm.when = alarm.maxWhenElapsed = alarm.whenElapsed;
+ delta = mRandom.nextInt(fuzz);
+ } else {
+ delta = 0;
}
+ alarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, triggerBeforeFuzz - delta);
return true;
}
/**
- * Adjusts the alarm delivery time based on the current app standby bucket.
+ * Adjusts the alarm's policy time for app_standby.
*
- * @param alarm The alarm to adjust
- * @return true if the alarm delivery time was updated.
+ * @param alarm The alarm to update.
+ * @return {@code true} if the actual delivery time of the given alarm was updated due to
+ * adjustments made in this call.
*/
private boolean adjustDeliveryTimeBasedOnBucketLocked(Alarm alarm) {
- if (isExemptFromAppStandby(alarm)) {
- return false;
+ final long nowElapsed = mInjector.getElapsedRealtime();
+ if (isExemptFromAppStandby(alarm) || mAppStandbyParole) {
+ return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed);
}
- if (mAppStandbyParole) {
- if (alarm.whenElapsed > alarm.expectedWhenElapsed) {
- // We did defer this alarm earlier, restore original requirements
- alarm.whenElapsed = alarm.expectedWhenElapsed;
- alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
- return true;
- }
- return false;
- }
- final long oldWhenElapsed = alarm.whenElapsed;
- final long oldMaxWhenElapsed = alarm.maxWhenElapsed;
final String sourcePackage = alarm.sourcePackage;
final int sourceUserId = UserHandle.getUserId(alarm.creatorUid);
final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket(
- sourcePackage, sourceUserId, mInjector.getElapsedRealtime());
+ sourcePackage, sourceUserId, nowElapsed);
- // Quota deferring implementation:
- boolean deferred = false;
final int wakeupsInWindow = mAppWakeupHistory.getTotalWakeupsInWindow(sourcePackage,
sourceUserId);
if (standbyBucket == UsageStatsManager.STANDBY_BUCKET_RESTRICTED) {
@@ -1635,14 +1625,9 @@
if (wakeupsInWindow > 0) {
final long lastWakeupTime = mAppWakeupHistory.getNthLastWakeupForPackage(
sourcePackage, sourceUserId, mConstants.APP_STANDBY_RESTRICTED_QUOTA);
- if (mInjector.getElapsedRealtime() - lastWakeupTime
- < mConstants.APP_STANDBY_RESTRICTED_WINDOW) {
- final long minElapsed =
- lastWakeupTime + mConstants.APP_STANDBY_RESTRICTED_WINDOW;
- if (alarm.expectedWhenElapsed < minElapsed) {
- alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
- deferred = true;
- }
+ if ((nowElapsed - lastWakeupTime) < mConstants.APP_STANDBY_RESTRICTED_WINDOW) {
+ return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX,
+ lastWakeupTime + mConstants.APP_STANDBY_RESTRICTED_WINDOW);
}
}
} else {
@@ -1651,7 +1636,7 @@
final long minElapsed;
if (quotaForBucket <= 0) {
// Just keep deferring for a day till the quota changes
- minElapsed = mInjector.getElapsedRealtime() + MILLIS_IN_DAY;
+ minElapsed = nowElapsed + MILLIS_IN_DAY;
} else {
// Suppose the quota for window was q, and the qth last delivery time for this
// package was t(q) then the next delivery must be after t(q) + <window_size>
@@ -1659,19 +1644,11 @@
sourcePackage, sourceUserId, quotaForBucket);
minElapsed = t + mConstants.APP_STANDBY_WINDOW;
}
- if (alarm.expectedWhenElapsed < minElapsed) {
- alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
- deferred = true;
- }
+ return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, minElapsed);
}
}
- if (!deferred) {
- // Restore original requirements in case they were changed earlier.
- alarm.whenElapsed = alarm.expectedWhenElapsed;
- alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
- }
-
- return (oldWhenElapsed != alarm.whenElapsed || oldMaxWhenElapsed != alarm.maxWhenElapsed);
+ // wakeupsInWindow are less than the permitted quota, hence no deferring is needed.
+ return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed);
}
private static boolean isAllowedWhileIdle(Alarm a) {
@@ -1691,7 +1668,7 @@
ent.tag = a.operation.getTag("");
ent.op = "SET";
ent.elapsedRealtime = mInjector.getElapsedRealtime();
- ent.argRealtime = a.whenElapsed;
+ ent.argRealtime = a.getWhenElapsed();
mAllowWhileIdleDispatches.add(ent);
if (mPendingIdleUntil == null) {
IdleDispatchEntry ent2 = new IdleDispatchEntry();
@@ -1704,6 +1681,7 @@
if ((mPendingIdleUntil != a) && (mPendingIdleUntil != null)) {
Slog.wtfStack(TAG, "setImplLocked: idle until changed from " + mPendingIdleUntil
+ " to " + a);
+ mAlarmStore.remove(mPendingIdleUntil::equals);
}
mPendingIdleUntil = a;
final ArrayList<Alarm> notAllowedWhileIdleAlarms = mAlarmStore.remove(
@@ -1718,18 +1696,16 @@
}
}
if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
- if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) {
+ if (mNextWakeFromIdle == null || mNextWakeFromIdle.getWhenElapsed()
+ > a.getWhenElapsed()) {
mNextWakeFromIdle = a;
// If this wake from idle is earlier than whatever was previously scheduled,
- // and we are currently idling, then we need to rebatch alarms in case the idle
- // until time needs to be updated.
+ // and we are currently idling, then the idle-until time needs to be updated.
if (mPendingIdleUntil != null) {
- final long nowElapsed = mInjector.getElapsedRealtime();
- mAlarmStore.recalculateAlarmDeliveries(alarm -> {
+ mAlarmStore.updateAlarmDeliveries(alarm -> {
if (alarm != mPendingIdleUntil) {
return false;
}
- restoreAlarmLocked(alarm, nowElapsed);
return adjustIdleUntilTime(alarm);
});
}
@@ -2563,7 +2539,7 @@
long getNextWakeFromIdleTimeImpl() {
synchronized (mLock) {
- return mNextWakeFromIdle != null ? mNextWakeFromIdle.whenElapsed : Long.MAX_VALUE;
+ return mNextWakeFromIdle != null ? mNextWakeFromIdle.getWhenElapsed() : Long.MAX_VALUE;
}
}
@@ -2784,12 +2760,11 @@
restorePending = true;
}
if (mNextWakeFromIdle != null && mNextWakeFromIdle.matches(operation, directReceiver)) {
- mNextWakeFromIdle = null;
- mAlarmStore.recalculateAlarmDeliveries(alarm -> {
+ mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
+ mAlarmStore.updateAlarmDeliveries(alarm -> {
if (alarm != mPendingIdleUntil) {
return false;
}
- restoreAlarmLocked(alarm, mInjector.getElapsedRealtime());
return adjustIdleUntilTime(alarm);
});
}
@@ -2834,15 +2809,14 @@
mPendingBackgroundAlarms.removeAt(i);
}
}
- // If we're currently keying off of this app's alarms for doze transitions,
- // make sure to reset to other triggers.
+ // If we're currently using this app's alarms to come out of doze,
+ // make sure to reset to any remaining WAKE_FROM_IDLE alarms.
if (mNextWakeFromIdle != null && mNextWakeFromIdle.uid == uid) {
- mNextWakeFromIdle = null;
- mAlarmStore.recalculateAlarmDeliveries(alarm -> {
+ mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
+ mAlarmStore.updateAlarmDeliveries(alarm -> {
if (alarm != mPendingIdleUntil) {
return false;
}
- restoreAlarmLocked(alarm, mInjector.getElapsedRealtime());
return adjustIdleUntilTime(alarm);
});
}
@@ -2906,15 +2880,14 @@
mPendingBackgroundAlarms.removeAt(i);
}
}
- // If we're currently keying off of this app's alarms for doze transitions,
- // make sure to reset to other triggers.
+ // If we're currently using this app's alarms to come out of doze,
+ // make sure to reset to any remaining WAKE_FROM_IDLE alarms.
if (removedNextWakeFromIdle.value) {
- mNextWakeFromIdle = null;
- mAlarmStore.recalculateAlarmDeliveries(alarm -> {
+ mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
+ mAlarmStore.updateAlarmDeliveries(alarm -> {
if (alarm != mPendingIdleUntil) {
return false;
}
- restoreAlarmLocked(alarm, mInjector.getElapsedRealtime());
return adjustIdleUntilTime(alarm);
});
}
@@ -3071,20 +3044,6 @@
}
}
- private static final String labelForType(int type) {
- switch (type) {
- case RTC:
- return "RTC";
- case RTC_WAKEUP:
- return "RTC_WAKEUP";
- case ELAPSED_REALTIME:
- return "ELAPSED";
- case ELAPSED_REALTIME_WAKEUP:
- return "ELAPSED_WAKEUP";
- }
- return "--unknown--";
- }
-
private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
String prefix, long nowELAPSED, SimpleDateFormat sdf) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, prefix, prefix);
@@ -3095,7 +3054,7 @@
long nowELAPSED, SimpleDateFormat sdf) {
for (int i = list.size() - 1; i >= 0; i--) {
final Alarm a = list.get(i);
- final String label = labelForType(a.type);
+ final String label = Alarm.typeToString(a.type);
ipw.print(label);
ipw.print(" #");
ipw.print(i);
@@ -3125,6 +3084,9 @@
}
final String sourcePackage = alarm.sourcePackage;
final int sourceUid = alarm.creatorUid;
+ if (UserHandle.isCore(sourceUid)) {
+ return false;
+ }
return (mAppStateTracker != null) &&
mAppStateTracker.areAlarmsRestricted(sourceUid, sourcePackage,
exemptOnBatterySaver);
@@ -3169,11 +3131,7 @@
// Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE
// alarm went off for this app. Reschedule the alarm to be in the
// correct time period.
- alarm.expectedWhenElapsed = alarm.whenElapsed = minTime;
- if (alarm.maxWhenElapsed < minTime) {
- alarm.maxWhenElapsed = minTime;
- }
- alarm.expectedMaxWhenElapsed = alarm.maxWhenElapsed;
+ alarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, minTime);
if (RECORD_DEVICE_IDLE_ALARMS) {
IdleDispatchEntry ent = new IdleDispatchEntry();
ent.uid = alarm.uid;
@@ -3213,12 +3171,11 @@
restorePendingWhileIdleAlarmsLocked();
}
if (mNextWakeFromIdle == alarm) {
- mNextWakeFromIdle = null;
- mAlarmStore.recalculateAlarmDeliveries(a -> {
+ mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm();
+ mAlarmStore.updateAlarmDeliveries(a -> {
if (a != mPendingIdleUntil) {
return false;
}
- restoreAlarmLocked(a, nowELAPSED);
return adjustIdleUntilTime(a);
});
}
@@ -3228,14 +3185,17 @@
if (alarm.repeatInterval > 0) {
// this adjustment will be zero if we're late by
// less than one full repeat interval
- alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
+ alarm.count += (nowELAPSED - alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX))
+ / alarm.repeatInterval;
// Also schedule its next recurrence
final long delta = alarm.count * alarm.repeatInterval;
- final long nextElapsed = alarm.expectedWhenElapsed + delta;
- setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength,
- maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
- alarm.repeatInterval, alarm.operation, null, null, alarm.flags,
- alarm.workSource, alarm.alarmClock, alarm.uid, alarm.packageName);
+ final long nextElapsed = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX) + delta;
+ final long nextMaxElapsed = maxTriggerTime(nowELAPSED, nextElapsed,
+ alarm.repeatInterval);
+ setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed,
+ nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null,
+ null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid,
+ alarm.packageName);
}
if (alarm.wakeup) {
@@ -3277,7 +3237,7 @@
}
}
- static int fuzzForDuration(long duration) {
+ int fuzzForDuration(long duration) {
if (duration < 15 * 60 * 1000) {
// If the duration until the time is less than 15 minutes, the maximum fuzz
// is the duration.
@@ -3480,7 +3440,7 @@
FrameworkStatsLog.write(FrameworkStatsLog.WALL_CLOCK_TIME_SHIFTED, nowRTC);
removeImpl(null, mTimeTickTrigger);
removeImpl(mDateChangeSender, null);
- reevaluateRtcAlarms(nowELAPSED);
+ reevaluateRtcAlarms();
mClockReceiver.scheduleTimeTickEvent();
mClockReceiver.scheduleDateChangedEvent();
synchronized (mLock) {
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
index 9fdbb8b..7a846b9 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java
@@ -48,6 +48,15 @@
ArrayList<Alarm> remove(Predicate<Alarm> whichAlarms);
/**
+ * Gets the earliest alarm with the flag {@link android.app.AlarmManager#FLAG_WAKE_FROM_IDLE}
+ * based on {@link Alarm#getWhenElapsed()}.
+ *
+ * @return An alarm object matching the description above or {@code null} if no such alarm was
+ * found.
+ */
+ Alarm getNextWakeFromIdleAlarm();
+
+ /**
* Returns the total number of alarms in this store.
*/
int size();
@@ -71,7 +80,7 @@
/**
* Removes all alarms that are pending delivery at the given time.
*
- * @param nowElapsed The time at which delivery eligibility is evaluated.
+ * @param nowElapsed The time at which delivery eligibility is evaluated.
* @return The list of alarms pending at the given time.
*/
ArrayList<Alarm> removePendingAlarms(long nowElapsed);
@@ -82,7 +91,7 @@
*
* @return {@code true} if any of the alarm deliveries changed due to this call.
*/
- boolean recalculateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator);
+ boolean updateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator);
/**
* Returns all the alarms in the form of a list.
@@ -97,6 +106,7 @@
* Primary useful for debugging. Can be called from the
* {@link android.os.Binder#dump(FileDescriptor PrintWriter, String[]) dump} method of the
* caller.
+ *
* @param ipw The {@link IndentingPrintWriter} to write to.
* @param nowElapsed the time when the dump is requested in the
* {@link SystemClock#elapsedRealtime()
@@ -112,7 +122,7 @@
/**
* A functional interface used to update the alarm. Used to describe the update in
- * {@link #recalculateAlarmDeliveries(AlarmDeliveryCalculator)}
+ * {@link #updateAlarmDeliveries(AlarmDeliveryCalculator)}
*/
@FunctionalInterface
interface AlarmDeliveryCalculator {
@@ -125,3 +135,4 @@
boolean updateAlarmDelivery(Alarm a);
}
}
+
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
index 91c0c05..cbfe80b 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
@@ -66,8 +66,8 @@
};
private static final Comparator<Alarm> sIncreasingTimeOrder = (a1, a2) -> {
- long when1 = a1.whenElapsed;
- long when2 = a2.whenElapsed;
+ long when1 = a1.getWhenElapsed();
+ long when2 = a2.getWhenElapsed();
if (when1 > when2) {
return 1;
}
@@ -99,11 +99,28 @@
}
if (!removed.isEmpty()) {
mSize -= removed.size();
+ // Not needed if only whole batches were removed, but keeping existing behavior.
rebatchAllAlarms();
}
return removed;
}
+ @Override
+ public Alarm getNextWakeFromIdleAlarm() {
+ for (final Batch batch : mAlarmBatches) {
+ if ((batch.mFlags & AlarmManager.FLAG_WAKE_FROM_IDLE) == 0) {
+ continue;
+ }
+ for (int i = 0; i < batch.size(); i++) {
+ final Alarm a = batch.get(i);
+ if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
+ return a;
+ }
+ }
+ }
+ return null;
+ }
+
private void rebatchAllAlarms() {
final long start = mStatLogger.getTime();
final ArrayList<Batch> oldBatches = (ArrayList<Batch>) mAlarmBatches.clone();
@@ -157,7 +174,7 @@
}
@Override
- public boolean recalculateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator) {
+ public boolean updateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator) {
boolean changed = false;
for (final Batch b : mAlarmBatches) {
for (int i = 0; i < b.size(); i++) {
@@ -204,7 +221,7 @@
private void insertAndBatchAlarm(Alarm alarm) {
final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1
- : attemptCoalesce(alarm.whenElapsed, alarm.maxWhenElapsed);
+ : attemptCoalesce(alarm.getWhenElapsed(), alarm.getMaxWhenElapsed());
if (whichBatch < 0) {
addBatch(mAlarmBatches, new Batch(alarm));
@@ -247,8 +264,8 @@
final ArrayList<Alarm> mAlarms = new ArrayList<>();
Batch(Alarm seed) {
- mStart = seed.whenElapsed;
- mEnd = clampPositive(seed.maxWhenElapsed);
+ mStart = seed.getWhenElapsed();
+ mEnd = clampPositive(seed.getMaxWhenElapsed());
mFlags = seed.flags;
mAlarms.add(seed);
}
@@ -276,12 +293,12 @@
if (DEBUG_BATCH) {
Slog.v(TAG, "Adding " + alarm + " to " + this);
}
- if (alarm.whenElapsed > mStart) {
- mStart = alarm.whenElapsed;
+ if (alarm.getWhenElapsed() > mStart) {
+ mStart = alarm.getWhenElapsed();
newStart = true;
}
- if (alarm.maxWhenElapsed < mEnd) {
- mEnd = alarm.maxWhenElapsed;
+ if (alarm.getMaxWhenElapsed() < mEnd) {
+ mEnd = alarm.getMaxWhenElapsed();
}
mFlags |= alarm.flags;
@@ -309,11 +326,11 @@
Slog.wtf(TAG, "Removed TIME_TICK alarm");
}
} else {
- if (alarm.whenElapsed > newStart) {
- newStart = alarm.whenElapsed;
+ if (alarm.getWhenElapsed() > newStart) {
+ newStart = alarm.getWhenElapsed();
}
- if (alarm.maxWhenElapsed < newEnd) {
- newEnd = alarm.maxWhenElapsed;
+ if (alarm.getMaxWhenElapsed() < newEnd) {
+ newEnd = alarm.getMaxWhenElapsed();
}
newFlags |= alarm.flags;
i++;
diff --git a/api/Android.bp b/api/Android.bp
index f0218b8..388bb68 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -48,6 +48,7 @@
name: "frameworks-base-api-current-merged.txt",
srcs: [
":conscrypt.module.public.api{.public.api.txt}",
+ ":framework-appsearch{.public.api.txt}",
":framework-graphics{.public.api.txt}",
":framework-media{.public.api.txt}",
":framework-mediaprovider{.public.api.txt}",
@@ -61,12 +62,19 @@
out: ["current.txt"],
tools: ["metalava"],
cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/public/api",
+ dest: "android.txt",
+ },
}
genrule {
name: "frameworks-base-api-removed-merged.txt",
srcs: [
":conscrypt.module.public.api{.public.removed-api.txt}",
+ ":framework-appsearch{.public.removed-api.txt}",
+ ":framework-graphics{.public.removed-api.txt}",
":framework-media{.public.removed-api.txt}",
":framework-mediaprovider{.public.removed-api.txt}",
":framework-permission{.public.removed-api.txt}",
@@ -84,6 +92,7 @@
genrule {
name: "frameworks-base-api-system-current-merged.txt",
srcs: [
+ ":framework-appsearch{.system.api.txt}",
":framework-graphics{.system.api.txt}",
":framework-media{.system.api.txt}",
":framework-mediaprovider{.system.api.txt}",
@@ -97,11 +106,18 @@
out: ["system-current.txt"],
tools: ["metalava"],
cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/system/api",
+ dest: "android.txt",
+ },
}
genrule {
name: "frameworks-base-api-system-removed-merged.txt",
srcs: [
+ ":framework-appsearch{.system.removed-api.txt}",
+ ":framework-graphics{.system.removed-api.txt}",
":framework-media{.system.removed-api.txt}",
":framework-mediaprovider{.system.removed-api.txt}",
":framework-permission{.system.removed-api.txt}",
@@ -119,6 +135,7 @@
genrule {
name: "frameworks-base-api-module-lib-current-merged.txt",
srcs: [
+ ":framework-appsearch{.module-lib.api.txt}",
":framework-graphics{.module-lib.api.txt}",
":framework-media{.module-lib.api.txt}",
":framework-mediaprovider{.module-lib.api.txt}",
@@ -132,11 +149,18 @@
out: ["module-lib-current.txt"],
tools: ["metalava"],
cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/module-lib/api",
+ dest: "android.txt",
+ },
}
genrule {
name: "frameworks-base-api-module-lib-removed-merged.txt",
srcs: [
+ ":framework-appsearch{.module-lib.removed-api.txt}",
+ ":framework-graphics{.module-lib.removed-api.txt}",
":framework-media{.module-lib.removed-api.txt}",
":framework-mediaprovider{.module-lib.removed-api.txt}",
":framework-permission{.module-lib.removed-api.txt}",
diff --git a/api/current.txt b/api/current.txt
index 83df1d9..6b42af8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1613,6 +1613,7 @@
field public static final int windowHideAnimation = 16842935; // 0x10100b7
field public static final int windowIsFloating = 16842839; // 0x1010057
field public static final int windowIsTranslucent = 16842840; // 0x1010058
+ field public static final int windowLayoutAffinity = 16844313; // 0x1010619
field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586
field public static final int windowLightNavigationBar = 16844140; // 0x101056c
field public static final int windowLightStatusBar = 16844000; // 0x10104e0
@@ -2889,6 +2890,7 @@
field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28
field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
+ field public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43; // 0x2b
field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c
@@ -2897,11 +2899,13 @@
field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29
field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
+ field public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44; // 0x2c
field public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; // 0x1e
field public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; // 0x1f
field public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32; // 0x20
field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d
field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18
+ field public static final int GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD = 45; // 0x2d
field public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; // 0x26
field public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42; // 0x2a
field public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; // 0x25
@@ -4898,7 +4902,7 @@
@Deprecated public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
ctor @Deprecated public Fragment();
method @Deprecated public void dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]);
- method @Deprecated public final boolean equals(Object);
+ method @Deprecated public final boolean equals(@Nullable Object);
method @Deprecated public final android.app.Activity getActivity();
method @Deprecated public boolean getAllowEnterTransitionOverlap();
method @Deprecated public boolean getAllowReturnTransitionOverlap();
@@ -13063,38 +13067,38 @@
public interface Cursor extends java.io.Closeable {
method public void close();
- method public void copyStringToBuffer(int, android.database.CharArrayBuffer);
+ method public void copyStringToBuffer(@IntRange(from=0) int, android.database.CharArrayBuffer);
method @Deprecated public void deactivate();
- method public byte[] getBlob(int);
- method public int getColumnCount();
- method public int getColumnIndex(String);
- method public int getColumnIndexOrThrow(String) throws java.lang.IllegalArgumentException;
- method public String getColumnName(int);
+ method public byte[] getBlob(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getColumnCount();
+ method @IntRange(from=0xffffffff) public int getColumnIndex(String);
+ method @IntRange(from=0) public int getColumnIndexOrThrow(String) throws java.lang.IllegalArgumentException;
+ method public String getColumnName(@IntRange(from=0) int);
method public String[] getColumnNames();
- method public int getCount();
- method public double getDouble(int);
+ method @IntRange(from=0) public int getCount();
+ method public double getDouble(@IntRange(from=0) int);
method public android.os.Bundle getExtras();
- method public float getFloat(int);
- method public int getInt(int);
- method public long getLong(int);
+ method public float getFloat(@IntRange(from=0) int);
+ method public int getInt(@IntRange(from=0) int);
+ method public long getLong(@IntRange(from=0) int);
method public android.net.Uri getNotificationUri();
method @Nullable public default java.util.List<android.net.Uri> getNotificationUris();
- method public int getPosition();
- method public short getShort(int);
- method public String getString(int);
- method public int getType(int);
+ method @IntRange(from=0xffffffff) public int getPosition();
+ method public short getShort(@IntRange(from=0) int);
+ method public String getString(@IntRange(from=0) int);
+ method public int getType(@IntRange(from=0) int);
method public boolean getWantsAllOnMoveCalls();
method public boolean isAfterLast();
method public boolean isBeforeFirst();
method public boolean isClosed();
method public boolean isFirst();
method public boolean isLast();
- method public boolean isNull(int);
+ method public boolean isNull(@IntRange(from=0) int);
method public boolean move(int);
method public boolean moveToFirst();
method public boolean moveToLast();
method public boolean moveToNext();
- method public boolean moveToPosition(int);
+ method public boolean moveToPosition(@IntRange(from=0xffffffff) int);
method public boolean moveToPrevious();
method public void registerContentObserver(android.database.ContentObserver);
method public void registerDataSetObserver(android.database.DataSetObserver);
@@ -13136,33 +13140,33 @@
ctor @Deprecated public CursorWindow(boolean);
method public boolean allocRow();
method public void clear();
- method public void copyStringToBuffer(int, int, android.database.CharArrayBuffer);
+ method public void copyStringToBuffer(@IntRange(from=0) int, @IntRange(from=0) int, android.database.CharArrayBuffer);
method public int describeContents();
method public void freeLastRow();
- method public byte[] getBlob(int, int);
- method public double getDouble(int, int);
- method public float getFloat(int, int);
- method public int getInt(int, int);
- method public long getLong(int, int);
- method public int getNumRows();
- method public short getShort(int, int);
- method public int getStartPosition();
- method public String getString(int, int);
- method public int getType(int, int);
- method @Deprecated public boolean isBlob(int, int);
- method @Deprecated public boolean isFloat(int, int);
- method @Deprecated public boolean isLong(int, int);
- method @Deprecated public boolean isNull(int, int);
- method @Deprecated public boolean isString(int, int);
+ method public byte[] getBlob(@IntRange(from=0) int, @IntRange(from=0) int);
+ method public double getDouble(@IntRange(from=0) int, @IntRange(from=0) int);
+ method public float getFloat(@IntRange(from=0) int, @IntRange(from=0) int);
+ method public int getInt(@IntRange(from=0) int, @IntRange(from=0) int);
+ method public long getLong(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @IntRange(from=0) public int getNumRows();
+ method public short getShort(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @IntRange(from=0) public int getStartPosition();
+ method public String getString(@IntRange(from=0) int, @IntRange(from=0) int);
+ method public int getType(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @Deprecated public boolean isBlob(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @Deprecated public boolean isFloat(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @Deprecated public boolean isLong(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @Deprecated public boolean isNull(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @Deprecated public boolean isString(@IntRange(from=0) int, @IntRange(from=0) int);
method public static android.database.CursorWindow newFromParcel(android.os.Parcel);
method protected void onAllReferencesReleased();
- method public boolean putBlob(byte[], int, int);
- method public boolean putDouble(double, int, int);
- method public boolean putLong(long, int, int);
- method public boolean putNull(int, int);
- method public boolean putString(String, int, int);
- method public boolean setNumColumns(int);
- method public void setStartPosition(int);
+ method public boolean putBlob(byte[], @IntRange(from=0) int, @IntRange(from=0) int);
+ method public boolean putDouble(double, @IntRange(from=0) int, @IntRange(from=0) int);
+ method public boolean putLong(long, @IntRange(from=0) int, @IntRange(from=0) int);
+ method public boolean putNull(@IntRange(from=0) int, @IntRange(from=0) int);
+ method public boolean putString(String, @IntRange(from=0) int, @IntRange(from=0) int);
+ method public boolean setNumColumns(@IntRange(from=0) int);
+ method public void setStartPosition(@IntRange(from=0) int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.database.CursorWindow> CREATOR;
}
@@ -16522,23 +16526,6 @@
package android.graphics.text {
- public class GlyphStyle {
- ctor public GlyphStyle(@ColorInt int, @FloatRange(from=0) float, @FloatRange(from=0) float, @FloatRange(from=0) float, int);
- ctor public GlyphStyle(@NonNull android.graphics.Paint);
- method public void applyToPaint(@NonNull android.graphics.Paint);
- method @ColorInt public int getColor();
- method public int getFlags();
- method @FloatRange(from=0) public float getFontSize();
- method @FloatRange(from=0) public float getScaleX();
- method @FloatRange(from=0) public float getSkewX();
- method public void setColor(@ColorInt int);
- method public void setFlags(int);
- method public void setFontSize(@FloatRange(from=0) float);
- method public void setFromPaint(@NonNull android.graphics.Paint);
- method public void setScaleX(@FloatRange(from=0) float);
- method public void setSkewX(@FloatRange(from=0) float);
- }
-
public class LineBreaker {
method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int);
field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -16600,20 +16587,19 @@
}
public final class PositionedGlyphs {
+ method public float getAdvance();
method public float getAscent();
method public float getDescent();
method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int);
method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int);
- method public float getOriginX();
- method public float getOriginY();
- method public float getPositionX(@IntRange(from=0) int);
- method public float getPositionY(@IntRange(from=0) int);
- method @NonNull public android.graphics.text.GlyphStyle getStyle();
- method public float getTotalAdvance();
+ method public float getGlyphX(@IntRange(from=0) int);
+ method public float getGlyphY(@IntRange(from=0) int);
+ method public float getOffsetX();
+ method public float getOffsetY();
method @IntRange(from=0) public int glyphCount();
}
- public class TextShaper {
+ public class TextRunShaper {
method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull char[], int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull CharSequence, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
}
@@ -24089,9 +24075,13 @@
method @IntRange(from=1, to=java.lang.Integer.MAX_VALUE) public int getMaxUpdates();
method @FloatRange(from=0, to=java.lang.Float.MAX_VALUE) public float getMinUpdateDistanceMeters();
method @IntRange(from=0) public long getMinUpdateIntervalMillis();
+ method public int getQuality();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR;
field public static final long PASSIVE_INTERVAL = 9223372036854775807L; // 0x7fffffffffffffffL
+ field public static final int QUALITY_BALANCED_POWER_ACCURACY = 102; // 0x66
+ field public static final int QUALITY_HIGH_ACCURACY = 100; // 0x64
+ field public static final int QUALITY_LOW_POWER = 104; // 0x68
}
public static final class LocationRequest.Builder {
@@ -24104,6 +24094,7 @@
method @NonNull public android.location.LocationRequest.Builder setMaxUpdates(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int);
method @NonNull public android.location.LocationRequest.Builder setMinUpdateDistanceMeters(@FloatRange(from=0, to=java.lang.Float.MAX_VALUE) float);
method @NonNull public android.location.LocationRequest.Builder setMinUpdateIntervalMillis(@IntRange(from=0) long);
+ method @NonNull public android.location.LocationRequest.Builder setQuality(int);
}
public interface OnNmeaMessageListener {
@@ -25456,6 +25447,7 @@
public static final class MediaCodec.CryptoInfo {
ctor public MediaCodec.CryptoInfo();
+ method @NonNull public android.media.MediaCodec.CryptoInfo.Pattern getPattern();
method public void set(int, @NonNull int[], @NonNull int[], @NonNull byte[], @NonNull byte[], int);
method public void setPattern(android.media.MediaCodec.CryptoInfo.Pattern);
field public byte[] iv;
@@ -26408,7 +26400,7 @@
method public boolean containsKey(String);
method public int describeContents();
method public android.graphics.Bitmap getBitmap(String);
- method @IntRange(from=0) public int getBitmapDimensionLimit();
+ method @IntRange(from=1) public int getBitmapDimensionLimit();
method @NonNull public android.media.MediaDescription getDescription();
method public long getLong(String);
method public android.media.Rating getRating(String);
@@ -26458,7 +26450,7 @@
method public android.media.MediaMetadata.Builder putRating(String, android.media.Rating);
method public android.media.MediaMetadata.Builder putString(String, String);
method public android.media.MediaMetadata.Builder putText(String, CharSequence);
- method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(int);
+ method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(@IntRange(from=1) int);
}
@Deprecated public abstract class MediaMetadataEditor {
@@ -30253,9 +30245,12 @@
method public int describeContents();
method @NonNull public byte[] getKey();
method @NonNull public String getName();
+ method @NonNull public static java.util.Set<java.lang.String> getSupportedAlgorithms();
method public int getTruncationLengthBits();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final String AUTH_AES_XCBC = "xcbc(aes)";
field public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
+ field public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)";
field public static final String AUTH_HMAC_MD5 = "hmac(md5)";
field public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
field public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
@@ -30263,6 +30258,7 @@
field public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
field @NonNull public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
field public static final String CRYPT_AES_CBC = "cbc(aes)";
+ field public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))";
}
public final class IpSecManager {
@@ -31871,7 +31867,7 @@
method public void onPublishStarted(@NonNull android.net.wifi.aware.PublishDiscoverySession);
method public void onServiceDiscovered(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>);
method public void onServiceDiscoveredWithinRange(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>, int);
- method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle);
+ method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle, int);
method public void onSessionConfigFailed();
method public void onSessionConfigUpdated();
method public void onSessionTerminated();
@@ -31951,6 +31947,8 @@
field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0
field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1
+ field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1; // 0x1
+ field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0; // 0x0
}
public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo {
@@ -32090,13 +32088,13 @@
method public String getFriendlyName();
method @Nullable public long[] getMatchAllOis();
method @Nullable public long[] getMatchAnyOis();
- method @Nullable public String[] getOtherHomePartners();
+ method @NonNull public java.util.Collection<java.lang.String> getOtherHomePartnersList();
method public long[] getRoamingConsortiumOis();
method public void setFqdn(String);
method public void setFriendlyName(String);
method public void setMatchAllOis(@Nullable long[]);
method public void setMatchAnyOis(@Nullable long[]);
- method public void setOtherHomePartners(@Nullable String[]);
+ method public void setOtherHomePartnersList(@NonNull java.util.Collection<java.lang.String>);
method public void setRoamingConsortiumOis(long[]);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR;
@@ -43295,8 +43293,16 @@
method @NonNull public java.util.Map<android.view.autofill.AutofillId,android.service.autofill.FieldClassification> getFieldsClassification();
method @NonNull public java.util.Set<java.lang.String> getIgnoredDatasetIds();
method @NonNull public java.util.Map<android.view.autofill.AutofillId,java.util.Set<java.lang.String>> getManuallyEnteredField();
+ method public int getNoSaveReason();
method @NonNull public java.util.Set<java.lang.String> getSelectedDatasetIds();
method public int getType();
+ field public static final int NO_SAVE_REASON_DATASET_MATCH = 6; // 0x6
+ field public static final int NO_SAVE_REASON_FIELD_VALIDATION_FAILED = 5; // 0x5
+ field public static final int NO_SAVE_REASON_HAS_EMPTY_REQUIRED = 3; // 0x3
+ field public static final int NO_SAVE_REASON_NONE = 0; // 0x0
+ field public static final int NO_SAVE_REASON_NO_SAVE_INFO = 1; // 0x1
+ field public static final int NO_SAVE_REASON_NO_VALUE_CHANGED = 4; // 0x4
+ field public static final int NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG = 2; // 0x2
field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
field public static final int TYPE_CONTEXT_COMMITTED = 4; // 0x4
field public static final int TYPE_DATASETS_SHOWN = 5; // 0x5
@@ -44100,11 +44106,13 @@
method public long getLastAudiblyAlertedMillis();
method public String getOverrideGroupKey();
method public int getRank();
+ method @Nullable public android.content.pm.ShortcutInfo getShortcutInfo();
method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions();
method @NonNull public java.util.List<java.lang.CharSequence> getSmartReplies();
method public int getSuppressedVisualEffects();
method public int getUserSentiment();
method public boolean isAmbient();
+ method public boolean isConversation();
method public boolean isSuspended();
method public boolean matchesInterruptionFilter();
field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
@@ -48440,6 +48448,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getDeviceSoftwareVersion();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getEmergencyNumberList();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getEmergencyNumberList(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<java.lang.String> getEquivalentHomePlmns();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String[] getForbiddenPlmns();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getGroupIdLevel1();
method public String getIccAuthentication(int, int, String);
@@ -48464,7 +48473,7 @@
method @Deprecated public int getPhoneCount();
method public int getPhoneType();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
- method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
+ method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
method @Nullable public android.telephony.SignalStrength getSignalStrength();
method public int getSimCarrierId();
method @Nullable public CharSequence getSimCarrierIdName();
@@ -48487,7 +48496,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailAlphaTag();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getVoiceNetworkType();
- method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
+ method @Nullable public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
method public boolean hasCarrierPrivileges();
method public boolean hasIccCard();
method @Deprecated public boolean iccCloseLogicalChannel(int);
@@ -50052,10 +50061,6 @@
method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean);
}
- public class StyledTextShaper {
- method @NonNull public static java.util.List<android.graphics.text.PositionedGlyphs> shapeText(@NonNull CharSequence, int, int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint);
- }
-
public interface TextDirectionHeuristic {
method public boolean isRtl(char[], int, int);
method public boolean isRtl(CharSequence, int, int);
@@ -50085,6 +50090,14 @@
field @Px public float underlineThickness;
}
+ public class TextShaper {
+ method public static void shapeText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint, @NonNull android.text.TextShaper.GlyphsConsumer);
+ }
+
+ public static interface TextShaper.GlyphsConsumer {
+ method public void accept(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.text.PositionedGlyphs, @NonNull android.text.TextPaint);
+ }
+
public class TextUtils {
method @Deprecated public static CharSequence commaEllipsize(CharSequence, android.text.TextPaint, float, String, String);
method public static CharSequence concat(java.lang.CharSequence...);
@@ -53721,11 +53734,12 @@
method @Nullable public android.net.Uri getLinkUri();
method public int getSource();
field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
- field public static final int SOURCE_AUTOFILL = 3; // 0x3
- field public static final int SOURCE_CLIPBOARD = 0; // 0x0
- field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2
- field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
- field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4
+ field public static final int SOURCE_APP = 0; // 0x0
+ field public static final int SOURCE_AUTOFILL = 4; // 0x4
+ field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+ field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+ field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+ field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5
}
public static final class OnReceiveContentCallback.Payload.Builder {
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 2d24d5b..e825b62 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -1,10 +1,20 @@
// Signature format: 2.0
package android.app {
+ public class ActivityManager {
+ method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
+ method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
+ }
+
public class AppOpsManager {
field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
}
+ public abstract class HomeVisibilityListener {
+ ctor public HomeVisibilityListener();
+ method public abstract void onHomeVisibilityChanged(boolean);
+ }
+
public class NotificationManager {
method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED";
@@ -53,6 +63,10 @@
field public static final int FLAG_FROM_KEY = 4096; // 0x1000
}
+ public class MediaMetadataRetriever implements java.lang.AutoCloseable {
+ field public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40; // 0x28
+ }
+
@Deprecated public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable {
ctor @Deprecated public MediaParceledListSlice(@NonNull java.util.List<T>);
method @Deprecated public int describeContents();
@@ -174,6 +188,18 @@
}
+package android.telephony {
+
+ public abstract class CellSignalStrength {
+ method public static int getNumSignalStrengthLevels();
+ }
+
+ public class TelephonyManager {
+ method @NonNull public static int[] getAllNetworkTypes();
+ }
+
+}
+
package android.util {
public class AtomicFile {
diff --git a/api/system-current.txt b/api/system-current.txt
index c698784..25008f4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -122,6 +122,7 @@
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
+ field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS";
field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
@@ -674,8 +675,10 @@
public class NotificationManager {
method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments();
method @Nullable public android.content.ComponentName getAllowedNotificationAssistant();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public java.util.List<android.content.ComponentName> getEnabledNotificationListeners();
method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean);
field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL";
field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL";
field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL";
@@ -1448,20 +1451,20 @@
package android.app.usage {
public final class CacheQuotaHint implements android.os.Parcelable {
- ctor public CacheQuotaHint(android.app.usage.CacheQuotaHint.Builder);
+ ctor public CacheQuotaHint(@NonNull android.app.usage.CacheQuotaHint.Builder);
method public int describeContents();
method public long getQuota();
method public int getUid();
- method public android.app.usage.UsageStats getUsageStats();
- method public String getVolumeUuid();
- method public void writeToParcel(android.os.Parcel, int);
+ method @Nullable public android.app.usage.UsageStats getUsageStats();
+ method @Nullable public String getVolumeUuid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.CacheQuotaHint> CREATOR;
field public static final long QUOTA_NOT_SET = -1L; // 0xffffffffffffffffL
}
public static final class CacheQuotaHint.Builder {
ctor public CacheQuotaHint.Builder();
- ctor public CacheQuotaHint.Builder(android.app.usage.CacheQuotaHint);
+ ctor public CacheQuotaHint.Builder(@NonNull android.app.usage.CacheQuotaHint);
method @NonNull public android.app.usage.CacheQuotaHint build();
method @NonNull public android.app.usage.CacheQuotaHint.Builder setQuota(long);
method @NonNull public android.app.usage.CacheQuotaHint.Builder setUid(int);
@@ -4171,7 +4174,6 @@
method @Deprecated public long getInterval();
method @Deprecated public int getNumUpdates();
method @Deprecated @NonNull public String getProvider();
- method public int getQuality();
method @Deprecated public float getSmallestDisplacement();
method @NonNull public android.os.WorkSource getWorkSource();
method public boolean isHiddenFromAppOps();
@@ -4190,11 +4192,11 @@
method @Deprecated @NonNull public android.location.LocationRequest setQuality(int);
method @Deprecated @NonNull public android.location.LocationRequest setSmallestDisplacement(float);
method @Deprecated public void setWorkSource(@Nullable android.os.WorkSource);
- field public static final int ACCURACY_BLOCK = 102; // 0x66
- field public static final int ACCURACY_CITY = 104; // 0x68
- field public static final int ACCURACY_FINE = 100; // 0x64
- field public static final int POWER_HIGH = 203; // 0xcb
- field public static final int POWER_LOW = 201; // 0xc9
+ field @Deprecated public static final int ACCURACY_BLOCK = 102; // 0x66
+ field @Deprecated public static final int ACCURACY_CITY = 104; // 0x68
+ field @Deprecated public static final int ACCURACY_FINE = 100; // 0x64
+ field @Deprecated public static final int POWER_HIGH = 203; // 0xcb
+ field @Deprecated public static final int POWER_LOW = 201; // 0xc9
field @Deprecated public static final int POWER_NONE = 200; // 0xc8
}
@@ -4202,7 +4204,6 @@
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean);
- method @NonNull public android.location.LocationRequest.Builder setQuality(int);
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
}
@@ -4769,6 +4770,22 @@
field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.DvbDeviceInfo> CREATOR;
}
+ public final class TunedInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getAppTag();
+ method public int getAppType();
+ method @Nullable public android.net.Uri getChannelUri();
+ method @NonNull public String getInputId();
+ method public boolean isForeground();
+ method public boolean isRecordingSession();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int APP_TAG_SELF = 0; // 0x0
+ field public static final int APP_TYPE_NON_SYSTEM = 3; // 0x3
+ field public static final int APP_TYPE_SELF = 1; // 0x1
+ field public static final int APP_TYPE_SYSTEM = 2; // 0x2
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TunedInfo> CREATOR;
+ }
+
public final class TvContentRatingSystemInfo implements android.os.Parcelable {
method public static android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo);
method public int describeContents();
@@ -4884,6 +4901,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating);
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig);
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String);
+ method @NonNull @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS") public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos();
method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList();
method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList();
@@ -4909,6 +4927,10 @@
method public abstract void onStreamConfigChanged(android.media.tv.TvStreamConfig[]);
}
+ public abstract static class TvInputManager.TvInputCallback {
+ method public void onCurrentTunedInfosUpdated(@NonNull java.util.List<android.media.tv.TunedInfo>);
+ }
+
public abstract class TvInputService extends android.app.Service {
method @Nullable public android.media.tv.TvInputInfo onHardwareAdded(android.media.tv.TvInputHardwareInfo);
method @Nullable public String onHardwareRemoved(android.media.tv.TvInputHardwareInfo);
@@ -5036,6 +5058,7 @@
method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
+ method public int linkFrontendToCiCam(int);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
method @Nullable public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
@@ -5049,9 +5072,14 @@
method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
+ method public int unlinkFrontendToCiCam(int);
method public void updateResourcePriority(int, int);
field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
+ field public static final long INVALID_FILTER_ID_64BIT = -1L; // 0xffffffffffffffffL
+ field public static final int INVALID_FRONTEND_SETTING_FREQUENCY = -1; // 0xffffffff
+ field public static final int INVALID_LTS_ID = -1; // 0xffffffff
+ field public static final int INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM = -1; // 0xffffffff
field public static final int INVALID_STREAM_ID = 65535; // 0xffff
field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL
field public static final int INVALID_TS_PID = 65535; // 0xffff
@@ -5071,15 +5099,22 @@
method public void onResourceLost(@NonNull android.media.tv.tuner.Tuner);
}
+ public final class TunerVersionChecker {
+ method public static int getTunerVersion();
+ field public static final int TUNER_VERSION_1_0 = 65536; // 0x10000
+ field public static final int TUNER_VERSION_1_1 = 65537; // 0x10001
+ field public static final int TUNER_VERSION_UNKNOWN = 0; // 0x0
+ }
+
}
package android.media.tv.tuner.dvr {
public class DvrPlayback implements java.lang.AutoCloseable {
- method public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter);
+ method @Deprecated public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter);
method public void close();
method public int configure(@NonNull android.media.tv.tuner.dvr.DvrSettings);
- method public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter);
+ method @Deprecated public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter);
method public int flush();
method public long read(long);
method public long read(@NonNull byte[], long, long);
@@ -5204,6 +5239,7 @@
method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
method public int flush();
method public int getId();
+ method public long getId64Bit();
method public int read(@NonNull byte[], long, long);
method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter);
method public int start();
@@ -5255,16 +5291,19 @@
method @NonNull public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder();
method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress();
method public int getDstPort();
+ method public int getIpFilterContextId();
method @NonNull @Size(min=4, max=16) public byte[] getSrcIpAddress();
method public int getSrcPort();
method public int getType();
method public boolean isPassthrough();
+ field public static final int INVALID_IP_FILTER_CONTEXT_ID = -1; // 0xffffffff
}
public static final class IpFilterConfiguration.Builder {
method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration build();
method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstIpAddress(@NonNull byte[]);
method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstPort(int);
+ method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setIpFilterContextId(int);
method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setPassthrough(boolean);
method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings);
method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSrcIpAddress(@NonNull byte[]);
@@ -5304,6 +5343,8 @@
public class MmtpRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
method public long getDataLength();
+ method public int getMpuSequenceNumber();
+ method public long getPts();
method public int getScHevcIndexMask();
}
@@ -5466,6 +5507,7 @@
public class TsRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
method public long getDataLength();
method public int getPacketId();
+ method public long getPts();
method public int getScIndexMask();
method public int getTsIndexMask();
}
@@ -5481,9 +5523,13 @@
public class AnalogFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @NonNull public static android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder builder();
+ method public int getAftFlag();
method public int getSifStandard();
method public int getSignalType();
method public int getType();
+ field public static final int AFT_FLAG_FALSE = 2; // 0x2
+ field public static final int AFT_FLAG_TRUE = 1; // 0x1
+ field public static final int AFT_FLAG_UNDEFINED = 0; // 0x0
field public static final int SIF_AUTO = 1; // 0x1
field public static final int SIF_BG = 2; // 0x2
field public static final int SIF_BG_A2 = 4; // 0x4
@@ -5516,6 +5562,7 @@
public static class AnalogFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings build();
+ method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setAftFlag(int);
method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSifStandard(int);
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSignalType(int);
@@ -5631,6 +5678,69 @@
method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int);
}
+ public class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
+ method public int getBandwidthCapability();
+ method public int getCodeRateCapability();
+ method public int getGuardIntervalCapability();
+ method public int getModulationCapability();
+ method public int getTimeInterleaveModeCapability();
+ method public int getTransmissionModeCapability();
+ }
+
+ public class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
+ method @NonNull public static android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder builder();
+ method public int getBandwidth();
+ method public int getCodeRate();
+ method public int getGuardInterval();
+ method public int getModulation();
+ method public int getTimeInterleaveMode();
+ method public int getTransmissionMode();
+ method public int getType();
+ field public static final int BANDWIDTH_6MHZ = 4; // 0x4
+ field public static final int BANDWIDTH_8MHZ = 2; // 0x2
+ field public static final int BANDWIDTH_AUTO = 1; // 0x1
+ field public static final int BANDWIDTH_UNDEFINED = 0; // 0x0
+ field public static final int CODERATE_2_5 = 2; // 0x2
+ field public static final int CODERATE_3_5 = 4; // 0x4
+ field public static final int CODERATE_4_5 = 8; // 0x8
+ field public static final int CODERATE_AUTO = 1; // 0x1
+ field public static final int CODERATE_UNDEFINED = 0; // 0x0
+ field public static final int GUARD_INTERVAL_AUTO = 1; // 0x1
+ field public static final int GUARD_INTERVAL_PN_420_CONST = 16; // 0x10
+ field public static final int GUARD_INTERVAL_PN_420_VARIOUS = 2; // 0x2
+ field public static final int GUARD_INTERVAL_PN_595_CONST = 4; // 0x4
+ field public static final int GUARD_INTERVAL_PN_945_CONST = 32; // 0x20
+ field public static final int GUARD_INTERVAL_PN_945_VARIOUS = 8; // 0x8
+ field public static final int GUARD_INTERVAL_PN_RESERVED = 64; // 0x40
+ field public static final int GUARD_INTERVAL_UNDEFINED = 0; // 0x0
+ field public static final int MODULATION_CONSTELLATION_16QAM = 8; // 0x8
+ field public static final int MODULATION_CONSTELLATION_32QAM = 16; // 0x10
+ field public static final int MODULATION_CONSTELLATION_4QAM = 2; // 0x2
+ field public static final int MODULATION_CONSTELLATION_4QAM_NR = 4; // 0x4
+ field public static final int MODULATION_CONSTELLATION_64QAM = 32; // 0x20
+ field public static final int MODULATION_CONSTELLATION_AUTO = 1; // 0x1
+ field public static final int MODULATION_CONSTELLATION_UNDEFINED = 0; // 0x0
+ field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1
+ field public static final int TIME_INTERLEAVE_MODE_TIMER_INT_240 = 2; // 0x2
+ field public static final int TIME_INTERLEAVE_MODE_TIMER_INT_720 = 4; // 0x4
+ field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0
+ field public static final int TRANSMISSION_MODE_AUTO = 1; // 0x1
+ field public static final int TRANSMISSION_MODE_C1 = 2; // 0x2
+ field public static final int TRANSMISSION_MODE_C3780 = 4; // 0x4
+ field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0
+ }
+
+ public static final class DtmbFrontendSettings.Builder {
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings build();
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setBandwidth(int);
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setCodeRate(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setFrequency(int);
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setGuardInterval(int);
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setModulation(int);
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setTimeInterleaveMode(int);
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setTransmissionMode(int);
+ }
+
public class DvbcFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
method public int getAnnexCapability();
method public int getFecCapability();
@@ -5645,6 +5755,7 @@
method public int getOuterFec();
method public int getSpectralInversion();
method public int getSymbolRate();
+ method public int getTimeInterleaveMode();
method public int getType();
field public static final int ANNEX_A = 1; // 0x1
field public static final int ANNEX_B = 2; // 0x2
@@ -5660,9 +5771,20 @@
field public static final int OUTER_FEC_OUTER_FEC_NONE = 1; // 0x1
field public static final int OUTER_FEC_OUTER_FEC_RS = 2; // 0x2
field public static final int OUTER_FEC_UNDEFINED = 0; // 0x0
- field public static final int SPECTRAL_INVERSION_INVERTED = 2; // 0x2
- field public static final int SPECTRAL_INVERSION_NORMAL = 1; // 0x1
- field public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
+ field @Deprecated public static final int SPECTRAL_INVERSION_INVERTED = 2; // 0x2
+ field @Deprecated public static final int SPECTRAL_INVERSION_NORMAL = 1; // 0x1
+ field @Deprecated public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
+ field public static final int TIME_INTERLEAVE_MODE_128_1_0 = 2; // 0x2
+ field public static final int TIME_INTERLEAVE_MODE_128_1_1 = 4; // 0x4
+ field public static final int TIME_INTERLEAVE_MODE_128_2 = 128; // 0x80
+ field public static final int TIME_INTERLEAVE_MODE_128_3 = 256; // 0x100
+ field public static final int TIME_INTERLEAVE_MODE_128_4 = 512; // 0x200
+ field public static final int TIME_INTERLEAVE_MODE_16_8 = 32; // 0x20
+ field public static final int TIME_INTERLEAVE_MODE_32_4 = 16; // 0x10
+ field public static final int TIME_INTERLEAVE_MODE_64_2 = 8; // 0x8
+ field public static final int TIME_INTERLEAVE_MODE_8_16 = 64; // 0x40
+ field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1
+ field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0
}
public static class DvbcFrontendSettings.Builder {
@@ -5674,6 +5796,7 @@
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setOuterFec(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSpectralInversion(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSymbolRate(int);
+ method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setTimeInterleaveMode(int);
}
public class DvbsCodeRate {
@@ -5705,6 +5828,7 @@
method public int getModulation();
method public int getPilot();
method public int getRolloff();
+ method public int getScanType();
method public int getStandard();
method public int getSymbolRate();
method public int getType();
@@ -5735,6 +5859,11 @@
field public static final int ROLLOFF_0_35 = 1; // 0x1
field public static final int ROLLOFF_0_5 = 6; // 0x6
field public static final int ROLLOFF_UNDEFINED = 0; // 0x0
+ field public static final int SCAN_TYPE_DIRECT = 1; // 0x1
+ field public static final int SCAN_TYPE_DISEQC = 2; // 0x2
+ field public static final int SCAN_TYPE_JESS = 4; // 0x4
+ field public static final int SCAN_TYPE_UNDEFINED = 0; // 0x0
+ field public static final int SCAN_TYPE_UNICABLE = 3; // 0x3
field public static final int STANDARD_AUTO = 1; // 0x1
field public static final int STANDARD_S = 2; // 0x2
field public static final int STANDARD_S2 = 4; // 0x4
@@ -5752,6 +5881,7 @@
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setPilot(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setRolloff(int);
+ method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setScanType(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setStandard(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setSymbolRate(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setVcmMode(int);
@@ -5804,10 +5934,14 @@
field public static final int CODERATE_AUTO = 1; // 0x1
field public static final int CODERATE_UNDEFINED = 0; // 0x0
field public static final int CONSTELLATION_16QAM = 4; // 0x4
+ field public static final int CONSTELLATION_16QAM_R = 64; // 0x40
field public static final int CONSTELLATION_256QAM = 16; // 0x10
+ field public static final int CONSTELLATION_256QAM_R = 256; // 0x100
field public static final int CONSTELLATION_64QAM = 8; // 0x8
+ field public static final int CONSTELLATION_64QAM_R = 128; // 0x80
field public static final int CONSTELLATION_AUTO = 1; // 0x1
field public static final int CONSTELLATION_QPSK = 2; // 0x2
+ field public static final int CONSTELLATION_QPSK_R = 32; // 0x20
field public static final int CONSTELLATION_UNDEFINED = 0; // 0x0
field public static final int GUARD_INTERVAL_19_128 = 64; // 0x40
field public static final int GUARD_INTERVAL_19_256 = 128; // 0x80
@@ -5841,6 +5975,9 @@
field public static final int TRANSMISSION_MODE_4K = 8; // 0x8
field public static final int TRANSMISSION_MODE_8K = 4; // 0x4
field public static final int TRANSMISSION_MODE_AUTO = 1; // 0x1
+ field public static final int TRANSMISSION_MODE_EXTENDED_16K = 256; // 0x100
+ field public static final int TRANSMISSION_MODE_EXTENDED_32K = 512; // 0x200
+ field public static final int TRANSMISSION_MODE_EXTENDED_8K = 128; // 0x80
field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0
}
@@ -5878,8 +6015,12 @@
}
public abstract class FrontendSettings {
+ method public int getEndFrequency();
method public int getFrequency();
+ method public int getFrontendSpectralInversion();
method public abstract int getType();
+ method @IntRange(from=1) public void setEndFrequency(int);
+ method public void setSpectralInversion(int);
field public static final long FEC_11_15 = 4194304L; // 0x400000L
field public static final long FEC_11_20 = 8388608L; // 0x800000L
field public static final long FEC_11_45 = 16777216L; // 0x1000000L
@@ -5917,9 +6058,13 @@
field public static final long FEC_9_20 = 2097152L; // 0x200000L
field public static final long FEC_AUTO = 1L; // 0x1L
field public static final long FEC_UNDEFINED = 0L; // 0x0L
+ field public static final int FRONTEND_SPECTRAL_INVERSION_INVERTED = 2; // 0x2
+ field public static final int FRONTEND_SPECTRAL_INVERSION_NORMAL = 1; // 0x1
+ field public static final int FRONTEND_SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
field public static final int TYPE_ANALOG = 1; // 0x1
field public static final int TYPE_ATSC = 2; // 0x2
field public static final int TYPE_ATSC3 = 3; // 0x3
+ field public static final int TYPE_DTMB = 10; // 0xa
field public static final int TYPE_DVBC = 4; // 0x4
field public static final int TYPE_DVBS = 5; // 0x5
field public static final int TYPE_DVBT = 6; // 0x6
@@ -11027,6 +11172,25 @@
field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
}
+ public final class ModemActivityInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo);
+ method public long getIdleTimeMillis();
+ method public static int getNumTxPowerLevels();
+ method public long getReceiveTimeMillis();
+ method public long getSleepTimeMillis();
+ method public long getTimestampMillis();
+ method public long getTransmitDurationMillisAtPowerLevel(int);
+ method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
+ field public static final int TX_POWER_LEVEL_0 = 0; // 0x0
+ field public static final int TX_POWER_LEVEL_1 = 1; // 0x1
+ field public static final int TX_POWER_LEVEL_2 = 2; // 0x2
+ field public static final int TX_POWER_LEVEL_3 = 3; // 0x3
+ field public static final int TX_POWER_LEVEL_4 = 4; // 0x4
+ }
+
public final class NetworkRegistrationInfo implements android.os.Parcelable {
method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
method public int getRegistrationState();
@@ -11520,7 +11684,6 @@
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<java.lang.String> getEquivalentHomePlmns();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
@@ -11554,6 +11717,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
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);
@@ -11584,6 +11748,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -11634,6 +11799,8 @@
field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
field public static final int KEY_TYPE_EPDG = 1; // 0x1
field public static final int KEY_TYPE_WLAN = 2; // 0x2
+ field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1
+ field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2
field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
@@ -13076,7 +13243,7 @@
method @Nullable public String findProxyForUrl(@NonNull String);
method @NonNull public static android.webkit.PacProcessor getInstance();
method @Nullable public default android.net.Network getNetwork();
- method public default void releasePacProcessor();
+ method public default void release();
method public default void setNetwork(@Nullable android.net.Network);
method public boolean setProxyScript(@NonNull String);
}
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 7195144..773ecd03 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -159,8 +159,6 @@
MissingNullability: android.telephony.ModemActivityInfo#writeToParcel(android.os.Parcel, int) parameter #0:
-MissingNullability: android.telephony.ModemActivityInfo.TransmitPower#toString():
-
MissingNullability: android.telephony.NetworkService#onUnbind(android.content.Intent) parameter #0:
MissingNullability: android.telephony.SmsCbCmasInfo#toString():
diff --git a/api/test-current.txt b/api/test-current.txt
index 6ee4f95..d61526a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -14,6 +14,7 @@
field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
field public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES";
+ field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
@@ -23,6 +24,7 @@
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
+ field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
@@ -78,20 +80,22 @@
}
public class ActivityManager {
+ method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int);
method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName);
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
method public long getTotalRam();
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
- method @RequiresPermission("android.permission.INJECT_EVENTS") public void holdLock(int);
+ method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public void holdLock(int);
method public static boolean isHighEndGfx();
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void killProcessesWhenImperceptible(@NonNull int[], @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
method public static void resumeAppSwitches() throws android.os.RemoteException;
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
- method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle);
field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7
field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1
field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6
@@ -200,12 +204,12 @@
public class AppOpsManager {
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void addHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOps);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void clearHistory();
- method @Nullable @RequiresPermission("android.permission.GET_APP_OPS_STATS") public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
- method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
+ method @Nullable @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
+ method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void getHistoricalOpsFromDiskRaw(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method public static int getNumOps();
method public static String[] getOpStrs();
- method @NonNull @RequiresPermission("android.permission.GET_APP_OPS_STATS") public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
+ method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
method public boolean isOperationActive(int, int, String);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void offsetHistory(long);
method public static int opToDefaultMode(@NonNull String);
@@ -451,10 +455,15 @@
}
public class DreamManager {
- method @RequiresPermission("android.permission.READ_DREAM_STATE") public boolean isDreaming();
- method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void setActiveDream(@NonNull android.content.ComponentName);
- method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void startDream(@NonNull android.content.ComponentName);
- method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void stopDream();
+ method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isDreaming();
+ method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void setActiveDream(@NonNull android.content.ComponentName);
+ method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void startDream(@NonNull android.content.ComponentName);
+ method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void stopDream();
+ }
+
+ public abstract class HomeVisibilityListener {
+ ctor public HomeVisibilityListener();
+ method public abstract void onHomeVisibilityChanged(boolean);
}
public final class NotificationChannel implements android.os.Parcelable {
@@ -486,9 +495,11 @@
method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments();
method @Nullable public android.content.ComponentName getAllowedNotificationAssistant();
method public android.content.ComponentName getEffectsSuppressor();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public java.util.List<android.content.ComponentName> getEnabledNotificationListeners();
method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
method public boolean matchesCallFilter(android.os.Bundle);
method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean);
method public void updateNotificationChannel(@NonNull String, int, @NonNull android.app.NotificationChannel);
}
@@ -546,14 +557,14 @@
}
public class UiModeManager {
- method @RequiresPermission("android.permission.ENTER_CAR_MODE_PRIORITIZED") public void enableCarMode(@IntRange(from=0) int, int);
+ method @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) public void enableCarMode(@IntRange(from=0) int, int);
method public boolean isNightModeLocked();
method public boolean isUiModeLocked();
}
public class WallpaperManager {
method @Nullable public android.graphics.Bitmap getBitmap();
- method @RequiresPermission("android.permission.SET_WALLPAPER_COMPONENT") public boolean setWallpaperComponent(android.content.ComponentName);
+ method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponent(android.content.ComponentName);
method public boolean shouldEnableWideColorGamut();
method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public boolean wallpaperSupportsWcg(int);
}
@@ -629,11 +640,11 @@
package android.app.backup {
public class BackupManager {
- method @RequiresPermission("android.permission.BACKUP") public android.content.Intent getConfigurationIntent(String);
- method @RequiresPermission("android.permission.BACKUP") public android.content.Intent getDataManagementIntent(String);
- method @Nullable @RequiresPermission("android.permission.BACKUP") public CharSequence getDataManagementIntentLabel(@NonNull String);
- method @Deprecated @Nullable @RequiresPermission("android.permission.BACKUP") public String getDataManagementLabel(@NonNull String);
- method @RequiresPermission("android.permission.BACKUP") public String getDestinationString(String);
+ method @RequiresPermission(android.Manifest.permission.BACKUP) public android.content.Intent getConfigurationIntent(String);
+ method @RequiresPermission(android.Manifest.permission.BACKUP) public android.content.Intent getDataManagementIntent(String);
+ method @Nullable @RequiresPermission(android.Manifest.permission.BACKUP) public CharSequence getDataManagementIntentLabel(@NonNull String);
+ method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.BACKUP) public String getDataManagementLabel(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.BACKUP) public String getDestinationString(String);
}
}
@@ -758,21 +769,21 @@
}
public class RoleControllerManager {
- method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
}
public final class RoleManager {
- method @RequiresPermission("android.permission.OBSERVE_ROLE_HOLDERS") public void addOnRoleHoldersChangedListenerAsUser(@NonNull java.util.concurrent.Executor, @NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
- method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void addOnRoleHoldersChangedListenerAsUser(@NonNull java.util.concurrent.Executor, @NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
- method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
- method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
- method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
method @Nullable public String getSmsRoleHolder(int);
- method @RequiresPermission("android.permission.OBSERVE_ROLE_HOLDERS") public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
- method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>);
field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1
@@ -782,6 +793,28 @@
package android.app.usage {
+ public final class CacheQuotaHint implements android.os.Parcelable {
+ ctor public CacheQuotaHint(@NonNull android.app.usage.CacheQuotaHint.Builder);
+ method public int describeContents();
+ method public long getQuota();
+ method public int getUid();
+ method @Nullable public android.app.usage.UsageStats getUsageStats();
+ method @Nullable public String getVolumeUuid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.CacheQuotaHint> CREATOR;
+ field public static final long QUOTA_NOT_SET = -1L; // 0xffffffffffffffffL
+ }
+
+ public static final class CacheQuotaHint.Builder {
+ ctor public CacheQuotaHint.Builder();
+ ctor public CacheQuotaHint.Builder(@NonNull android.app.usage.CacheQuotaHint);
+ method @NonNull public android.app.usage.CacheQuotaHint build();
+ method @NonNull public android.app.usage.CacheQuotaHint.Builder setQuota(long);
+ method @NonNull public android.app.usage.CacheQuotaHint.Builder setUid(int);
+ method @NonNull public android.app.usage.CacheQuotaHint.Builder setUsageStats(@Nullable android.app.usage.UsageStats);
+ method @NonNull public android.app.usage.CacheQuotaHint.Builder setVolumeUuid(@Nullable String);
+ }
+
public class NetworkStatsManager {
method public void setPollForce(boolean);
}
@@ -794,6 +827,10 @@
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_CRATES) @WorkerThread public java.util.Collection<android.os.storage.CrateInfo> queryCratesForUser(@NonNull java.util.UUID, @NonNull android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
}
+ public final class UsageStats implements android.os.Parcelable {
+ ctor public UsageStats();
+ }
+
public final class UsageStatsManager {
method public void forceUsageSourceSettingRead();
}
@@ -879,7 +916,7 @@
method public int getUserId();
method public void setAutofillOptions(@Nullable android.content.AutofillOptions);
method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions);
- method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
+ method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
field public static final String BUGREPORT_SERVICE = "bugreport";
field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
@@ -898,7 +935,7 @@
}
public class Intent implements java.lang.Cloneable android.os.Parcelable {
- field @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
+ field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
field public static final String ACTION_ROLLBACK_COMMITTED = "android.intent.action.ROLLBACK_COMMITTED";
field public static final String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID";
field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
@@ -998,7 +1035,7 @@
public static class PackageInstaller.SessionParams implements android.os.Parcelable {
method public void setEnableRollback(boolean);
method public void setEnableRollback(boolean, int);
- method @RequiresPermission("android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS") public void setGrantedRuntimePermissions(String[]);
+ method @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) public void setGrantedRuntimePermissions(String[]);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex();
method public void setInstallAsInstantApp(boolean);
method public void setInstallerPackageName(@Nullable String);
@@ -1010,25 +1047,25 @@
method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void addOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
method public abstract boolean arePermissionsIndividuallyControlled();
method @Nullable public String getContentCaptureServicePackageName();
- method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract String getDefaultBrowserPackageNameAsUser(int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract String getDefaultBrowserPackageNameAsUser(int);
method @Nullable public String getDefaultTextClassifierPackageName();
method @Nullable public String getIncidentReportApproverPackageName();
method public abstract int getInstallReason(@NonNull String, @NonNull android.os.UserHandle);
method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
- method @NonNull @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
method @Nullable public abstract String[] getNamesForUids(int[]);
method @NonNull public abstract String getPermissionControllerPackageName();
- method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS", "android.permission.GET_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+ method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
method @Nullable public String getSystemTextClassifierPackageName();
method @Nullable public String getWellbeingPackageName();
- method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
- method @RequiresPermission("android.permission.INJECT_EVENTS") public void holdLock(int);
+ method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+ method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public void holdLock(int);
method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
- method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
- method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull String);
- method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, int, int, @NonNull android.os.UserHandle);
+ method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+ method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull String);
+ method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle);
field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
@@ -1223,6 +1260,34 @@
}
+package android.hardware.biometrics {
+
+ public class BiometricManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession createTestSession(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties();
+ }
+
+ public class BiometricTestSession implements java.lang.AutoCloseable {
+ method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void acceptAuthentication(int);
+ method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void cleanupInternalState(int);
+ method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void close();
+ method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void finishEnroll(int);
+ method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void notifyAcquired(int);
+ method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void notifyError(int);
+ method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void rejectAuthentication(int);
+ method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void startEnroll(int);
+ }
+
+ public class SensorProperties {
+ method public int getSensorId();
+ method public int getSensorStrength();
+ field public static final int STRENGTH_CONVENIENCE = 0; // 0x0
+ field public static final int STRENGTH_STRONG = 2; // 0x2
+ field public static final int STRENGTH_WEAK = 1; // 0x1
+ }
+
+}
+
package android.hardware.camera2 {
public abstract class CameraDevice implements java.lang.AutoCloseable {
@@ -1313,7 +1378,7 @@
}
public final class DisplayManager {
- method @RequiresPermission("android.permission.ACCESS_AMBIENT_LIGHT_STATS") public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS) public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
method @RequiresPermission(android.Manifest.permission.BRIGHTNESS_SLIDER_USAGE) public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
method @Nullable @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
@@ -1328,11 +1393,20 @@
}
+package android.hardware.fingerprint {
+
+ @Deprecated public class FingerprintManager {
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession createTestSession(int);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties();
+ }
+
+}
+
package android.hardware.hdmi {
public final class HdmiControlManager {
method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient();
- method @RequiresPermission("android.permission.HDMI_CEC") public void setStandbyMode(boolean);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setStandbyMode(boolean);
field public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE";
field public static final int AVR_VOLUME_MUTED = 101; // 0x65
field public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 162; // 0xa2
@@ -1443,11 +1517,9 @@
field public static final int PORT_OUTPUT = 1; // 0x1
}
- public class HdmiSwitchClient {
+ public class HdmiSwitchClient extends android.hardware.hdmi.HdmiClient {
method public int getDeviceType();
method @NonNull public java.util.List<android.hardware.hdmi.HdmiPortInfo> getPortInfo();
- method public void sendKeyEvent(int, boolean);
- method public void sendVendorCommand(int, byte[], boolean);
}
}
@@ -1718,8 +1790,7 @@
method @NonNull public String[] getBackgroundThrottlingWhitelist();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
method @NonNull public String[] getIgnoreSettingsWhitelist();
- method @Deprecated @Nullable @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public java.util.List<java.lang.String> getProviderPackages(@NonNull String);
- method @NonNull public java.util.List<android.location.LocationRequest> getTestProviderCurrentRequests(String);
+ method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public java.util.List<java.lang.String> getProviderPackages(@NonNull String);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
@@ -1732,15 +1803,15 @@
method public boolean isHiddenFromAppOps();
method public boolean isLocationSettingsIgnored();
method public boolean isLowPower();
- field public static final int ACCURACY_BLOCK = 102; // 0x66
- field public static final int ACCURACY_CITY = 104; // 0x68
- field public static final int ACCURACY_FINE = 100; // 0x64
- field public static final int POWER_HIGH = 203; // 0xcb
- field public static final int POWER_LOW = 201; // 0xc9
+ field @Deprecated public static final int ACCURACY_BLOCK = 102; // 0x66
+ field @Deprecated public static final int ACCURACY_CITY = 104; // 0x68
+ field @Deprecated public static final int ACCURACY_FINE = 100; // 0x64
+ field @Deprecated public static final int POWER_HIGH = 203; // 0xcb
+ field @Deprecated public static final int POWER_LOW = 201; // 0xc9
}
public static final class LocationRequest.Builder {
- method @NonNull @RequiresPermission("android.permission.UPDATE_APP_OPS_STATS") public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
+ method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
@@ -1775,12 +1846,12 @@
}
public class AudioManager {
- method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method public boolean hasRegisteredDynamicPolicy();
- method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
- method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
- method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
- method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
field public static final int SUCCESS = 0; // 0x0
}
@@ -2095,6 +2166,21 @@
}
+package android.media.tv.tuner {
+
+ public final class TunerVersionChecker {
+ method public static int getMajorVersion(int);
+ method public static int getMinorVersion(int);
+ method public static int getTunerVersion();
+ method public static boolean isHigherOrEqualVersionTo(int);
+ method public static boolean supportTunerVersion(int);
+ field public static final int TUNER_VERSION_1_0 = 65536; // 0x10000
+ field public static final int TUNER_VERSION_1_1 = 65537; // 0x10001
+ field public static final int TUNER_VERSION_UNKNOWN = 0; // 0x0
+ }
+
+}
+
package android.metrics {
public class LogMaker {
@@ -2273,15 +2359,15 @@
method @NonNull public android.net.NetworkCapabilities build();
method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
- method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int);
method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int);
method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier);
- method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setOwnerUid(int);
- method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String);
- method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setRequestorUid(int);
- method @NonNull @RequiresPermission("android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP") public android.net.NetworkCapabilities.Builder setSignalStrength(int);
- method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setOwnerUid(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String);
+ 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 setTransportInfo(@Nullable android.net.TransportInfo);
}
@@ -2339,100 +2425,6 @@
method public void teardownTestNetwork(@NonNull android.net.Network);
}
- public final class TetheredClient implements android.os.Parcelable {
- ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
- method public int describeContents();
- method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
- method @NonNull public android.net.MacAddress getMacAddress();
- method public int getTetheringType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
- }
-
- public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.net.LinkAddress getAddress();
- method @Nullable public String getHostname();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
- }
-
- public class TetheringManager {
- method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
- field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
- field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
- field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
- field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
- field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
- field public static final int TETHERING_BLUETOOTH = 2; // 0x2
- field public static final int TETHERING_ETHERNET = 5; // 0x5
- field public static final int TETHERING_INVALID = -1; // 0xffffffff
- field public static final int TETHERING_NCM = 4; // 0x4
- field public static final int TETHERING_USB = 1; // 0x1
- field public static final int TETHERING_WIFI = 0; // 0x0
- field public static final int TETHERING_WIFI_P2P = 3; // 0x3
- field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
- field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
- field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
- field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
- field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
- field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
- field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
- field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
- field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
- field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
- field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
- field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
- field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
- field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
- field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
- field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
- field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
- field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
- field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
- field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
- }
-
- public static interface TetheringManager.OnTetheringEntitlementResultListener {
- method public void onTetheringEntitlementResult(int);
- }
-
- public static interface TetheringManager.StartTetheringCallback {
- method public default void onTetheringFailed(int);
- method public default void onTetheringStarted();
- }
-
- public static interface TetheringManager.TetheringEventCallback {
- method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
- method public default void onError(@NonNull String, int);
- method public default void onOffloadStatusChanged(int);
- method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetheringSupported(boolean);
- method public default void onUpstreamChanged(@Nullable android.net.Network);
- }
-
- public static class TetheringManager.TetheringRequest {
- method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
- method @Nullable public android.net.LinkAddress getLocalIpv4Address();
- method public boolean getShouldShowEntitlementUi();
- method public int getTetheringType();
- method public boolean isExemptFromEntitlementCheck();
- }
-
- public static class TetheringManager.TetheringRequest.Builder {
- ctor public TetheringManager.TetheringRequest.Builder(int);
- method @NonNull public android.net.TetheringManager.TetheringRequest build();
- method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
- method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
- method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
- }
-
public class TrafficStats {
method public static long getLoopbackRxBytes();
method public static long getLoopbackRxPackets();
@@ -2632,7 +2624,7 @@
package android.os {
public class BatteryManager {
- method @RequiresPermission("android.permission.POWER_SAVER") public boolean setChargingStateUpdateDelayMillis(int);
+ method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setChargingStateUpdateDelayMillis(int);
}
public final class BugreportManager {
@@ -2676,7 +2668,7 @@
}
public class DeviceIdleManager {
- method @RequiresPermission("android.permission.DEVICE_POWER") public void endIdle(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void endIdle(@NonNull String);
method @NonNull public String[] getSystemPowerWhitelist();
method @NonNull public String[] getSystemPowerWhitelistExceptIdle();
}
@@ -2929,21 +2921,21 @@
}
public final class PowerManager {
- method @RequiresPermission("android.permission.POWER_SAVER") public int getPowerSaveModeTrigger();
- method @RequiresPermission("android.permission.DEVICE_POWER") public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
- method @RequiresPermission("android.permission.POWER_SAVER") public boolean setDynamicPowerSaveHint(boolean, int);
- method @RequiresPermission(anyOf={"android.permission.DEVICE_POWER", "android.permission.POWER_SAVER"}) public boolean setPowerSaveModeEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveModeTrigger();
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
+ method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean, int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean);
field public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED = "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
field public static final int POWER_SAVE_MODE_TRIGGER_DYNAMIC = 1; // 0x1
field public static final int POWER_SAVE_MODE_TRIGGER_PERCENTAGE = 0; // 0x0
}
public class PowerWhitelistManager {
- method @RequiresPermission("android.permission.DEVICE_POWER") public void addToWhitelist(@NonNull String);
- method @RequiresPermission("android.permission.DEVICE_POWER") public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
- method @RequiresPermission("android.permission.DEVICE_POWER") public void removeFromWhitelist(@NonNull String);
- method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public void whitelistAppTemporarily(@NonNull String, long);
- method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String);
field public static final int EVENT_MMS = 2; // 0x2
field public static final int EVENT_SMS = 1; // 0x1
field public static final int EVENT_UNSPECIFIED = 0; // 0x0
@@ -3009,8 +3001,8 @@
}
public class SystemConfigManager {
- method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
- method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
}
public class SystemProperties {
@@ -3039,7 +3031,7 @@
}
public class UserManager {
- method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS"}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
method public static boolean isSplitSystemUser();
field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
}
@@ -3099,10 +3091,10 @@
}
public abstract class Vibrator {
- method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
- method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener);
- method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public boolean isVibrating();
- method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void removeVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public boolean isVibrating();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void removeVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
}
public static interface Vibrator.OnVibratorStateChangedListener {
@@ -3201,12 +3193,12 @@
public class DynamicSystemClient {
ctor public DynamicSystemClient(@NonNull android.content.Context);
- method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void bind();
+ method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void bind();
method public void setOnStatusChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.image.DynamicSystemClient.OnStatusChangedListener);
method public void setOnStatusChangedListener(@NonNull android.os.image.DynamicSystemClient.OnStatusChangedListener);
- method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void start(@NonNull android.net.Uri, long);
- method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void start(@NonNull android.net.Uri, long, long);
- method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void unbind();
+ method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void start(@NonNull android.net.Uri, long);
+ method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void start(@NonNull android.net.Uri, long, long);
+ method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void unbind();
field public static final int CAUSE_ERROR_EXCEPTION = 6; // 0x6
field public static final int CAUSE_ERROR_INVALID_URL = 4; // 0x4
field public static final int CAUSE_ERROR_IO = 3; // 0x3
@@ -3260,13 +3252,13 @@
package android.permission {
public final class PermissionControllerManager {
- method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.RESTORE_RUNTIME_PERMISSIONS"}) public void applyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void countPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, @Nullable android.os.Handler);
- method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void getAppPermissions(@NonNull String, @NonNull android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, @Nullable android.os.Handler);
- method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void getRuntimePermissionBackup(@NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>);
- method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermission(@NonNull String, @NonNull String);
- method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull java.util.concurrent.Executor, @NonNull android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
- method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.RESTORE_RUNTIME_PERMISSIONS"}) public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[], @NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void applyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void countPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, @Nullable android.os.Handler);
+ method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getAppPermissions(@NonNull String, @NonNull android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, @Nullable android.os.Handler);
+ method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getRuntimePermissionBackup(@NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>);
+ method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermission(@NonNull String, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull java.util.concurrent.Executor, @NonNull android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[], @NonNull android.os.UserHandle);
field public static final int COUNT_ONLY_WHEN_GRANTED = 1; // 0x1
field public static final int COUNT_WHEN_SYSTEM = 2; // 0x2
field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2
@@ -3287,9 +3279,9 @@
}
public final class PermissionManager {
- method @IntRange(from=0) @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion();
+ method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion();
method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
- method @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
}
public static final class PermissionManager.SplitPermissionInfo {
@@ -3355,14 +3347,14 @@
}
public final class DeviceConfig {
- method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
- method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
- method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static float getFloat(@NonNull String, @NonNull String, float);
- method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static int getInt(@NonNull String, @NonNull String, int);
- method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static long getLong(@NonNull String, @NonNull String, long);
- method @NonNull @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static android.provider.DeviceConfig.Properties getProperties(@NonNull String, @NonNull java.lang.String...);
- method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getProperty(@NonNull String, @NonNull String);
- method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getString(@NonNull String, @NonNull String, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
+ method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
+ method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float);
+ method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int);
+ method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static long getLong(@NonNull String, @NonNull String, long);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static android.provider.DeviceConfig.Properties getProperties(@NonNull String, @NonNull java.lang.String...);
+ method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getProperty(@NonNull String, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(@NonNull String, @NonNull String, @Nullable String);
method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String);
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperties(@NonNull android.provider.DeviceConfig.Properties) throws android.provider.DeviceConfig.BadConfigException;
@@ -3413,12 +3405,6 @@
method @NonNull public static android.net.Uri setManageMode(@NonNull android.net.Uri);
}
- public final class MediaStore {
- method @NonNull @WorkerThread public static android.net.Uri scanFile(@NonNull android.content.ContentResolver, @NonNull java.io.File);
- method @WorkerThread public static void scanVolume(@NonNull android.content.ContentResolver, @NonNull String);
- method @WorkerThread public static void waitForIdle(@NonNull android.content.ContentResolver);
- }
-
public final class Settings {
field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
field public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION = "android.settings.MANAGE_APP_OVERLAY_PERMISSION";
@@ -3435,6 +3421,7 @@
field public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD = "dynamic_power_savings_disable_threshold";
field public static final String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled";
field public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions";
+ field public static final String HIDDEN_API_POLICY = "hidden_api_policy";
field public static final String HIDE_ERROR_DIALOGS = "hide_error_dialogs";
field public static final String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch";
field public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST = "location_ignore_settings_package_whitelist";
@@ -3450,6 +3437,11 @@
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String);
field public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
+ field public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY = "accessibility_magnification_capability";
+ field public static final String ACCESSIBILITY_MAGNIFICATION_MODE = "accessibility_magnification_mode";
+ field public static final int ACCESSIBILITY_MAGNIFICATION_MODE_ALL = 3; // 0x3
+ field public static final int ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN = 1; // 0x1
+ field public static final int ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW = 2; // 0x2
field public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
field public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
field public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
@@ -4177,6 +4169,27 @@
field public static final String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
}
+ public final class ModemActivityInfo implements android.os.Parcelable {
+ ctor public ModemActivityInfo(long, int, int, @NonNull int[], int);
+ method public int describeContents();
+ method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo);
+ method public long getIdleTimeMillis();
+ method public static int getNumTxPowerLevels();
+ method public long getReceiveTimeMillis();
+ method public long getSleepTimeMillis();
+ method public long getTimestampMillis();
+ method public long getTransmitDurationMillisAtPowerLevel(int);
+ method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int);
+ method public boolean isValid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
+ field public static final int TX_POWER_LEVEL_0 = 0; // 0x0
+ field public static final int TX_POWER_LEVEL_1 = 1; // 0x1
+ field public static final int TX_POWER_LEVEL_2 = 2; // 0x2
+ field public static final int TX_POWER_LEVEL_3 = 3; // 0x3
+ field public static final int TX_POWER_LEVEL_4 = 4; // 0x4
+ }
+
public final class NetworkRegistrationInfo implements android.os.Parcelable {
method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
method public int getRegistrationState();
@@ -4219,8 +4232,8 @@
method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
- field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
- field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
+ field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
+ field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
}
public final class PreciseDataConnectionState implements android.os.Parcelable {
@@ -4261,27 +4274,31 @@
public class TelephonyManager {
method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
method public int checkCarrierPrivilegesForPackage(String);
- method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
+ method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
method public int getCarrierIdListVersion();
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
- method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public android.content.ComponentName getDefaultRespondViaMessageApplication();
+ method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getDefaultRespondViaMessageApplication();
method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
- method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void resetOtaEmergencyNumberDbFilePath();
+ method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void resetOtaEmergencyNumberDbFilePath();
method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
method public void setCarrierTestOverride(String, String, String, String, String, String, String, String, String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>);
- method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor);
+ method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor);
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
+ field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1
+ field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2
field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff
}
@@ -5357,7 +5374,7 @@
}
public interface WindowManager extends android.view.ViewManager {
- method @RequiresPermission("android.permission.INJECT_EVENTS") public default void holdLock(int);
+ method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public default void holdLock(int);
method public default void setShouldShowIme(int, boolean);
method public default void setShouldShowSystemDecors(int, boolean);
method public default void setShouldShowWithInsecureKeyguard(int, boolean);
@@ -5378,11 +5395,11 @@
public final class AccessibilityManager {
method public void addAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, @Nullable android.os.Handler);
- method @NonNull @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public java.util.List<java.lang.String> getAccessibilityShortcutTargets(int);
- method @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public void performAccessibilityShortcut();
- method @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public void registerSystemAction(@NonNull android.app.RemoteAction, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public java.util.List<java.lang.String> getAccessibilityShortcutTargets(int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void registerSystemAction(@NonNull android.app.RemoteAction, int);
method public void removeAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
- method @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public void unregisterSystemAction(int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int);
}
public static interface AccessibilityManager.AccessibilityServicesStateChangeListener {
@@ -5725,6 +5742,15 @@
field public static final int FEATURE_WINDOW_TOKENS = 2; // 0x2
}
+ public final class TaskAppearedInfo implements android.os.Parcelable {
+ ctor public TaskAppearedInfo(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
+ method public int describeContents();
+ method @NonNull public android.view.SurfaceControl getLeash();
+ method @NonNull public android.app.ActivityManager.RunningTaskInfo getTaskInfo();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskAppearedInfo> CREATOR;
+ }
+
public class TaskOrganizer extends android.window.WindowOrganizer {
ctor public TaskOrganizer();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.app.ActivityManager.RunningTaskInfo createRootTask(int, int);
@@ -5736,10 +5762,10 @@
method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void registerOrganizer();
+ method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public java.util.List<android.window.TaskAppearedInfo> registerOrganizer();
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setLaunchRoot(int, @NonNull android.window.WindowContainerToken);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void unregisterOrganizer();
+ method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void unregisterOrganizer();
}
public final class WindowContainerToken implements android.os.Parcelable {
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 91a09e3..0440d1a 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -2511,6 +2511,8 @@
NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_BLACKLIST_EXEMPTIONS:
+NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_POLICY:
+
NoSettingsProvider: android.provider.Settings.Global#HIDE_ERROR_DIALOGS:
NoSettingsProvider: android.provider.Settings.Global#LOCATION_GLOBAL_KILL_SWITCH:
@@ -2530,6 +2532,16 @@
NoSettingsProvider: android.provider.Settings.Global#USE_OPEN_WIFI_PACKAGE:
NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_CAPABILITY:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_ALL:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW:
NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE:
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 90c8788..6eba5c6 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -266,7 +266,7 @@
147 [(module) = "framework", (module) = "statsd"];
BiometricSystemHealthIssueDetected biometric_system_health_issue_detected =
148 [(module) = "framework"];
- BubbleUIChanged bubble_ui_changed = 149 [(module) = "sysui"];
+ BubbleUIChanged bubble_ui_changed = 149 [(module) = "framework"];
ScheduledJobConstraintChanged scheduled_job_constraint_changed =
150 [(module) = "framework"];
BluetoothActiveDeviceChanged bluetooth_active_device_changed =
@@ -493,13 +493,17 @@
WifiConnectionStateChanged wifi_connection_state_changed = 308 [(module) = "wifi"];
HdmiCecActiveSourceChanged hdmi_cec_active_source_changed = 309 [(module) = "framework"];
HdmiCecMessageReported hdmi_cec_message_reported = 310 [(module) = "framework"];
+ AirplaneMode airplane_mode = 311 [(module) = "telephony"];
+ ModemRestart modem_restart = 312 [(module) = "telephony"];
+ CarrierIdMismatchEvent carrier_id_mismatch_event = 313 [(module) = "telephony"];
+ CarrierIdMatchingTable carrier_id_table_update = 314 [(module) = "telephony"];
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
}
// Pulled events will start at field 10000.
- // Next: 10088
+ // Next: 10089
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -600,6 +604,7 @@
10085 [(module) = "mediaprovider"];
IncomingSms incoming_sms = 10086 [(module) = "telephony"];
OutgoingSms outgoing_sms = 10087 [(module) = "telephony"];
+ CarrierIdMatchingTable carrier_id_table_version = 10088 [(module) = "telephony"];
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -3832,6 +3837,12 @@
// App startup time (until call to Activity#reportFullyDrawn()).
optional int64 app_startup_time_millis = 6;
+ // The compiler filter used when when the package was optimized.
+ optional int32 package_optimization_compilation_filter = 7;
+
+ // The reason why the package was optimized.
+ optional int32 package_optimization_compilation_reason = 8;
+
enum SourceType {
UNAVAILABLE = 0;
LAUNCHER = 1;
@@ -3839,11 +3850,11 @@
LOCKSCREEN = 3;
}
// The type of the startup source.
- optional SourceType source_type = 7;
+ optional SourceType source_type = 9;
// The time from the startup source to the beginning of handling the startup event.
// -1 means not available.
- optional int32 source_event_delay_millis = 8;
+ optional int32 source_event_delay_millis = 10;
}
/**
@@ -4466,6 +4477,7 @@
// which requires reboot and not eligible for any reboot promotion strategy
// (e.g. soft restart, notification restart).
NO_REBOOT_PROMOTION_STRATEGY_ELIGIBLE = 30;
+ REBOOT_TRIGGER_FAILURE = 31;
}
optional State state = 6;
// Possible experiment ids for monitoring this push.
@@ -5266,12 +5278,23 @@
* Event to track Jank for various system interactions.
*
* Logged from:
- * frameworks/base/core/java/android/os/aot/FrameTracker.java
+ * frameworks/base/core/java/com/android/internal/jank/FrameTracker.java
*/
message UIInteractionFrameInfoReported {
enum InteractionType {
UNKNOWN = 0;
NOTIFICATION_SHADE_SWIPE = 1;
+ SHADE_EXPAND_COLLAPSE_LOCK = 2;
+ SHADE_SCROLL_FLING = 3;
+ SHADE_ROW_EXPAND = 4;
+ SHADE_ROW_SWIPE = 5;
+ SHADE_QS_EXPAND_COLLAPSE = 6;
+ SHADE_QS_SCROLL_SWIPE = 7;
+ LAUNCHER_APP_LAUNCH_FROM_RECENTS = 8;
+ LAUNCHER_APP_LAUNCH_FROM_ICON = 9;
+ LAUNCHER_APP_CLOSE_TO_HOME = 10;
+ LAUNCHER_APP_CLOSE_TO_PIP = 11;
+ LAUNCHER_QUICK_SWITCH = 12;
}
optional InteractionType interaction_type = 1;
@@ -10575,12 +10598,85 @@
}
/**
+ * Push information about usage of airplane mode.
+ *
+ * Logged from:
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/AirplaneModeStats.java
+ */
+message AirplaneMode {
+ // Status of airplane mode
+ optional bool is_enabled = 1;
+
+ // When is_enabled is false, indicates if this was a very short airplane mode toggle
+ // (i.e. airplane mode was disabled after less than 10 seconds from enablement).
+ optional bool short_toggle = 2;
+
+ // Carrier ID of the SIM card.
+ // See https://source.android.com/devices/tech/config/carrierid.
+ optional int32 carrier_id = 3;
+}
+
+/**
+ * Push information about modem restarts.
+ *
+ * Logged from:
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/ModemRestartStats.java
+ */
+message ModemRestart {
+ // Software version of the modem, as provided by android.os.Build.getRadioVersion().
+ optional string baseband_version = 1;
+
+ // Reason of the modem restart, as provided in the modemReset indication of IRadio HAL.
+ optional string reason = 2;
+
+ // Carrier ID of the first SIM card.
+ // See https://source.android.com/devices/tech/config/carrierid.
+ optional int32 carrier_id = 3;
+}
+
+/**
+ * Push the SIM card details when the carrier ID match is not complete.
+ *
+ * The atom is pushed when a SIM card is initialized and the MCC/MNC is not present in the
+ * carrier ID table, or the SIM card contains a GID1 value that is not present in the carrier ID
+ * table. This atom is pushed only once for each type of SIM card.
+ *
+ * Logged from:
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java
+ */
+message CarrierIdMismatchEvent {
+ // Matched carrier ID. The value -1 is used if no match is found.
+ optional int32 carrier_id = 1;
+
+ // MCC/MNC of the SIM card.
+ optional string mcc_mnc = 2;
+
+ // Group identifier (level 1) of the SIM card.
+ optional string gid1 = 3;
+
+ // SPN value of the SIM card.
+ optional string spn = 4;
+}
+
+/**
+ * Pulls/pushes the version of the carrier ID matching table.
+ *
+ * The atom is pushed when a new version is detected.
+ *
+ * Logged from:
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java
+ */
+message CarrierIdMatchingTable {
+ // Version of the CarrierId matching table.
+ optional int32 table_version = 1;
+}
+
+/**
* Logs gnss stats from location service provider
*
* Pulled from:
* frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
*/
-
message GnssStats {
// Number of location reports since boot
optional int64 location_reports = 1;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 9dda248..2a37b58 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -17,9 +17,11 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
-#include "../guardrail/StatsdStats.h"
#include "GaugeMetricProducer.h"
-#include "../stats_log_util.h"
+
+#include "guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
+#include "stats_log_util.h"
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
@@ -154,6 +156,58 @@
}
}
+bool GaugeMetricProducer::onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!MetricProducer::onConfigUpdatedLocked(
+ config, configIndex, metricIndex, allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+ trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+ return false;
+ }
+
+ const GaugeMetric& metric = config.gauge_metric(configIndex);
+ // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps.
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, /*enforceOneAtom=*/false,
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ trackerToMetricMap, mWhatMatcherIndex)) {
+ return false;
+ }
+
+ // Need to update maps since the index changed, but mTriggerAtomId will not change.
+ int triggerTrackerIndex;
+ if (metric.has_trigger_event() &&
+ !handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex,
+ /*enforceOneAtom=*/true, allAtomMatchingTrackers,
+ newAtomMatchingTrackerMap, trackerToMetricMap,
+ triggerTrackerIndex)) {
+ return false;
+ }
+
+ if (metric.has_condition() &&
+ !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, mConditionTrackerIndex,
+ conditionToMetricMap)) {
+ return false;
+ }
+ sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard;
+ mEventMatcherWizard = matcherWizard;
+ return true;
+}
+
void GaugeMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
if (mCurrentSlicedBucket == nullptr ||
mCurrentSlicedBucket->size() == 0) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index e933d4b..9bdaac9 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -53,8 +53,8 @@
// This gauge metric producer first register the puller to automatically pull the gauge at the
// beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise
// proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric
-// producer always reports the guage at the earliest time of the bucket when the condition is met.
-class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
+// producer always reports the gauge at the earliest time of the bucket when the condition is met.
+class GaugeMetricProducer : public MetricProducer, public virtual PullDataReceiver {
public:
GaugeMetricProducer(
const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
@@ -142,7 +142,23 @@
void pullAndMatchEventsLocked(const int64_t timestampNs);
- const int mWhatMatcherIndex;
+ bool onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation) override;
+
+ int mWhatMatcherIndex;
sp<EventMatcherWizard> mEventMatcherWizard;
@@ -209,6 +225,8 @@
FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents);
FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled);
+
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 4360010..18e62d2 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -568,6 +568,7 @@
FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricActivations);
FRIEND_TEST(ConfigUpdateTest, TestUpdateCountMetrics);
FRIEND_TEST(ConfigUpdateTest, TestUpdateEventMetrics);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricsMultipleTypes);
};
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index 8477dba..d32f5a9 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -516,6 +516,21 @@
return false;
}
}
+ for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) {
+ const DurationMetric& metric = config.duration_metric(i);
+ set<int64_t> conditionDependencies({metric.what()});
+ if (metric.has_condition()) {
+ conditionDependencies.insert(metric.condition());
+ }
+ if (!determineMetricUpdateStatus(
+ config, metric, metric.id(), METRIC_TYPE_DURATION, /*matcherDependencies=*/{},
+ conditionDependencies, metric.slice_by_state(), metric.links(),
+ oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ replacedMatchers, replacedConditions, replacedStates,
+ metricsToUpdate[metricIndex])) {
+ return false;
+ }
+ }
for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
const EventMetric& metric = config.event_metric(i);
set<int64_t> conditionDependencies;
@@ -538,8 +553,7 @@
if (metric.has_condition()) {
conditionDependencies.insert(metric.condition());
}
- set<int64_t> matcherDependencies;
- matcherDependencies.insert(metric.what());
+ set<int64_t> matcherDependencies({metric.what()});
if (metric.has_trigger_event()) {
matcherDependencies.insert(metric.trigger_event());
}
@@ -552,10 +566,48 @@
return false;
}
}
- // TODO: determine update status for value, duration metrics.
+ // TODO: determine update status for value metrics.
return true;
}
+// Called when a metric is preserved during a config update. Finds the metric in oldMetricProducers
+// and calls onConfigUpdated to update all indices.
+optional<sp<MetricProducer>> updateMetric(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const int64_t metricId, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId);
+ if (oldMetricProducerIt == oldMetricProducerMap.end()) {
+ ALOGE("Could not find Metric %lld in the previous config, but expected it "
+ "to be there",
+ (long long)metricId);
+ return nullopt;
+ }
+ const int oldIndex = oldMetricProducerIt->second;
+ sp<MetricProducer> producer = oldMetricProducers[oldIndex];
+ if (!producer->onConfigUpdated(config, configIndex, metricIndex, allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap,
+ matcherWizard, allConditionTrackers, conditionTrackerMap, wizard,
+ metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+ activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+ return nullopt;
+ }
+ return {producer};
+}
+
bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
@@ -609,41 +661,29 @@
// Now, perform the update. Must iterate the metric types in the same order
int metricIndex = 0;
for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) {
- newMetricProducerMap[config.count_metric(i).id()] = metricIndex;
const CountMetric& metric = config.count_metric(i);
+ newMetricProducerMap[metric.id()] = metricIndex;
+ optional<sp<MetricProducer>> producer;
switch (metricsToUpdate[metricIndex]) {
case UPDATE_PRESERVE: {
- const auto& oldMetricProducerIt = oldMetricProducerMap.find(metric.id());
- if (oldMetricProducerIt == oldMetricProducerMap.end()) {
- ALOGE("Could not find Metric %lld in the previous config, but expected it "
- "to be there",
- (long long)metric.id());
- return false;
- }
- const int oldIndex = oldMetricProducerIt->second;
- sp<MetricProducer> producer = oldMetricProducers[oldIndex];
- producer->onConfigUpdated(
- config, i, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap,
- newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers,
- conditionTrackerMap, wizard, metricToActivationMap, trackerToMetricMap,
+ producer = updateMetric(
+ config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+ oldMetricProducers, metricToActivationMap, trackerToMetricMap,
conditionToMetricMap, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation);
- newMetricProducers.push_back(producer);
break;
}
case UPDATE_REPLACE:
case UPDATE_NEW: {
- sp<MetricProducer> producer = createCountMetricProducerAndUpdateMetadata(
+ producer = createCountMetricProducerAndUpdateMetadata(
key, config, timeBaseNs, currentTimeNs, metric, metricIndex,
allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
allStateGroupMaps, metricToActivationMap, trackerToMetricMap,
conditionToMetricMap, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation);
- if (producer == nullptr) {
- return false;
- }
- newMetricProducers.push_back(producer);
break;
}
default: {
@@ -652,42 +692,34 @@
return false;
}
}
+ if (!producer) {
+ return false;
+ }
+ newMetricProducers.push_back(producer.value());
}
for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
newMetricProducerMap[config.event_metric(i).id()] = metricIndex;
const EventMetric& metric = config.event_metric(i);
+ optional<sp<MetricProducer>> producer;
switch (metricsToUpdate[metricIndex]) {
case UPDATE_PRESERVE: {
- const auto& oldMetricProducerIt = oldMetricProducerMap.find(metric.id());
- if (oldMetricProducerIt == oldMetricProducerMap.end()) {
- ALOGE("Could not find Metric %lld in the previous config, but expected it "
- "to be there",
- (long long)metric.id());
- return false;
- }
- const int oldIndex = oldMetricProducerIt->second;
- sp<MetricProducer> producer = oldMetricProducers[oldIndex];
- producer->onConfigUpdated(
- config, i, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap,
- newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers,
- conditionTrackerMap, wizard, metricToActivationMap, trackerToMetricMap,
+ producer = updateMetric(
+ config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+ oldMetricProducers, metricToActivationMap, trackerToMetricMap,
conditionToMetricMap, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation);
- newMetricProducers.push_back(producer);
break;
}
case UPDATE_REPLACE:
case UPDATE_NEW: {
- sp<MetricProducer> producer = createEventMetricProducerAndUpdateMetadata(
+ producer = createEventMetricProducerAndUpdateMetadata(
key, config, timeBaseNs, metric, metricIndex, allAtomMatchingTrackers,
newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
conditionToMetricMap, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation);
- if (producer == nullptr) {
- return false;
- }
- newMetricProducers.push_back(producer);
break;
}
default: {
@@ -696,8 +728,49 @@
return false;
}
}
+ if (!producer) {
+ return false;
+ }
+ newMetricProducers.push_back(producer.value());
}
- // TODO: perform update for count, gauge, value, duration metric.
+ for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
+ const GaugeMetric& metric = config.gauge_metric(i);
+ newMetricProducerMap[metric.id()] = metricIndex;
+ optional<sp<MetricProducer>> producer;
+ switch (metricsToUpdate[metricIndex]) {
+ case UPDATE_PRESERVE: {
+ producer = updateMetric(
+ config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+ oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ break;
+ }
+ case UPDATE_REPLACE:
+ case UPDATE_NEW: {
+ producer = createGaugeMetricProducerAndUpdateMetadata(
+ key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex,
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+ conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
+ metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation);
+ break;
+ }
+ default: {
+ ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+ (long long)metric.id());
+ return false;
+ }
+ }
+ if (!producer) {
+ return false;
+ }
+ newMetricProducers.push_back(producer.value());
+ }
+ // TODO: perform update for value, duration metric.
const set<int> atomsAllowedFromAnyUid(config.whitelisted_atom_ids().begin(),
config.whitelisted_atom_ids().end());
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
index 98c6222..34e265c 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -348,7 +348,7 @@
return true;
}
-sp<MetricProducer> createCountMetricProducerAndUpdateMetadata(
+optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata(
const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex,
const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
@@ -366,14 +366,14 @@
vector<int>& metricsWithActivation) {
if (!metric.has_id() || !metric.has_what()) {
ALOGW("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id());
- return nullptr;
+ return nullopt;
}
int trackerIndex;
if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
metric.has_dimensions_in_what(),
allAtomMatchingTrackers, atomMatchingTrackerMap,
trackerToMetricMap, trackerIndex)) {
- return nullptr;
+ return nullopt;
}
int conditionIndex = -1;
@@ -381,12 +381,12 @@
if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
metric.links(), allConditionTrackers, conditionIndex,
conditionToMetricMap)) {
- return nullptr;
+ return nullopt;
}
} else {
if (metric.links_size() > 0) {
ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return nullptr;
+ return nullopt;
}
}
@@ -395,12 +395,12 @@
if (metric.slice_by_state_size() > 0) {
if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
- return nullptr;
+ return nullopt;
}
} else {
if (metric.state_link_size() > 0) {
ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
- return nullptr;
+ return nullopt;
}
}
@@ -410,20 +410,20 @@
atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation,
eventActivationMap, eventDeactivationMap)) {
- return nullptr;
+ return nullopt;
}
uint64_t metricHash;
if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
- return nullptr;
+ return nullopt;
}
- return new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
- metricHash, timeBaseNs, currentTimeNs, eventActivationMap,
- eventDeactivationMap, slicedStateAtoms, stateGroupMap);
+ return {new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+ metricHash, timeBaseNs, currentTimeNs, eventActivationMap,
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
}
-sp<MetricProducer> createEventMetricProducerAndUpdateMetadata(
+optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
const EventMetric& metric, const int metricIndex,
const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
@@ -439,13 +439,13 @@
vector<int>& metricsWithActivation) {
if (!metric.has_id() || !metric.has_what()) {
ALOGW("cannot find the metric name or what in config");
- return nullptr;
+ return nullopt;
}
int trackerIndex;
if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
allAtomMatchingTrackers, atomMatchingTrackerMap,
trackerToMetricMap, trackerIndex)) {
- return nullptr;
+ return nullopt;
}
int conditionIndex = -1;
@@ -453,12 +453,12 @@
if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
metric.links(), allConditionTrackers, conditionIndex,
conditionToMetricMap)) {
- return nullptr;
+ return nullopt;
}
} else {
if (metric.links_size() > 0) {
ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return nullptr;
+ return nullopt;
}
}
@@ -472,12 +472,125 @@
uint64_t metricHash;
if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
- return nullptr;
+ return nullopt;
}
- return new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
- metricHash, timeBaseNs, eventActivationMap,
- eventDeactivationMap);
+ return {new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+ metricHash, timeBaseNs, eventActivationMap,
+ eventDeactivationMap)};
+}
+
+optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const GaugeMetric& metric, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!metric.has_id() || !metric.has_what()) {
+ ALOGW("cannot find metric id or \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
+ return nullopt;
+ }
+
+ if ((!metric.gauge_fields_filter().has_include_all() ||
+ (metric.gauge_fields_filter().include_all() == false)) &&
+ !hasLeafNode(metric.gauge_fields_filter().fields())) {
+ ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
+ return nullopt;
+ }
+ if ((metric.gauge_fields_filter().has_include_all() &&
+ metric.gauge_fields_filter().include_all() == true) &&
+ hasLeafNode(metric.gauge_fields_filter().fields())) {
+ ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
+ return nullopt;
+ }
+
+ int trackerIndex;
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
+ metric.has_dimensions_in_what(),
+ allAtomMatchingTrackers, atomMatchingTrackerMap,
+ trackerToMetricMap, trackerIndex)) {
+ return nullopt;
+ }
+
+ sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+ // For GaugeMetric atom, it should be simple matcher with one tagId.
+ if (atomMatcher->getAtomIds().size() != 1) {
+ return nullopt;
+ }
+ int atomTagId = *(atomMatcher->getAtomIds().begin());
+ int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+
+ int triggerTrackerIndex;
+ int triggerAtomId = -1;
+ if (metric.has_trigger_event()) {
+ if (pullTagId == -1) {
+ ALOGW("Pull atom not specified for trigger");
+ return nullopt;
+ }
+ // trigger_event should be used with FIRST_N_SAMPLES
+ if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) {
+ ALOGW("Gauge Metric with trigger event must have sampling type FIRST_N_SAMPLES");
+ return nullopt;
+ }
+ if (!handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex,
+ /*enforceOneAtom=*/true, allAtomMatchingTrackers,
+ atomMatchingTrackerMap, trackerToMetricMap,
+ triggerTrackerIndex)) {
+ return nullopt;
+ }
+ sp<AtomMatchingTracker> triggerAtomMatcher =
+ allAtomMatchingTrackers.at(triggerTrackerIndex);
+ triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
+ }
+
+ if (!metric.has_trigger_event() && pullTagId != -1 &&
+ metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) {
+ ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger");
+ return nullopt;
+ }
+
+ int conditionIndex = -1;
+ if (metric.has_condition()) {
+ if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap)) {
+ return nullopt;
+ }
+ } else {
+ if (metric.links_size() > 0) {
+ ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+ return nullopt;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+ atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation,
+ eventActivationMap, eventDeactivationMap)) {
+ return nullopt;
+ }
+
+ uint64_t metricHash;
+ if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+ return nullopt;
+ }
+
+ return {new GaugeMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+ metricHash, trackerIndex, matcherWizard, pullTagId,
+ triggerAtomId, atomTagId, timeBaseNs, currentTimeNs,
+ pullerManager, eventActivationMap, eventDeactivationMap)};
}
bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
@@ -628,17 +741,17 @@
int metricIndex = allMetricProducers.size();
const CountMetric& metric = config.count_metric(i);
metricMap.insert({metric.id(), metricIndex});
- sp<MetricProducer> producer = createCountMetricProducerAndUpdateMetadata(
+ optional<sp<MetricProducer>> producer = createCountMetricProducerAndUpdateMetadata(
key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex,
allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
metricsWithActivation);
- if (producer == nullptr) {
+ if (!producer) {
return false;
}
- allMetricProducers.push_back(producer);
+ allMetricProducers.push_back(producer.value());
}
// build DurationMetricProducer
@@ -762,16 +875,16 @@
int metricIndex = allMetricProducers.size();
const EventMetric& metric = config.event_metric(i);
metricMap.insert({metric.id(), metricIndex});
- sp<MetricProducer> producer = createEventMetricProducerAndUpdateMetadata(
+ optional<sp<MetricProducer>> producer = createEventMetricProducerAndUpdateMetadata(
key, config, timeBaseTimeNs, metric, metricIndex, allAtomMatchingTrackers,
atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
conditionToMetricMap, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation);
- if (producer == nullptr) {
+ if (!producer) {
return false;
}
- allMetricProducers.push_back(producer);
+ allMetricProducers.push_back(producer.value());
}
// build ValueMetricProducer
@@ -871,104 +984,20 @@
// Gauge metrics.
for (int i = 0; i < config.gauge_metric_size(); i++) {
- const GaugeMetric& metric = config.gauge_metric(i);
- if (!metric.has_what()) {
- ALOGW("cannot find \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
- return false;
- }
-
- if ((!metric.gauge_fields_filter().has_include_all() ||
- (metric.gauge_fields_filter().include_all() == false)) &&
- !hasLeafNode(metric.gauge_fields_filter().fields())) {
- ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
- return false;
- }
- if ((metric.gauge_fields_filter().has_include_all() &&
- metric.gauge_fields_filter().include_all() == true) &&
- hasLeafNode(metric.gauge_fields_filter().fields())) {
- ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
- return false;
- }
-
int metricIndex = allMetricProducers.size();
+ const GaugeMetric& metric = config.gauge_metric(i);
metricMap.insert({metric.id(), metricIndex});
- int trackerIndex;
- if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
- metric.has_dimensions_in_what(),
- allAtomMatchingTrackers, atomMatchingTrackerMap,
- trackerToMetricMap, trackerIndex)) {
- return false;
- }
-
- sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
- // For GaugeMetric atom, it should be simple matcher with one tagId.
- if (atomMatcher->getAtomIds().size() != 1) {
- return false;
- }
- int atomTagId = *(atomMatcher->getAtomIds().begin());
- int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
-
- int triggerTrackerIndex;
- int triggerAtomId = -1;
- if (metric.has_trigger_event()) {
- if (pullTagId == -1) {
- ALOGW("Pull atom not specified for trigger");
- return false;
- }
- // event_trigger should be used with FIRST_N_SAMPLES
- if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) {
- return false;
- }
- if (!handleMetricWithAtomMatchingTrackers(
- metric.trigger_event(), metricIndex, /*enforceOneAtom=*/true,
- allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap,
- triggerTrackerIndex)) {
- return false;
- }
- sp<AtomMatchingTracker> triggerAtomMatcher =
- allAtomMatchingTrackers.at(triggerTrackerIndex);
- triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
- }
-
- if (!metric.has_trigger_event() && pullTagId != -1 &&
- metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) {
- ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger");
- return false;
- }
-
- int conditionIndex = -1;
- if (metric.has_condition()) {
- bool good = handleMetricWithConditions(
- metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
- allConditionTrackers, conditionIndex, conditionToMetricMap);
- if (!good) {
- return false;
- }
- } else {
- if (metric.links_size() > 0) {
- ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return false;
- }
- }
-
- unordered_map<int, shared_ptr<Activation>> eventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- bool success = handleMetricActivation(
- config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
+ optional<sp<MetricProducer>> producer = createGaugeMetricProducerAndUpdateMetadata(
+ key, config, timeBaseTimeNs, currentTimeNs, pullerManager, metric, metricIndex,
+ allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+ conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
+ metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, eventActivationMap, eventDeactivationMap);
- if (!success) return false;
-
- uint64_t metricHash;
- if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+ metricsWithActivation);
+ if (!producer) {
return false;
}
-
- sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
- key, metric, conditionIndex, initialConditionCache, wizard, metricHash,
- trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs,
- currentTimeNs, pullerManager, eventActivationMap, eventDeactivationMap);
- allMetricProducers.push_back(gaugeProducer);
+ allMetricProducers.push_back(producer.value());
}
for (int i = 0; i < config.no_report_metric_size(); ++i) {
const auto no_report_metric = config.no_report_metric(i);
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
index f6b2b7d..f909aff 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
@@ -94,8 +94,8 @@
std::unordered_map<int, std::vector<shared_ptr<Activation>>>& newEventDeactivationMap);
// Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with
-// the appropriate indices. Returns an sp to the producer, or null if there was an error.
-sp<MetricProducer> createCountMetricProducerAndUpdateMetadata(
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata(
const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
@@ -113,8 +113,8 @@
std::vector<int>& metricsWithActivation);
// Creates an EventMetricProducer and updates the vectors/maps used by MetricsManager with
-// the appropriate indices. Returns an sp to the producer, or null if there was an error.
-sp<MetricProducer> createEventMetricProducerAndUpdateMetadata(
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
const EventMetric& metric, const int metricIndex,
const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
@@ -129,6 +129,25 @@
std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
std::vector<int>& metricsWithActivation);
+// Creates a GaugeMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const GaugeMetric& metric, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
+
// Helper functions for MetricsManager to initialize from StatsdConfig.
// *Note*: only initStatsdConfig() should be called from outside.
// All other functions are intermediate
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index 70e4dea..a20be15 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -27,6 +27,7 @@
#include "src/condition/CombinationConditionTracker.h"
#include "src/condition/SimpleConditionTracker.h"
#include "src/matchers/CombinationAtomMatchingTracker.h"
+#include "src/metrics/GaugeMetricProducer.h"
#include "src/metrics/parsing_utils/metrics_manager_util.h"
#include "tests/statsd_test_util.h"
@@ -137,6 +138,37 @@
return metric;
}
+GaugeMetric createGaugeMetric(string name, int64_t what, GaugeMetric::SamplingType samplingType,
+ optional<int64_t> condition, optional<int64_t> triggerEvent) {
+ GaugeMetric metric;
+ metric.set_id(StringToId(name));
+ metric.set_what(what);
+ metric.set_bucket(TEN_MINUTES);
+ metric.set_sampling_type(samplingType);
+ if (condition) {
+ metric.set_condition(condition.value());
+ }
+ if (triggerEvent) {
+ metric.set_trigger_event(triggerEvent.value());
+ }
+ metric.mutable_gauge_fields_filter()->set_include_all(true);
+ return metric;
+}
+
+DurationMetric createDurationMetric(string name, int64_t what, optional<int64_t> condition,
+ vector<int64_t> states) {
+ DurationMetric metric;
+ metric.set_id(StringToId(name));
+ metric.set_what(what);
+ metric.set_bucket(TEN_MINUTES);
+ if (condition) {
+ metric.set_condition(condition.value());
+ }
+ for (const int64_t state : states) {
+ metric.add_slice_by_state(state);
+ }
+ return metric;
+}
} // anonymous namespace
TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) {
@@ -1278,11 +1310,8 @@
Predicate predicate = CreateScreenIsOnPredicate();
*config.add_predicate() = predicate;
- GaugeMetric* metric = config.add_gauge_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->set_condition(predicate.id());
- metric->mutable_gauge_fields_filter()->set_include_all(true);
+ *config.add_gauge_metric() = createGaugeMetric(
+ "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt);
EXPECT_TRUE(initConfig(config));
@@ -1300,15 +1329,13 @@
AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
*config.add_atom_matcher() = whatMatcher;
- GaugeMetric* metric = config.add_gauge_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->mutable_gauge_fields_filter()->set_include_all(true);
+ *config.add_gauge_metric() = createGaugeMetric(
+ "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
EXPECT_TRUE(initConfig(config));
// Change split bucket on app upgrade, which should change the proto, causing replacement.
- metric->set_split_bucket_for_app_upgrade(false);
+ config.mutable_gauge_metric(0)->set_split_bucket_for_app_upgrade(false);
unordered_map<int64_t, int> metricToActivationMap;
vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
@@ -1324,10 +1351,8 @@
AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
*config.add_atom_matcher() = whatMatcher;
- GaugeMetric* metric = config.add_gauge_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->mutable_gauge_fields_filter()->set_include_all(true);
+ *config.add_gauge_metric() = createGaugeMetric(
+ "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
EXPECT_TRUE(initConfig(config));
@@ -1352,11 +1377,8 @@
Predicate predicate = CreateScreenIsOnPredicate();
*config.add_predicate() = predicate;
- GaugeMetric* metric = config.add_gauge_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->set_condition(predicate.id());
- metric->mutable_gauge_fields_filter()->set_include_all(true);
+ *config.add_gauge_metric() = createGaugeMetric(
+ "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt);
EXPECT_TRUE(initConfig(config));
@@ -1376,14 +1398,9 @@
AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
*config.add_atom_matcher() = whatMatcher;
- GaugeMetric* metric = config.add_gauge_metric();
- metric->set_id(12345);
- metric->set_what(whatMatcher.id());
- metric->set_trigger_event(triggerEvent.id());
- metric->mutable_gauge_fields_filter()->set_include_all(true);
- metric->set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
+ *config.add_gauge_metric() = createGaugeMetric(
+ "GAUGE1", whatMatcher.id(), GaugeMetric::FIRST_N_SAMPLES, nullopt, triggerEvent.id());
- // Create an initial config.
EXPECT_TRUE(initConfig(config));
unordered_map<int64_t, int> metricToActivationMap;
@@ -1395,6 +1412,130 @@
EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
}
+TEST_F(ConfigUpdateTest, TestDurationMetricPreserve) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+
+ Predicate what = CreateScreenIsOnPredicate();
+ *config.add_predicate() = what;
+ Predicate condition = CreateScreenIsOffPredicate();
+ *config.add_predicate() = condition;
+
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ *config.add_duration_metric() =
+ createDurationMetric("DURATION1", what.id(), condition.id(), {sliceState.id()});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricDefinitionChange) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+
+ Predicate what = CreateScreenIsOnPredicate();
+ *config.add_predicate() = what;
+
+ *config.add_duration_metric() = createDurationMetric("DURATION1", what.id(), nullopt, {});
+ EXPECT_TRUE(initConfig(config));
+
+ config.mutable_duration_metric(0)->set_aggregation_type(DurationMetric::MAX_SPARSE);
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap, /*replacedMatchers*/ {},
+ /*replacedConditions=*/{}, /*replacedStates=*/{},
+ metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricWhatChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+
+ Predicate what = CreateScreenIsOnPredicate();
+ *config.add_predicate() = what;
+
+ *config.add_duration_metric() = createDurationMetric("DURATION1", what.id(), nullopt, {});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{what.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricConditionChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+
+ Predicate what = CreateScreenIsOnPredicate();
+ *config.add_predicate() = what;
+ Predicate condition = CreateScreenIsOffPredicate();
+ *config.add_predicate() = condition;
+
+ *config.add_duration_metric() = createDurationMetric("DURATION", what.id(), condition.id(), {});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{condition.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricStateChange) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+
+ Predicate what = CreateScreenIsOnPredicate();
+ *config.add_predicate() = what;
+
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ *config.add_duration_metric() =
+ createDurationMetric("DURATION1", what.id(), nullopt, {sliceState.id()});
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) {
StatsdConfig config;
@@ -1856,6 +1997,221 @@
EXPECT_EQ(screenState.mValue.int_value, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
}
+TEST_F(ConfigUpdateTest, TestUpdateGaugeMetrics) {
+ StatsdConfig config;
+
+ // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+ AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+ int64_t matcher2Id = matcher2.id();
+ *config.add_atom_matcher() = matcher2;
+
+ AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ AtomMatcher matcher4 = CreateTemperatureAtomMatcher();
+ int64_t matcher4Id = matcher4.id();
+ *config.add_atom_matcher() = matcher4;
+
+ AtomMatcher matcher5 = CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+ int64_t matcher5Id = matcher5.id();
+ *config.add_atom_matcher() = matcher5;
+
+ Predicate predicate1 = CreateScreenIsOnPredicate();
+ int64_t predicate1Id = predicate1.id();
+ *config.add_predicate() = predicate1;
+
+ // Add a few gauge metrics.
+ // Will be preserved.
+ GaugeMetric gauge1 = createGaugeMetric("GAUGE1", matcher4Id, GaugeMetric::FIRST_N_SAMPLES,
+ predicate1Id, matcher1Id);
+ int64_t gauge1Id = gauge1.id();
+ *config.add_gauge_metric() = gauge1;
+
+ // Will be replaced.
+ GaugeMetric gauge2 =
+ createGaugeMetric("GAUGE2", matcher1Id, GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt);
+ int64_t gauge2Id = gauge2.id();
+ *config.add_gauge_metric() = gauge2;
+
+ // Will be replaced.
+ GaugeMetric gauge3 = createGaugeMetric("GAUGE3", matcher5Id, GaugeMetric::FIRST_N_SAMPLES,
+ nullopt, matcher3Id);
+ int64_t gauge3Id = gauge3.id();
+ *config.add_gauge_metric() = gauge3;
+
+ // Will be replaced.
+ GaugeMetric gauge4 = createGaugeMetric("GAUGE4", matcher3Id, GaugeMetric::RANDOM_ONE_SAMPLE,
+ predicate1Id, nullopt);
+ int64_t gauge4Id = gauge4.id();
+ *config.add_gauge_metric() = gauge4;
+
+ // Will be deleted.
+ GaugeMetric gauge5 =
+ createGaugeMetric("GAUGE5", matcher2Id, GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, {});
+ int64_t gauge5Id = gauge5.id();
+ *config.add_gauge_metric() = gauge5;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+ sp<EventMatcherWizard> oldMatcherWizard =
+ static_cast<GaugeMetricProducer*>(oldMetricProducers[0].get())->mEventMatcherWizard;
+ EXPECT_EQ(oldMatcherWizard->getStrongCount(), 6);
+
+ // Change gauge2, causing it to be replaced.
+ gauge2.set_max_num_gauge_atoms_per_bucket(50);
+
+ // Mark matcher 3 as replaced. Causes gauge3 and gauge4 to be replaced.
+ set<int64_t> replacedMatchers = {matcher3Id};
+
+ // New gauge metric.
+ GaugeMetric gauge6 = createGaugeMetric("GAUGE6", matcher5Id, GaugeMetric::FIRST_N_SAMPLES,
+ predicate1Id, matcher3Id);
+ int64_t gauge6Id = gauge6.id();
+
+ // Map the matchers and predicates in reverse order to force the indices to change.
+ std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ const int matcher5Index = 0;
+ newAtomMatchingTrackerMap[matcher5Id] = 0;
+ const int matcher4Index = 1;
+ newAtomMatchingTrackerMap[matcher4Id] = 1;
+ const int matcher3Index = 2;
+ newAtomMatchingTrackerMap[matcher3Id] = 2;
+ const int matcher2Index = 3;
+ newAtomMatchingTrackerMap[matcher2Id] = 3;
+ const int matcher1Index = 4;
+ newAtomMatchingTrackerMap[matcher1Id] = 4;
+ // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+ std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+ newAtomMatchingTrackers.begin());
+
+ std::unordered_map<int64_t, int> newConditionTrackerMap;
+ const int predicate1Index = 0;
+ newConditionTrackerMap[predicate1Id] = 0;
+ // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<ConditionTracker>> newConditionTrackers(1);
+ std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+ newConditionTrackers.begin());
+ // Say that predicate1 is unknown since the initial condition never changed.
+ vector<ConditionState> conditionCache = {ConditionState::kUnknown};
+
+ StatsdConfig newConfig;
+ *newConfig.add_gauge_metric() = gauge6;
+ const int gauge6Index = 0;
+ *newConfig.add_gauge_metric() = gauge3;
+ const int gauge3Index = 1;
+ *newConfig.add_gauge_metric() = gauge1;
+ const int gauge1Index = 2;
+ *newConfig.add_gauge_metric() = gauge4;
+ const int gauge4Index = 3;
+ *newConfig.add_gauge_metric() = gauge2;
+ const int gauge2Index = 4;
+
+ // Output data structures to validate.
+ unordered_map<int64_t, int> newMetricProducerMap;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
+ EXPECT_TRUE(updateMetrics(
+ key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+ newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
+ newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
+ /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+ newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation));
+
+ unordered_map<int64_t, int> expectedMetricProducerMap = {
+ {gauge1Id, gauge1Index}, {gauge2Id, gauge2Index}, {gauge3Id, gauge3Index},
+ {gauge4Id, gauge4Index}, {gauge6Id, gauge6Index},
+ };
+ EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+
+ // Make sure preserved metrics are the same.
+ ASSERT_EQ(newMetricProducers.size(), 5);
+ EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(gauge1Id)],
+ newMetricProducers[newMetricProducerMap.at(gauge1Id)]);
+
+ // Make sure replaced metrics are different.
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge2Id)],
+ newMetricProducers[newMetricProducerMap.at(gauge2Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge3Id)],
+ newMetricProducers[newMetricProducerMap.at(gauge3Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge4Id)],
+ newMetricProducers[newMetricProducerMap.at(gauge4Id)]);
+
+ // Verify the conditionToMetricMap.
+ ASSERT_EQ(conditionToMetricMap.size(), 1);
+ const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+ EXPECT_THAT(condition1Metrics, UnorderedElementsAre(gauge1Index, gauge4Index, gauge6Index));
+
+ // Verify the trackerToMetricMap.
+ ASSERT_EQ(trackerToMetricMap.size(), 4);
+ const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+ EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(gauge1Index, gauge2Index));
+ const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+ EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gauge3Index, gauge4Index, gauge6Index));
+ const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+ EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(gauge1Index));
+ const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
+ EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(gauge3Index, gauge6Index));
+
+ // Verify event activation/deactivation maps.
+ ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(metricsWithActivation.size(), 0);
+
+ // Verify tracker indices/ids/conditions/states are correct.
+ GaugeMetricProducer* gaugeProducer1 =
+ static_cast<GaugeMetricProducer*>(newMetricProducers[gauge1Index].get());
+ EXPECT_EQ(gaugeProducer1->getMetricId(), gauge1Id);
+ EXPECT_EQ(gaugeProducer1->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(gaugeProducer1->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(gaugeProducer1->mWhatMatcherIndex, matcher4Index);
+ GaugeMetricProducer* gaugeProducer2 =
+ static_cast<GaugeMetricProducer*>(newMetricProducers[gauge2Index].get());
+ EXPECT_EQ(gaugeProducer2->getMetricId(), gauge2Id);
+ EXPECT_EQ(gaugeProducer2->mConditionTrackerIndex, -1);
+ EXPECT_EQ(gaugeProducer2->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(gaugeProducer2->mWhatMatcherIndex, matcher1Index);
+ GaugeMetricProducer* gaugeProducer3 =
+ static_cast<GaugeMetricProducer*>(newMetricProducers[gauge3Index].get());
+ EXPECT_EQ(gaugeProducer3->getMetricId(), gauge3Id);
+ EXPECT_EQ(gaugeProducer3->mConditionTrackerIndex, -1);
+ EXPECT_EQ(gaugeProducer3->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(gaugeProducer3->mWhatMatcherIndex, matcher5Index);
+ GaugeMetricProducer* gaugeProducer4 =
+ static_cast<GaugeMetricProducer*>(newMetricProducers[gauge4Index].get());
+ EXPECT_EQ(gaugeProducer4->getMetricId(), gauge4Id);
+ EXPECT_EQ(gaugeProducer4->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(gaugeProducer4->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(gaugeProducer4->mWhatMatcherIndex, matcher3Index);
+ GaugeMetricProducer* gaugeProducer6 =
+ static_cast<GaugeMetricProducer*>(newMetricProducers[gauge6Index].get());
+ EXPECT_EQ(gaugeProducer6->getMetricId(), gauge6Id);
+ EXPECT_EQ(gaugeProducer6->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(gaugeProducer6->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(gaugeProducer6->mWhatMatcherIndex, matcher5Index);
+
+ sp<EventMatcherWizard> newMatcherWizard = gaugeProducer1->mEventMatcherWizard;
+ EXPECT_NE(newMatcherWizard, oldMatcherWizard);
+ EXPECT_EQ(newMatcherWizard->getStrongCount(), 6);
+ oldMetricProducers.clear();
+ // Only reference to the old wizard should be the one in the test.
+ EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1);
+}
+
TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) {
StatsdConfig config;
// Add atom matchers
@@ -1995,6 +2351,10 @@
int64_t matcher2Id = matcher2.id();
*config.add_atom_matcher() = matcher2;
+ AtomMatcher matcher3 = CreateTemperatureAtomMatcher();
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
Predicate predicate1 = CreateScreenIsOnPredicate();
int64_t predicate1Id = predicate1.id();
*config.add_predicate() = predicate1;
@@ -2010,24 +2370,35 @@
int64_t eventMetricId = eventMetric.id();
*config.add_event_metric() = eventMetric;
+ // Will be replaced because the definition changes - a predicate is added.
+ GaugeMetric gaugeMetric = createGaugeMetric("GAUGE1", matcher3Id,
+ GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
+ int64_t gaugeMetricId = gaugeMetric.id();
+ *config.add_gauge_metric() = gaugeMetric;
+
EXPECT_TRUE(initConfig(config));
// Used later to ensure the condition wizard is replaced. Get it before doing the update.
sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
- EXPECT_EQ(oldConditionWizard->getStrongCount(), 3);
+ EXPECT_EQ(oldConditionWizard->getStrongCount(), 4);
// Mark matcher 2 as replaced. Causes eventMetric to be replaced.
set<int64_t> replacedMatchers;
replacedMatchers.insert(matcher2Id);
+ // Add predicate1 as a predicate on gaugeMetric, causing it to be replaced.
+ gaugeMetric.set_condition(predicate1Id);
+
// Map the matchers and predicates in reverse order to force the indices to change.
std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
- const int matcher2Index = 0;
- newAtomMatchingTrackerMap[matcher2Id] = 0;
- const int matcher1Index = 1;
- newAtomMatchingTrackerMap[matcher1Id] = 1;
+ const int matcher3Index = 0;
+ newAtomMatchingTrackerMap[matcher3Id] = 0;
+ const int matcher2Index = 1;
+ newAtomMatchingTrackerMap[matcher2Id] = 1;
+ const int matcher1Index = 2;
+ newAtomMatchingTrackerMap[matcher1Id] = 2;
// Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
- vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(2);
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(3);
std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
newAtomMatchingTrackers.begin());
@@ -2045,6 +2416,8 @@
const int countMetricIndex = 0;
*newConfig.add_event_metric() = eventMetric;
const int eventMetricIndex = 1;
+ *newConfig.add_gauge_metric() = gaugeMetric;
+ const int gaugeMetricIndex = 2;
// Output data structures to validate.
unordered_map<int64_t, int> newMetricProducerMap;
@@ -2068,29 +2441,34 @@
unordered_map<int64_t, int> expectedMetricProducerMap = {
{countMetricId, countMetricIndex},
{eventMetricId, eventMetricIndex},
+ {gaugeMetricId, gaugeMetricIndex},
};
EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
// Make sure preserved metrics are the same.
- ASSERT_EQ(newMetricProducers.size(), 2);
+ ASSERT_EQ(newMetricProducers.size(), 3);
EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(countMetricId)],
newMetricProducers[newMetricProducerMap.at(countMetricId)]);
// Make sure replaced metrics are different.
EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(eventMetricId)],
newMetricProducers[newMetricProducerMap.at(eventMetricId)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gaugeMetricId)],
+ newMetricProducers[newMetricProducerMap.at(gaugeMetricId)]);
// Verify the conditionToMetricMap.
ASSERT_EQ(conditionToMetricMap.size(), 1);
const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
- EXPECT_THAT(condition1Metrics, UnorderedElementsAre(countMetricIndex));
+ EXPECT_THAT(condition1Metrics, UnorderedElementsAre(countMetricIndex, gaugeMetricIndex));
// Verify the trackerToMetricMap.
- ASSERT_EQ(trackerToMetricMap.size(), 2);
+ ASSERT_EQ(trackerToMetricMap.size(), 3);
const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(countMetricIndex));
const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex));
+ const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+ EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gaugeMetricIndex));
// Verify event activation/deactivation maps.
ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
@@ -2104,10 +2482,13 @@
EXPECT_EQ(newMetricProducers[eventMetricIndex]->getMetricId(), eventMetricId);
EXPECT_EQ(newMetricProducers[eventMetricIndex]->mConditionTrackerIndex, -1);
EXPECT_EQ(newMetricProducers[eventMetricIndex]->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->getMetricId(), gaugeMetricId);
+ EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mCondition, ConditionState::kUnknown);
sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
EXPECT_NE(newConditionWizard, oldConditionWizard);
- EXPECT_EQ(newConditionWizard->getStrongCount(), 3);
+ EXPECT_EQ(newConditionWizard->getStrongCount(), 4);
oldMetricProducers.clear();
// Only reference to the old wizard should be the one in the test.
EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 25729ab..e3139eb 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -20,6 +20,7 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
@@ -28,11 +29,13 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP;
@@ -85,13 +88,16 @@
/** @hide */
@IntDef(prefix = { "GESTURE_" }, value = {
GESTURE_2_FINGER_SINGLE_TAP,
+ GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD,
GESTURE_2_FINGER_DOUBLE_TAP,
GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD,
GESTURE_2_FINGER_TRIPLE_TAP,
GESTURE_3_FINGER_SINGLE_TAP,
+ GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD,
GESTURE_3_FINGER_DOUBLE_TAP,
GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD,
GESTURE_3_FINGER_TRIPLE_TAP,
+ GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD,
GESTURE_DOUBLE_TAP,
GESTURE_DOUBLE_TAP_AND_HOLD,
GESTURE_SWIPE_UP,
@@ -180,15 +186,21 @@
private static String eventTypeToString(int eventType) {
switch (eventType) {
case GESTURE_2_FINGER_SINGLE_TAP: return "GESTURE_2_FINGER_SINGLE_TAP";
+ case GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD:
+ return "GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD";
case GESTURE_2_FINGER_DOUBLE_TAP: return "GESTURE_2_FINGER_DOUBLE_TAP";
case GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD:
return "GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD";
case GESTURE_2_FINGER_TRIPLE_TAP: return "GESTURE_2_FINGER_TRIPLE_TAP";
case GESTURE_3_FINGER_SINGLE_TAP: return "GESTURE_3_FINGER_SINGLE_TAP";
+ case GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD:
+ return "GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD";
case GESTURE_3_FINGER_DOUBLE_TAP: return "GESTURE_3_FINGER_DOUBLE_TAP";
case GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD:
return "GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD";
case GESTURE_3_FINGER_TRIPLE_TAP: return "GESTURE_3_FINGER_TRIPLE_TAP";
+ case GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD:
+ return "GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD";
case GESTURE_4_FINGER_SINGLE_TAP: return "GESTURE_4_FINGER_SINGLE_TAP";
case GESTURE_4_FINGER_DOUBLE_TAP: return "GESTURE_4_FINGER_DOUBLE_TAP";
case GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD:
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index b5b0ce3..7c6d448 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -421,6 +421,15 @@
/** The user has performed a three-finger double tap and hold gesture on the touch screen. */
public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41;
+ /** The user has performed a two-finger single-tap and hold gesture on the touch screen. */
+ public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43;
+
+ /** The user has performed a three-finger single-tap and hold gesture on the touch screen. */
+ public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44;
+
+ /** The user has performed a three-finger triple-tap and hold gesture on the touch screen. */
+ public static final int GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD = 45;
+
/** The user has performed a two-finger double tap and hold gesture on the touch screen. */
public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index d334de6..769d006 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -1077,7 +1077,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index 623734e..fe1cca5 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -287,7 +287,7 @@
* {@inheritDoc}
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
index 9a18880..b960a7f 100644
--- a/core/java/android/accounts/Account.java
+++ b/core/java/android/accounts/Account.java
@@ -50,7 +50,7 @@
@UnsupportedAppUsage
private final @Nullable String accessId;
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == this) return true;
if (!(o instanceof Account)) return false;
final Account other = (Account)o;
diff --git a/core/java/android/accounts/AccountAndUser.java b/core/java/android/accounts/AccountAndUser.java
index fd67394..adbc4e9 100644
--- a/core/java/android/accounts/AccountAndUser.java
+++ b/core/java/android/accounts/AccountAndUser.java
@@ -16,6 +16,7 @@
package android.accounts;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
/**
@@ -35,7 +36,7 @@
this.userId = userId;
}
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (!(o instanceof AccountAndUser)) return false;
final AccountAndUser other = (AccountAndUser) o;
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 9a810a7..9bb02cd 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -19,6 +19,7 @@
import android.annotation.BroadcastBehavior;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -357,7 +358,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == null) {
return false;
}
@@ -419,7 +420,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == null) {
return false;
}
diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java
index b7bf11d..4be3538 100644
--- a/core/java/android/accounts/AuthenticatorDescription.java
+++ b/core/java/android/accounts/AuthenticatorDescription.java
@@ -16,6 +16,7 @@
package android.accounts;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -111,7 +112,7 @@
}
/** Compares the type only, suitable for key comparisons. */
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == this) return true;
if (!(o instanceof AuthenticatorDescription)) return false;
final AuthenticatorDescription other = (AuthenticatorDescription) o;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e75d2f6..cb98093 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -100,6 +100,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.concurrent.Executor;
/**
* <p>
@@ -1619,7 +1620,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof TaskDescription)) {
return false;
}
@@ -4752,31 +4753,43 @@
}
/**
- * Register with {@link HomeVisibilityObserver} with ActivityManager.
- * TODO: b/144351078 expose as SystemApi
+ * Register to be notified when the visibility of the home screen changes.
+ *
+ * @param executor The executor on which the listener should be called.
+ * @param listener The listener that is called when home visibility changes.
* @hide
*/
- public void registerHomeVisibilityObserver(@NonNull HomeVisibilityObserver observer) {
- Preconditions.checkNotNull(observer);
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ public void addHomeVisibilityListener(@NonNull Executor executor,
+ @NonNull HomeVisibilityListener listener) {
+ Preconditions.checkNotNull(listener);
+ Preconditions.checkNotNull(executor);
try {
- observer.init(mContext, this);
- getService().registerProcessObserver(observer.mObserver);
+ listener.init(mContext, executor, this);
+ getService().registerProcessObserver(listener.mObserver);
// Notify upon first registration.
- observer.onHomeVisibilityChanged(observer.mIsHomeActivityVisible);
+ executor.execute(() ->
+ listener.onHomeVisibilityChanged(listener.mIsHomeActivityVisible));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Unregister with {@link HomeVisibilityObserver} with ActivityManager.
- * TODO: b/144351078 expose as SystemApi
+ * Removes a listener that was previously added with {@link #addHomeVisibilityListener}.
+ *
+ * @param listener The listener that was previously added.
* @hide
*/
- public void unregisterHomeVisibilityObserver(@NonNull HomeVisibilityObserver observer) {
- Preconditions.checkNotNull(observer);
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ public void removeHomeVisibilityListener(@NonNull HomeVisibilityListener listener) {
+ Preconditions.checkNotNull(listener);
try {
- getService().unregisterProcessObserver(observer.mObserver);
+ getService().unregisterProcessObserver(listener.mObserver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 85cb120..ef146b1 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -37,6 +37,7 @@
import android.hardware.HardwareBuffer;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Parcel;
import android.os.Parcelable;
@@ -306,6 +307,12 @@
private static final String KEY_REMOTE_ANIMATION_ADAPTER
= "android:activity.remoteAnimationAdapter";
+ /**
+ * @see #setLaunchCookie
+ * @hide
+ */
+ private static final String KEY_LAUNCH_COOKIE = "android.activity.launchCookie";
+
/** @hide */
public static final int ANIM_UNDEFINED = -1;
/** @hide */
@@ -381,6 +388,7 @@
private Bundle mAppVerificationBundle;
private IAppTransitionAnimationSpecsFuture mSpecsFuture;
private RemoteAnimationAdapter mRemoteAnimationAdapter;
+ private IBinder mLaunchCookie;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -1071,6 +1079,7 @@
KEY_SPECS_FUTURE));
}
mRemoteAnimationAdapter = opts.getParcelable(KEY_REMOTE_ANIMATION_ADAPTER);
+ mLaunchCookie = opts.getBinder(KEY_LAUNCH_COOKIE);
}
/**
@@ -1284,10 +1293,10 @@
}
/**
- * Sets the id of the display where activity should be launched.
- * An app can launch activities on public displays or private displays that are owned by the app
- * or where an app already has activities. Otherwise, trying to launch on a private display
- * or providing an invalid display id will result in an exception.
+ * Sets the id of the display where the activity should be launched.
+ * An app can launch activities on public displays or displays where the app already has
+ * activities. Otherwise, trying to launch on a private display or providing an invalid display
+ * id will result in an exception.
* <p>
* Setting launch display id will be ignored on devices that don't have
* {@link android.content.pm.PackageManager#FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS}.
@@ -1496,6 +1505,25 @@
}
/**
+ * Sets a launch cookie that can be used to track the activity and task that are launch as a
+ * result of this option.
+ *
+ * @hide
+ */
+ public void setLaunchCookie(IBinder launchCookie) {
+ mLaunchCookie = launchCookie;
+ }
+
+ /**
+ * @return The launch tracking cookie if set or {@code null} otherwise.
+ *
+ * @hide
+ */
+ public IBinder getLaunchCookie() {
+ return mLaunchCookie;
+ }
+
+ /**
* Update the current values in this ActivityOptions from those supplied
* in <var>otherOptions</var>. Any values
* defined in <var>otherOptions</var> replace those in the base options.
@@ -1717,6 +1745,9 @@
if (mRemoteAnimationAdapter != null) {
b.putParcelable(KEY_REMOTE_ANIMATION_ADAPTER, mRemoteAnimationAdapter);
}
+ if (mLaunchCookie != null) {
+ b.putBinder(KEY_LAUNCH_COOKIE, mLaunchCookie);
+ }
return b;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8f7116c..4ea2aac 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -435,7 +435,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof ProviderKey) {
final ProviderKey other = (ProviderKey) o;
return Objects.equals(authority, other.authority) && userId == other.userId;
@@ -2791,7 +2791,7 @@
memInfo.getTotalPrivateDirty(),
memInfo.getTotalPrivateClean(),
memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() :
- memInfo.getTotalSwappedOut(), memInfo.getTotalPss(),
+ memInfo.getTotalSwappedOut(), memInfo.getTotalRss(),
nativeMax+dalvikMax,
nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
}
@@ -3631,7 +3631,7 @@
TAG, "Handling launch of " + r);
// Initialize before creating the activity
- if (!ThreadedRenderer.sRendererDisabled
+ if (ThreadedRenderer.sRendererEnabled
&& (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
HardwareRenderer.preload();
}
@@ -7417,14 +7417,7 @@
@UnsupportedAppUsage
public static ActivityThread systemMain() {
- // The system process on low-memory devices do not get to use hardware
- // accelerated drawing, since this can add too much overhead to the
- // process.
- if (!ActivityManager.isHighEndGfx()) {
- ThreadedRenderer.disable(true);
- } else {
- ThreadedRenderer.enableForegroundTrimming();
- }
+ ThreadedRenderer.initForSystemProcess();
ActivityThread thread = new ActivityThread();
thread.attach(true, 0);
return thread;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 155de36..c5bc356 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -7911,7 +7911,7 @@
*
* @param op The op to note
* @param proxiedUid The uid to note the op for {@code null}
- * @param proxiedUid The package name the uid belongs to
+ * @param proxiedPackageName The package name the uid belongs to
* @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
* attribution tag} or {@code null} for default attribution
* @param message A message describing the reason the op was noted
@@ -7939,17 +7939,7 @@
*Like {@link #startProxyOp(String, int, String, String, String)} but instead
* of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
*
- * @param op The op to note
- * @param proxiedUid The uid to note the op for {@code null}
- * @param proxiedUid The package name the uid belongs to
- * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
- * attribution tag} or {@code null} for default attribution
- * @param message A message describing the reason the op was noted*
- * <p>This API requires package with the {@code proxiedPackageName} to belong to
- * {@code proxiedUid}.
- *
- * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
- * if it is not allowed and should be silently ignored (without causing the app to crash).
+ * @see #startProxyOp(String, int, String, String, String)
*/
public int startProxyOpNoThrow(@NonNull String op, int proxiedUid,
@NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag,
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index e7b3e14..d716a3c 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -1164,7 +1164,7 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (other == null || !(other instanceof ApplicationExitInfo)) {
return false;
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7b25e25..c6b52c1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -667,7 +667,7 @@
name, version);
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof HasSystemFeatureQuery) {
HasSystemFeatureQuery r = (HasSystemFeatureQuery) o;
return Objects.equals(name, r.name) && version == r.version;
@@ -1050,7 +1050,7 @@
* are handled first.
*/
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof GetPackagesForUidResult) {
String [] r = ((GetPackagesForUidResult) o).mValue;
String [] l = mValue;
@@ -2083,7 +2083,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 7180c01..c3272c1 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -282,7 +282,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof AutomaticZenRule)) return false;
if (o == this) return true;
final AutomaticZenRule other = (AutomaticZenRule) o;
diff --git a/core/java/android/app/DirectAction.java b/core/java/android/app/DirectAction.java
index 0268f7c..b0ed490 100644
--- a/core/java/android/app/DirectAction.java
+++ b/core/java/android/app/DirectAction.java
@@ -132,7 +132,7 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (other == null) {
return false;
}
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index f7fb3c3..ce4109c 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -596,7 +596,7 @@
/**
* Subclasses can not override equals().
*/
- @Override final public boolean equals(Object o) {
+ @Override final public boolean equals(@Nullable Object o) {
return super.equals(o);
}
diff --git a/core/java/android/app/HomeVisibilityListener.java b/core/java/android/app/HomeVisibilityListener.java
new file mode 100644
index 0000000..c6e5699
--- /dev/null
+++ b/core/java/android/app/HomeVisibilityListener.java
@@ -0,0 +1,112 @@
+/*
+ * 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.app;
+
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.Binder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * A listener that will be invoked when the visibility of the home screen changes.
+ * Register this callback via {@link ActivityManager#addHomeVisibilityListener}
+ * @hide
+ */
+// This is a single-method listener that needs a bunch of supporting code, so it can't be an
+// interface
+@SuppressLint("ListenerInterface")
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@TestApi
+public abstract class HomeVisibilityListener {
+ private Context mContext;
+ private ActivityManager mActivityManager;
+ private Executor mExecutor;
+ /** @hide */
+ android.app.IProcessObserver.Stub mObserver;
+ /** @hide */
+ boolean mIsHomeActivityVisible;
+
+ /** @hide */
+ void init(Context context, Executor executor, ActivityManager activityManager) {
+ mContext = context;
+ mActivityManager = activityManager;
+ mIsHomeActivityVisible = isHomeActivityVisible();
+ mExecutor = executor;
+ }
+
+ /**
+ * Called when the visibility of the home screen changes.
+ *
+ * @param isHomeActivityVisible Whether the home screen activity is now visible.
+ */
+ public abstract void onHomeVisibilityChanged(boolean isHomeActivityVisible);
+
+ public HomeVisibilityListener() {
+ mObserver = new android.app.IProcessObserver.Stub() {
+ @Override
+ public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
+ refreshHomeVisibility();
+ }
+
+ @Override
+ public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) {
+ }
+
+ @Override
+ public void onProcessDied(int pid, int uid) {
+ refreshHomeVisibility();
+ }
+
+ private void refreshHomeVisibility() {
+ boolean isHomeActivityVisible = isHomeActivityVisible();
+ if (mIsHomeActivityVisible != isHomeActivityVisible) {
+ mIsHomeActivityVisible = isHomeActivityVisible;
+ Binder.withCleanCallingIdentity(() ->
+ mExecutor.execute(() ->
+ onHomeVisibilityChanged(mIsHomeActivityVisible)));
+ }
+ }
+ };
+ }
+
+ private boolean isHomeActivityVisible() {
+ List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
+ if (tasks == null || tasks.isEmpty()) {
+ return false;
+ }
+
+ String top = tasks.get(0).topActivity.getPackageName();
+ if (top == null) {
+ return false;
+ }
+
+ // We can assume that the screen is idle if the home application is in the foreground.
+ String defaultHomePackage = mContext.getPackageManager()
+ .getHomeActivities(new ArrayList<>()).getPackageName();
+ if (Objects.equals(top, defaultHomePackage)) {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/core/java/android/app/HomeVisibilityObserver.java b/core/java/android/app/HomeVisibilityObserver.java
deleted file mode 100644
index 8422c6f..0000000
--- a/core/java/android/app/HomeVisibilityObserver.java
+++ /dev/null
@@ -1,97 +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.app;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-
-import java.util.List;
-
-/**
- * An observer / callback to create and register by
- * {@link ActivityManager#registerHomeVisibilityObserver} so that it's triggered when
- * visibility of home page changes.
- * TODO: b/144351078 expose as SystemApi
- * @hide
- */
-public abstract class HomeVisibilityObserver {
- private Context mContext;
- private ActivityManager mActivityManager;
- /** @hide */
- IProcessObserver.Stub mObserver;
- /** @hide */
- boolean mIsHomeActivityVisible;
-
- /** @hide */
- void init(Context context, ActivityManager activityManager) {
- mContext = context;
- mActivityManager = activityManager;
- mIsHomeActivityVisible = isHomeActivityVisible();
- }
-
- /**
- * The API that needs implemented and will be triggered when activity on home page changes.
- */
- public abstract void onHomeVisibilityChanged(boolean isHomeActivityVisible);
-
- public HomeVisibilityObserver() {
- mObserver = new IProcessObserver.Stub() {
- @Override
- public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
- boolean isHomeActivityVisible = isHomeActivityVisible();
- if (mIsHomeActivityVisible != isHomeActivityVisible) {
- mIsHomeActivityVisible = isHomeActivityVisible;
- onHomeVisibilityChanged(mIsHomeActivityVisible);
- }
- }
-
- @Override
- public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) {
- }
-
- @Override
- public void onProcessDied(int pid, int uid) {
- }
- };
- }
-
- private boolean isHomeActivityVisible() {
- List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
- if (tasks == null || tasks.isEmpty()) {
- return false;
- }
-
- String top = tasks.get(0).topActivity.getPackageName();
- if (top == null) {
- return false;
- }
-
- // We can assume that the screen is idle if the home application is in the foreground.
- final Intent intent = new Intent(Intent.ACTION_MAIN, null);
- intent.addCategory(Intent.CATEGORY_HOME);
-
- ResolveInfo info = mContext.getPackageManager().resolveActivity(intent,
- PackageManager.MATCH_DEFAULT_ONLY);
- if (info != null && top.equals(info.activityInfo.packageName)) {
- return true;
- }
-
- return false;
- }
-}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 66007e5..7530229 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -165,7 +165,8 @@
int getTaskForActivity(in IBinder token, in boolean onlyRoot);
/** Finish all activities that were started for result from the specified activity. */
void finishSubActivity(in IBinder token, in String resultWho, int requestCode);
- ParceledListSlice getRecentTasks(int maxNum, int flags, int userId);
+ ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
+ int userId);
boolean willActivityBeVisible(in IBinder token);
void setRequestedOrientation(in IBinder token, int requestedOrientation);
int getRequestedOrientation(in IBinder token);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0d682d6..5438062 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2240,7 +2240,8 @@
.setTicker(tickerText)
.setContentTitle(contentTitle)
.setContentText(contentText)
- .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
+ .setContentIntent(PendingIntent.getActivity(
+ context, 0, contentIntent, PendingIntent.FLAG_MUTABLE))
.buildInto(this);
}
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index c827e60..1bfdaf5 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -1175,7 +1175,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NotificationChannel that = (NotificationChannel) o;
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 403fb3e..ec7fa33 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -15,6 +15,7 @@
*/
package android.app;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -291,7 +292,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NotificationChannelGroup that = (NotificationChannelGroup) o;
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index 55fff8b..0c8188b 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -116,7 +116,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HistoricalNotification that = (HistoricalNotification) o;
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index fe89366..aefaf78 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1567,8 +1567,20 @@
}
}
- /** @hide */
- public void setNotificationListenerAccessGranted(ComponentName listener, boolean granted) {
+ /**
+ * Grants/revokes Notification Listener access to the given component for current user.
+ * To grant access for a particular user, obtain this service by using the {@link Context}
+ * provided by {@link Context#createPackageContextAsUser}
+ *
+ * @param listener Name of component to grant/revoke access
+ * @param granted Grant/revoke access
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS)
+ public void setNotificationListenerAccessGranted(
+ @NonNull ComponentName listener, boolean granted) {
INotificationManager service = getService();
try {
service.setNotificationListenerAccessGranted(listener, granted);
@@ -1610,6 +1622,21 @@
}
}
+ /**
+ * Gets the list of enabled notification listener components for current user.
+ * To query for a particular user, obtain this service by using the {@link Context}
+ * provided by {@link Context#createPackageContextAsUser}
+ *
+ * @return the list of {@link ComponentName}s of the notification listeners
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS)
+ public @NonNull List<ComponentName> getEnabledNotificationListeners() {
+ return getEnabledNotificationListeners(mContext.getUserId());
+ }
+
/** @hide */
public List<ComponentName> getEnabledNotificationListeners(int userId) {
INotificationManager service = getService();
@@ -2000,7 +2027,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof Policy)) return false;
if (o == this) return true;
final Policy other = (Policy) o;
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index e8937a8..acc42dbc 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -363,6 +363,7 @@
* parameters. May return null only if {@link #FLAG_NO_CREATE} has been
* supplied.
*/
+ @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
public static PendingIntent getActivity(Context context, int requestCode,
Intent intent, @Flags int flags) {
return getActivity(context, requestCode, intent, flags, null);
@@ -489,6 +490,7 @@
* parameters. May return null only if {@link #FLAG_NO_CREATE} has been
* supplied.
*/
+ @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
public static PendingIntent getActivities(Context context, int requestCode,
@NonNull Intent[] intents, @Flags int flags) {
return getActivities(context, requestCode, intents, flags, null);
@@ -611,6 +613,7 @@
* parameters. May return null only if {@link #FLAG_NO_CREATE} has been
* supplied.
*/
+ @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
public static PendingIntent getBroadcast(Context context, int requestCode,
Intent intent, @Flags int flags) {
return getBroadcastAsUser(context, requestCode, intent, flags, context.getUser());
@@ -1236,7 +1239,7 @@
* operation.
*/
@Override
- public boolean equals(Object otherObj) {
+ public boolean equals(@Nullable Object otherObj) {
if (otherObj instanceof PendingIntent) {
return mTarget.asBinder().equals(((PendingIntent)otherObj)
.mTarget.asBinder());
diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java
index 63ef248..97a794d 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -138,7 +138,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof Person) {
final Person other = (Person) obj;
return Objects.equals(mName, other.mName)
diff --git a/core/java/android/app/Presentation.java b/core/java/android/app/Presentation.java
index 7a18b81..ad90364 100644
--- a/core/java/android/app/Presentation.java
+++ b/core/java/android/app/Presentation.java
@@ -16,30 +16,28 @@
package android.app;
-import static android.content.Context.DISPLAY_SERVICE;
-import static android.content.Context.WINDOW_SERVICE;
+import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
-import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.util.DisplayMetrics;
-import android.util.Log;
+import android.os.Looper;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;
-import android.view.WindowManagerImpl;
+import android.view.WindowManager.LayoutParams.WindowType;
+import com.android.internal.util.Preconditions;
/**
* Base class for presentations.
* <p>
@@ -153,11 +151,10 @@
public class Presentation extends Dialog {
private static final String TAG = "Presentation";
- private static final int MSG_CANCEL = 1;
-
private final Display mDisplay;
private final DisplayManager mDisplayManager;
- private final IBinder mToken = new Binder();
+ private final Handler mHandler = new Handler(Preconditions.checkNotNull(Looper.myLooper(),
+ "Presentation must be constructed on a looper thread."));
/**
* Creates a new presentation that is attached to the specified display
@@ -179,6 +176,11 @@
* @param outerContext The context of the application that is showing the presentation.
* The presentation will create its own context (see {@link #getContext()}) based
* on this context and information about the associated display.
+ * From {@link android.os.Build.VERSION_CODES#S}, the presentation will create its own window
+ * context based on this context, information about the associated display. Customizing window
+ * type by {@link Window#setType(int) #getWindow#setType(int)} causes the mismatch of the window
+ * and the created window context, which leads to
+ * {@link android.view.WindowManager.InvalidDisplayException} when invoking {@link #show()}.
* @param display The display to which the presentation should be attached.
* @param theme A style resource describing the theme to use for the window.
* See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">
@@ -187,24 +189,53 @@
* <var>outerContext</var>. If 0, the default presentation theme will be used.
*/
public Presentation(Context outerContext, Display display, int theme) {
- super(createPresentationContext(outerContext, display, theme), theme, false);
+ this(outerContext, display, theme, INVALID_WINDOW_TYPE);
+ }
+
+ /**
+ * Creates a new presentation that is attached to the specified display
+ * using the optionally specified theme, and override the default window type for the
+ * presentation.
+ * @param outerContext The context of the application that is showing the presentation.
+ * The presentation will create its own context (see {@link #getContext()}) based
+ * on this context and information about the associated display.
+ * From {@link android.os.Build.VERSION_CODES#S}, the presentation will create its own window
+ * context based on this context, information about the associated display and the window type.
+ * If the window type is not specified, the presentation will choose the default type for the
+ * presentation.
+ * @param display The display to which the presentation should be attached.
+ * @param theme A style resource describing the theme to use for the window.
+ * See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">
+ * Style and Theme Resources</a> for more information about defining and using
+ * styles. This theme is applied on top of the current theme in
+ * <var>outerContext</var>. If 0, the default presentation theme will be used.
+ * @param type Window type.
+ *
+ * @hide
+ */
+ public Presentation(@NonNull Context outerContext, @NonNull Display display, int theme,
+ @WindowType int type) {
+ super(createPresentationContext(outerContext, display, theme, type), theme, false);
mDisplay = display;
- mDisplayManager = (DisplayManager)getContext().getSystemService(DISPLAY_SERVICE);
-
- final int windowType =
- (display.getFlags() & Display.FLAG_PRIVATE) != 0 ? TYPE_PRIVATE_PRESENTATION
- : TYPE_PRESENTATION;
+ mDisplayManager = getContext().getSystemService(DisplayManager.class);
final Window w = getWindow();
final WindowManager.LayoutParams attr = w.getAttributes();
- attr.token = mToken;
w.setAttributes(attr);
w.setGravity(Gravity.FILL);
- w.setType(windowType);
+ w.setType(getWindowType(type, display));
setCanceledOnTouchOutside(false);
}
+ private static @WindowType int getWindowType(@WindowType int type, @NonNull Display display) {
+ if (type != INVALID_WINDOW_TYPE) {
+ return type;
+ }
+ return (display.getFlags() & Display.FLAG_PRIVATE) != 0 ? TYPE_PRIVATE_PRESENTATION
+ : TYPE_PRESENTATION;
+ }
+
/**
* Gets the {@link Display} that this presentation appears on.
*
@@ -229,16 +260,6 @@
protected void onStart() {
super.onStart();
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
-
- // Since we were not watching for display changes until just now, there is a
- // chance that the display metrics have changed. If so, we will need to
- // dismiss the presentation immediately. This case is expected
- // to be rare but surprising, so we'll write a log message about it.
- if (!isConfigurationStillValid()) {
- Log.i(TAG, "Presentation is being dismissed because the "
- + "display metrics have changed since it was created.");
- mHandler.sendEmptyMessage(MSG_CANCEL);
- }
}
@Override
@@ -273,10 +294,6 @@
* Called by the system when the properties of the {@link Display} to which
* the presentation is attached have changed.
*
- * If the display metrics have changed (for example, if the display has been
- * resized or rotated), then the system automatically calls
- * {@link #cancel} to dismiss the presentation.
- *
* @see #getDisplay
*/
public void onDisplayChanged() {
@@ -289,28 +306,16 @@
private void handleDisplayChanged() {
onDisplayChanged();
-
- // We currently do not support configuration changes for presentations
- // (although we could add that feature with a bit more work).
- // If the display metrics have changed in any way then the current configuration
- // is invalid and the application must recreate the presentation to get
- // a new context.
- if (!isConfigurationStillValid()) {
- Log.i(TAG, "Presentation is being dismissed because the "
- + "display metrics have changed since it was created.");
- cancel();
- }
}
- private boolean isConfigurationStillValid() {
- DisplayMetrics dm = new DisplayMetrics();
- mDisplay.getMetrics(dm);
- return dm.equalsPhysical(getResources().getDisplayMetrics());
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@code N/A}")
+ private static Context createPresentationContext(Context outerContext, Display display,
+ int theme) {
+ return createPresentationContext(outerContext, display, theme, INVALID_WINDOW_TYPE);
}
- @UnsupportedAppUsage
private static Context createPresentationContext(
- Context outerContext, Display display, int theme) {
+ Context outerContext, Display display, int theme, @WindowType int type) {
if (outerContext == null) {
throw new IllegalArgumentException("outerContext must not be null");
}
@@ -318,31 +323,15 @@
throw new IllegalArgumentException("display must not be null");
}
- Context displayContext = outerContext.createDisplayContext(display);
+ Context windowContext = outerContext.createDisplayContext(display)
+ .createWindowContext(getWindowType(type, display), null /* options */);
if (theme == 0) {
TypedValue outValue = new TypedValue();
- displayContext.getTheme().resolveAttribute(
+ windowContext.getTheme().resolveAttribute(
com.android.internal.R.attr.presentationTheme, outValue, true);
theme = outValue.resourceId;
}
-
- // Derive the display's window manager from the outer window manager.
- // We do this because the outer window manager have some extra information
- // such as the parent window, which is important if the presentation uses
- // an application window type.
- final WindowManagerImpl outerWindowManager =
- (WindowManagerImpl)outerContext.getSystemService(WINDOW_SERVICE);
- final WindowManagerImpl displayWindowManager =
- outerWindowManager.createPresentationWindowManager(displayContext);
- return new ContextThemeWrapper(displayContext, theme) {
- @Override
- public Object getSystemService(String name) {
- if (WINDOW_SERVICE.equals(name)) {
- return displayWindowManager;
- }
- return super.getSystemService(name);
- }
- };
+ return new ContextThemeWrapper(windowContext, theme);
}
private final DisplayListener mDisplayListener = new DisplayListener() {
@@ -364,15 +353,4 @@
}
}
};
-
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_CANCEL:
- cancel();
- break;
- }
- }
- };
}
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index d6eb4a8..854406c 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -172,7 +173,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 3e3a956..04a12af 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -206,6 +206,10 @@
private static final String TAG = "PropertyInvalidatedCache";
private static final boolean DEBUG = false;
private static final boolean VERIFY = false;
+ // If this is true, dumpsys will dump the cache entries along with cache statistics.
+ // Most of the time this causes dumpsys to fail because the output stream is too
+ // large. Only set it to true in development images.
+ private static final boolean DETAILED = false;
// Per-Cache performance counters. As some cache instances are declared static,
@GuardedBy("mLock")
@@ -912,14 +916,13 @@
" Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d",
mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow));
pw.println(String.format(" Enabled: %s", mDisabled ? "false" : "true"));
+ pw.println("");
Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet();
- if (cacheEntries.size() == 0) {
- pw.println("");
+ if (!DETAILED || cacheEntries.size() == 0) {
return;
}
- pw.println("");
pw.println(" Contents:");
for (Map.Entry<Query, Result> entry : cacheEntries) {
String key = Objects.toString(entry.getKey());
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index b267840..36d5b5e 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -127,7 +127,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof ApkKey)) {
return false;
}
diff --git a/core/java/android/app/ResultInfo.java b/core/java/android/app/ResultInfo.java
index 979d3db..535f69f 100644
--- a/core/java/android/app/ResultInfo.java
+++ b/core/java/android/app/ResultInfo.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.os.Build;
@@ -90,7 +91,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj == null || !(obj instanceof ResultInfo)) {
return false;
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 90401ad..b020c70 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -81,6 +81,7 @@
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.IAuthService;
import android.hardware.camera2.CameraManager;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager;
@@ -1348,6 +1349,12 @@
throws ServiceNotFoundException {
return new DreamManager(ctx);
}});
+ registerService(Context.DEVICE_STATE_SERVICE, DeviceStateManager.class,
+ new CachedServiceFetcher<DeviceStateManager>() {
+ @Override
+ public DeviceStateManager createService(ContextImpl ctx) {
+ return new DeviceStateManager();
+ }});
sInitializing = true;
try {
@@ -1404,6 +1411,7 @@
case Context.CONTENT_CAPTURE_MANAGER_SERVICE:
case Context.APP_PREDICTION_SERVICE:
case Context.INCREMENTAL_SERVICE:
+ case Context.ETHERNET_SERVICE:
return null;
}
Slog.wtf(TAG, "Manager wrapper not available: " + name);
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 4718cf1..849f679 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -24,11 +24,14 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
import android.window.WindowContainerToken;
+import java.util.ArrayList;
+
/**
* Stores information about a particular Task.
*/
@@ -177,6 +180,13 @@
*/
public boolean isResizeable;
+ /**
+ * The launch cookies associated with activities in this task if any.
+ * @see ActivityOptions#setLaunchCookie(IBinder)
+ * @hide
+ */
+ public ArrayList<IBinder> launchCookies = new ArrayList<>();
+
TaskInfo() {
// Do nothing
}
@@ -213,6 +223,11 @@
return configuration;
}
+ /** @hide */
+ public void addLaunchCookie(IBinder cookie) {
+ launchCookies.add(cookie);
+ }
+
/**
* Reads the TaskInfo from a parcel.
*/
@@ -222,9 +237,7 @@
taskId = source.readInt();
displayId = source.readInt();
isRunning = source.readBoolean();
- baseIntent = source.readInt() != 0
- ? Intent.CREATOR.createFromParcel(source)
- : null;
+ baseIntent = source.readTypedObject(Intent.CREATOR);
baseActivity = ComponentName.readFromParcel(source);
topActivity = ComponentName.readFromParcel(source);
origActivity = ComponentName.readFromParcel(source);
@@ -233,21 +246,16 @@
numActivities = source.readInt();
lastActiveTime = source.readLong();
- taskDescription = source.readInt() != 0
- ? ActivityManager.TaskDescription.CREATOR.createFromParcel(source)
- : null;
+ taskDescription = source.readTypedObject(ActivityManager.TaskDescription.CREATOR);
supportsSplitScreenMultiWindow = source.readBoolean();
resizeMode = source.readInt();
configuration.readFromParcel(source);
token = WindowContainerToken.CREATOR.createFromParcel(source);
topActivityType = source.readInt();
- pictureInPictureParams = source.readInt() != 0
- ? PictureInPictureParams.CREATOR.createFromParcel(source)
- : null;
- topActivityInfo = source.readInt() != 0
- ? ActivityInfo.CREATOR.createFromParcel(source)
- : null;
+ pictureInPictureParams = source.readTypedObject(PictureInPictureParams.CREATOR);
+ topActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
isResizeable = source.readBoolean();
+ source.readBinderList(launchCookies);
}
/**
@@ -259,13 +267,8 @@
dest.writeInt(taskId);
dest.writeInt(displayId);
dest.writeBoolean(isRunning);
+ dest.writeTypedObject(baseIntent, 0);
- if (baseIntent != null) {
- dest.writeInt(1);
- baseIntent.writeToParcel(dest, 0);
- } else {
- dest.writeInt(0);
- }
ComponentName.writeToParcel(baseActivity, dest);
ComponentName.writeToParcel(topActivity, dest);
ComponentName.writeToParcel(origActivity, dest);
@@ -274,30 +277,16 @@
dest.writeInt(numActivities);
dest.writeLong(lastActiveTime);
- if (taskDescription != null) {
- dest.writeInt(1);
- taskDescription.writeToParcel(dest, flags);
- } else {
- dest.writeInt(0);
- }
+ dest.writeTypedObject(taskDescription, flags);
dest.writeBoolean(supportsSplitScreenMultiWindow);
dest.writeInt(resizeMode);
configuration.writeToParcel(dest, flags);
token.writeToParcel(dest, flags);
dest.writeInt(topActivityType);
- if (pictureInPictureParams == null) {
- dest.writeInt(0);
- } else {
- dest.writeInt(1);
- pictureInPictureParams.writeToParcel(dest, flags);
- }
- if (topActivityInfo == null) {
- dest.writeInt(0);
- } else {
- dest.writeInt(1);
- topActivityInfo.writeToParcel(dest, flags);
- }
+ dest.writeTypedObject(pictureInPictureParams, flags);
+ dest.writeTypedObject(topActivityInfo, flags);
dest.writeBoolean(isResizeable);
+ dest.writeBinderList(launchCookies);
}
@Override
@@ -316,6 +305,7 @@
+ " token=" + token
+ " topActivityType=" + topActivityType
+ " pictureInPictureParams=" + pictureInPictureParams
- + " topActivityInfo=" + topActivityInfo;
+ + " topActivityInfo=" + topActivityInfo
+ + " launchCookies" + launchCookies;
}
}
diff --git a/core/java/android/app/TaskStackBuilder.java b/core/java/android/app/TaskStackBuilder.java
index b99b327..e238046 100644
--- a/core/java/android/app/TaskStackBuilder.java
+++ b/core/java/android/app/TaskStackBuilder.java
@@ -264,6 +264,7 @@
*
* @return The obtained PendingIntent
*/
+ @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
public PendingIntent getPendingIntent(int requestCode, @PendingIntent.Flags int flags,
Bundle options) {
if (mIntents.isEmpty()) {
@@ -278,6 +279,7 @@
/**
* @hide
*/
+ @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
public PendingIntent getPendingIntent(int requestCode, int flags, Bundle options,
UserHandle user) {
if (mIntents.isEmpty()) {
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index 7624f35..2d203f57 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -337,7 +337,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == null || getClass() != o.getClass()) {
return false;
}
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 79f05a3..4ae1670 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -639,7 +639,7 @@
/** @hide */
@Override
- public boolean equals(Object that) {
+ public boolean equals(@Nullable Object that) {
if (that == null) return false;
if (that == this) return true;
if (!(that instanceof WindowConfiguration)) {
@@ -852,15 +852,6 @@
}
/**
- * Returns true if this container may be scaled without resizing, and windows within may need
- * to be configured as such.
- * @hide
- */
- public boolean windowsAreScaleable() {
- return mWindowingMode == WINDOWING_MODE_PINNED;
- }
-
- /**
* Returns true if windows in this container should be given move animations by default.
* @hide
*/
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index 39e1f0d..20a60bb 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -44,6 +44,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.os.Parcel;
import android.os.Parcelable;
@@ -705,7 +706,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final PasswordMetrics that = (PasswordMetrics) o;
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index fb7f573..5a4ab48 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -17,6 +17,7 @@
package android.app.admin;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -745,7 +746,7 @@
* @hide
*/
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SecurityEvent other = (SecurityEvent) o;
diff --git a/core/java/android/app/admin/SystemUpdateInfo.java b/core/java/android/app/admin/SystemUpdateInfo.java
index 4019290..53b2386 100644
--- a/core/java/android/app/admin/SystemUpdateInfo.java
+++ b/core/java/android/app/admin/SystemUpdateInfo.java
@@ -179,7 +179,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SystemUpdateInfo that = (SystemUpdateInfo) o;
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 0651271..7e232ac 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -1358,7 +1358,7 @@
/** @hide */
@Override
- public boolean equals(Object object) {
+ public boolean equals(@Nullable Object object) {
if (this == object) {
return true;
}
diff --git a/core/java/android/app/compat/ChangeIdStateQuery.java b/core/java/android/app/compat/ChangeIdStateQuery.java
index 3c245b1..91765f7 100644
--- a/core/java/android/app/compat/ChangeIdStateQuery.java
+++ b/core/java/android/app/compat/ChangeIdStateQuery.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import com.android.internal.annotations.Immutable;
@@ -25,7 +26,6 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
-
/**
* A key type for caching calls to {@link com.android.internal.compat.IPlatformCompat}
*
@@ -68,7 +68,7 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 8a4bee9..d5585db 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -20,6 +20,7 @@
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.content.res.Configuration;
@@ -107,7 +108,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index 87ea3f8..b3e34b3 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -19,6 +19,7 @@
import static android.app.ActivityThread.DEBUG_ORDER;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.ResultInfo;
@@ -144,7 +145,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
index 47096a8..8320f49 100644
--- a/core/java/android/app/servertransaction/ActivityResultItem.java
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -19,6 +19,7 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.ResultInfo;
@@ -101,7 +102,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 3d04437..90890d5 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -222,7 +222,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index 0f244d0..2acff49 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -16,6 +16,7 @@
package android.app.servertransaction;
+import android.annotation.Nullable;
import android.app.ClientTransactionHandler;
import android.content.res.Configuration;
import android.os.IBinder;
@@ -90,7 +91,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index 1611369..a074286 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -19,6 +19,7 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
@@ -106,7 +107,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/EnterPipRequestedItem.java b/core/java/android/app/servertransaction/EnterPipRequestedItem.java
index b7e81a5..7dcae65 100644
--- a/core/java/android/app/servertransaction/EnterPipRequestedItem.java
+++ b/core/java/android/app/servertransaction/EnterPipRequestedItem.java
@@ -16,6 +16,7 @@
package android.app.servertransaction;
+import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.Parcel;
@@ -67,7 +68,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return this == o;
}
diff --git a/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java b/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java
index 6183d5f..4c06333 100644
--- a/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java
+++ b/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java
@@ -16,6 +16,7 @@
package android.app.servertransaction;
+import android.annotation.Nullable;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
import android.os.Parcel;
@@ -74,7 +75,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 77457af..7f08bfb 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -19,6 +19,7 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.ProfilerInfo;
@@ -176,7 +177,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 32de53f..944367e 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -19,6 +19,7 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.content.res.Configuration;
@@ -110,7 +111,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
index b4e2a7b..ac57f2b 100644
--- a/core/java/android/app/servertransaction/NewIntentItem.java
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -20,6 +20,7 @@
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.compat.annotation.UnsupportedAppUsage;
@@ -107,7 +108,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index cb154e9..f7c645e 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -19,6 +19,7 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
@@ -143,7 +144,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index d2a156c..b4523f5 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -19,6 +19,7 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.ActivityThread.ActivityClientRecord;
@@ -142,7 +143,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/StartActivityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java
index ae0bd24..483f9de 100644
--- a/core/java/android/app/servertransaction/StartActivityItem.java
+++ b/core/java/android/app/servertransaction/StartActivityItem.java
@@ -19,6 +19,7 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.Parcel;
@@ -92,7 +93,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index 7708104..7e9116d 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -19,6 +19,7 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
@@ -107,7 +108,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
index 345c1dd..2b0c1b9 100644
--- a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
+++ b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
@@ -18,6 +18,7 @@
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
@@ -111,7 +112,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/slice/SliceSpec.java b/core/java/android/app/slice/SliceSpec.java
index b1080e0..a332349 100644
--- a/core/java/android/app/slice/SliceSpec.java
+++ b/core/java/android/app/slice/SliceSpec.java
@@ -17,6 +17,7 @@
package android.app.slice;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -97,7 +98,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof SliceSpec)) return false;
SliceSpec other = (SliceSpec) obj;
return mType.equals(other.mType) && mRevision == other.mRevision;
diff --git a/core/java/android/app/time/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java
index df89c28..7cc67b3 100644
--- a/core/java/android/app/time/TimeZoneCapabilities.java
+++ b/core/java/android/app/time/TimeZoneCapabilities.java
@@ -205,7 +205,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
index b339e53..f9a0c74 100644
--- a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
+++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
@@ -17,6 +17,7 @@
package android.app.time;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -93,7 +94,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/time/TimeZoneConfiguration.java b/core/java/android/app/time/TimeZoneConfiguration.java
index c0a0c21..7403c12 100644
--- a/core/java/android/app/time/TimeZoneConfiguration.java
+++ b/core/java/android/app/time/TimeZoneConfiguration.java
@@ -17,6 +17,7 @@
package android.app.time;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.os.Bundle;
@@ -155,7 +156,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java
index da51ce2..299e951 100644
--- a/core/java/android/app/timedetector/ManualTimeSuggestion.java
+++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java
@@ -109,7 +109,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
index 89fd6f31..a5259c2 100644
--- a/core/java/android/app/timedetector/NetworkTimeSuggestion.java
+++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
@@ -109,7 +109,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java
index c0e8957..6c3a304 100644
--- a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java
+++ b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java
@@ -154,7 +154,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/timezone/DistroFormatVersion.java b/core/java/android/app/timezone/DistroFormatVersion.java
index 04e3142..13ecaf5 100644
--- a/core/java/android/app/timezone/DistroFormatVersion.java
+++ b/core/java/android/app/timezone/DistroFormatVersion.java
@@ -16,6 +16,7 @@
package android.app.timezone;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -86,7 +87,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/timezone/DistroRulesVersion.java b/core/java/android/app/timezone/DistroRulesVersion.java
index 3fae161..54937b8 100644
--- a/core/java/android/app/timezone/DistroRulesVersion.java
+++ b/core/java/android/app/timezone/DistroRulesVersion.java
@@ -19,6 +19,7 @@
import static android.app.timezone.Utils.validateRulesVersion;
import static android.app.timezone.Utils.validateVersion;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -94,7 +95,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java
index d35967d..ee88ec54 100644
--- a/core/java/android/app/timezone/RulesState.java
+++ b/core/java/android/app/timezone/RulesState.java
@@ -223,7 +223,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
index 002c663..01a60b1 100644
--- a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
@@ -105,7 +105,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java
index 430462b..eb6750f 100644
--- a/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java
@@ -263,7 +263,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/app/usage/CacheQuotaHint.java b/core/java/android/app/usage/CacheQuotaHint.java
index b5aed49f..d5c72f2 100644
--- a/core/java/android/app/usage/CacheQuotaHint.java
+++ b/core/java/android/app/usage/CacheQuotaHint.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,6 +33,7 @@
* allows apps to understand how much cache to use.
* {@hide}
*/
+@TestApi
@SystemApi
public final class CacheQuotaHint implements Parcelable {
public static final long QUOTA_NOT_SET = -1;
@@ -44,14 +46,14 @@
* Create a new request.
* @param builder A builder for this object.
*/
- public CacheQuotaHint(Builder builder) {
+ public CacheQuotaHint(@NonNull Builder builder) {
this.mUuid = builder.mUuid;
this.mUid = builder.mUid;
this.mUsageStats = builder.mUsageStats;
this.mQuota = builder.mQuota;
}
- public String getVolumeUuid() {
+ @Nullable public String getVolumeUuid() {
return mUuid;
}
@@ -63,12 +65,12 @@
return mQuota;
}
- public UsageStats getUsageStats() {
+ @Nullable public UsageStats getUsageStats() {
return mUsageStats;
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mUuid);
dest.writeInt(mUid);
dest.writeLong(mQuota);
@@ -106,7 +108,7 @@
public Builder() {
}
- public Builder(CacheQuotaHint hint) {
+ public Builder(@NonNull CacheQuotaHint hint) {
setVolumeUuid(hint.getVolumeUuid());
setUid(hint.getUid());
setUsageStats(hint.getUsageStats());
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index f2a054d..dcecd90 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -30,6 +30,7 @@
import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Bundle;
@@ -155,6 +156,7 @@
/**
* {@hide}
*/
+ @TestApi
public UsageStats() {
}
diff --git a/core/java/android/bluetooth/BluetoothAudioConfig.java b/core/java/android/bluetooth/BluetoothAudioConfig.java
index 9591a70..4c8b8c1 100644
--- a/core/java/android/bluetooth/BluetoothAudioConfig.java
+++ b/core/java/android/bluetooth/BluetoothAudioConfig.java
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -39,7 +40,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothAudioConfig) {
BluetoothAudioConfig bac = (BluetoothAudioConfig) o;
return (bac.mSampleRate == mSampleRate && bac.mChannelConfig == mChannelConfig
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 905b0cee..603a7ff 100755
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -72,7 +73,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothClass) {
return mClass == ((BluetoothClass) o).mClass;
}
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
index a52fc89..1d0bf97 100644
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -208,7 +209,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothCodecConfig) {
BluetoothCodecConfig other = (BluetoothCodecConfig) o;
return (other.mCodecType == mCodecType
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
index 7b567b4..7764ebe 100644
--- a/core/java/android/bluetooth/BluetoothCodecStatus.java
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.java
@@ -56,7 +56,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothCodecStatus) {
BluetoothCodecStatus other = (BluetoothCodecStatus) o;
return (Objects.equals(other.mCodecConfig, mCodecConfig)
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 6287453..1b0fe9d 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -974,7 +974,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothDevice) {
return mAddress.equals(((BluetoothDevice) o).getAddress());
}
diff --git a/core/java/android/bluetooth/BluetoothMasInstance.java b/core/java/android/bluetooth/BluetoothMasInstance.java
index b64d049..eeaf085 100644
--- a/core/java/android/bluetooth/BluetoothMasInstance.java
+++ b/core/java/android/bluetooth/BluetoothMasInstance.java
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,7 +35,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof BluetoothMasInstance) {
return mId == ((BluetoothMasInstance) o).mId;
}
diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java
index 573b932..397326c 100644
--- a/core/java/android/bluetooth/le/AdvertiseData.java
+++ b/core/java/android/bluetooth/le/AdvertiseData.java
@@ -124,7 +124,7 @@
* @hide
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
index 7a8c2c6..54b953c 100644
--- a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
@@ -148,7 +148,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 7511fd0..51f63f7 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -479,7 +479,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/bluetooth/le/ScanResult.java b/core/java/android/bluetooth/le/ScanResult.java
index 855d345..57dad1a 100644
--- a/core/java/android/bluetooth/le/ScanResult.java
+++ b/core/java/android/bluetooth/le/ScanResult.java
@@ -309,7 +309,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 1f57c7d..c55f0ff 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -73,7 +73,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AssociationRequest that = (AssociationRequest) o;
diff --git a/core/java/android/companion/BluetoothDeviceFilter.java b/core/java/android/companion/BluetoothDeviceFilter.java
index cf9eeca..48dab3b 100644
--- a/core/java/android/companion/BluetoothDeviceFilter.java
+++ b/core/java/android/companion/BluetoothDeviceFilter.java
@@ -126,7 +126,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BluetoothDeviceFilter that = (BluetoothDeviceFilter) o;
diff --git a/core/java/android/companion/BluetoothLeDeviceFilter.java b/core/java/android/companion/BluetoothLeDeviceFilter.java
index 8c071fe..784e3a0 100644
--- a/core/java/android/companion/BluetoothLeDeviceFilter.java
+++ b/core/java/android/companion/BluetoothLeDeviceFilter.java
@@ -184,7 +184,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BluetoothLeDeviceFilter that = (BluetoothLeDeviceFilter) o;
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index da4980b..e94556b 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -131,7 +131,7 @@
* you use the {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND} and {@link
* android.Manifest.permission#REQUEST_COMPANION_USE_DATA_IN_BACKGROUND} respectively. Note that these
* special capabilities have a negative effect on the device's battery and user's data
- * usage, therefore you should requested them when absolutely necessary.</p>
+ * usage, therefore you should request them when absolutely necessary.</p>
*
* <p>You can call {@link #getAssociations} to get the list of currently associated
* devices, and {@link #disassociate} to remove an association. Consider doing so when the
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index 1967a01..b1cee0c 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -312,7 +312,7 @@
* same name, and if the classes that implement each component also have the same name.
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
try {
if (obj != null) {
ComponentName other = (ComponentName)obj;
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index f9f4c5d..02a5ba1 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -93,7 +93,7 @@
}
@Override
- public boolean equals(Object object) {
+ public boolean equals(@Nullable Object object) {
if (!(object instanceof ContentValues)) {
return false;
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9c216a3..c4157cf 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3508,6 +3508,7 @@
PERMISSION_SERVICE,
LIGHTS_SERVICE,
//@hide: PEOPLE_SERVICE,
+ //@hide: DEVICE_STATE_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -5246,6 +5247,14 @@
public static final String PEOPLE_SERVICE = "people";
/**
+ * Use with {@link #getSystemService(String)} to access device state service.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ public static final String DEVICE_STATE_SERVICE = "device_state";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2f1254f..5622cca 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -10373,7 +10373,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof FilterComparison) {
Intent other = ((FilterComparison)obj).mIntent;
return mIntent.filterEquals(other);
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 7fe29a9..5240ab4 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -1144,7 +1144,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof AuthorityEntry) {
final AuthorityEntry other = (AuthorityEntry)obj;
return match(other);
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index f40dc29..858d1e4 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -16,6 +16,7 @@
package android.content;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
@@ -27,7 +28,6 @@
import android.os.UserHandle;
import android.util.AndroidException;
-
/**
* A description of an Intent and target action to perform with it.
* The returned object can be
@@ -284,7 +284,7 @@
* same package.
*/
@Override
- public boolean equals(Object otherObj) {
+ public boolean equals(@Nullable Object otherObj) {
if (otherObj instanceof IntentSender) {
return mTarget.asBinder().equals(((IntentSender)otherObj)
.mTarget.asBinder());
diff --git a/core/java/android/content/LocusId.java b/core/java/android/content/LocusId.java
index 98e71f0..a04fbd1 100644
--- a/core/java/android/content/LocusId.java
+++ b/core/java/android/content/LocusId.java
@@ -16,6 +16,7 @@
package android.content;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.contentcapture.ContentCaptureManager;
@@ -92,7 +93,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java
index a075148..432e81b 100644
--- a/core/java/android/content/PeriodicSync.java
+++ b/core/java/android/content/PeriodicSync.java
@@ -16,10 +16,11 @@
package android.content;
-import android.os.Parcelable;
+import android.accounts.Account;
+import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Parcel;
-import android.accounts.Account;
+import android.os.Parcelable;
import java.util.Objects;
@@ -117,7 +118,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == this) {
return true;
}
diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
index 8f08fde..63fcb49 100644
--- a/core/java/android/content/RestrictionEntry.java
+++ b/core/java/android/content/RestrictionEntry.java
@@ -17,6 +17,7 @@
package android.content;
import android.annotation.ArrayRes;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -452,7 +453,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == this) return true;
if (!(o instanceof RestrictionEntry)) return false;
final RestrictionEntry other = (RestrictionEntry) o;
diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java
index 7bcdbfd..ffcdf53 100644
--- a/core/java/android/content/SyncAdapterType.java
+++ b/core/java/android/content/SyncAdapterType.java
@@ -176,7 +176,7 @@
return new SyncAdapterType(authority, accountType);
}
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == this) return true;
if (!(o instanceof SyncAdapterType)) return false;
final SyncAdapterType other = (SyncAdapterType)o;
diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java
index f363a54..a64f393 100644
--- a/core/java/android/content/integrity/AtomicFormula.java
+++ b/core/java/android/content/integrity/AtomicFormula.java
@@ -20,6 +20,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -240,7 +241,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -426,7 +427,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -594,7 +595,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/content/integrity/CompoundFormula.java b/core/java/android/content/integrity/CompoundFormula.java
index 14b1197..160bec9 100644
--- a/core/java/android/content/integrity/CompoundFormula.java
+++ b/core/java/android/content/integrity/CompoundFormula.java
@@ -20,6 +20,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -158,7 +159,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/content/integrity/Rule.java b/core/java/android/content/integrity/Rule.java
index d29e6df..8f6d73f 100644
--- a/core/java/android/content/integrity/Rule.java
+++ b/core/java/android/content/integrity/Rule.java
@@ -20,6 +20,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
@@ -115,7 +116,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/content/om/OverlayableInfo.java b/core/java/android/content/om/OverlayableInfo.java
index 5923907..6bd42d9 100644
--- a/core/java/android/content/om/OverlayableInfo.java
+++ b/core/java/android/content/om/OverlayableInfo.java
@@ -82,7 +82,7 @@
@Override
@DataClass.Generated.Member
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
// You can override field equality logic by defining either of the methods like:
// boolean fieldNameEquals(OverlayableInfo other) { ... }
// boolean fieldNameEquals(FieldType otherValue) { ... }
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index ea4b762..b371141 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1419,6 +1419,13 @@
public static final class WindowLayout {
public WindowLayout(int width, float widthFraction, int height, float heightFraction,
int gravity, int minWidth, int minHeight) {
+ this(width, widthFraction, height, heightFraction, gravity, minWidth, minHeight,
+ null /* windowLayoutAffinity */);
+ }
+
+ /** @hide */
+ public WindowLayout(int width, float widthFraction, int height, float heightFraction,
+ int gravity, int minWidth, int minHeight, String windowLayoutAffinity) {
this.width = width;
this.widthFraction = widthFraction;
this.height = height;
@@ -1426,6 +1433,7 @@
this.gravity = gravity;
this.minWidth = minWidth;
this.minHeight = minHeight;
+ this.windowLayoutAffinity = windowLayoutAffinity;
}
/** @hide */
@@ -1506,6 +1514,8 @@
/**
* Affinity of window layout parameters. Activities with the same UID and window layout
* affinity will share the same window dimension record.
+ *
+ * @attr ref android.R.styleable#AndroidManifestLayout_windowLayoutAffinity
* @hide
*/
public String windowLayoutAffinity;
diff --git a/core/java/android/content/pm/KeySet.java b/core/java/android/content/pm/KeySet.java
index 5c1d35e..fd459e6 100644
--- a/core/java/android/content/pm/KeySet.java
+++ b/core/java/android/content/pm/KeySet.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.annotation.Nullable;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -45,7 +46,7 @@
/** @hide */
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof KeySet) {
KeySet ks = (KeySet) o;
return token == ks.token;
diff --git a/core/java/android/content/pm/ModuleInfo.java b/core/java/android/content/pm/ModuleInfo.java
index a6db662..a7306a3 100644
--- a/core/java/android/content/pm/ModuleInfo.java
+++ b/core/java/android/content/pm/ModuleInfo.java
@@ -130,7 +130,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof ModuleInfo)) {
return false;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1a992f5..72499d5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8271,7 +8271,7 @@
}
@Override
- public boolean equals(Object rval) {
+ public boolean equals(@Nullable Object rval) {
if (rval == null) {
return false;
}
@@ -8374,7 +8374,7 @@
}
@Override
- public boolean equals(Object rval) {
+ public boolean equals(@Nullable Object rval) {
if (rval == null) {
return false;
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e108451..177f691 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6473,7 +6473,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (!(o instanceof SigningDetails)) return false;
diff --git a/core/java/android/content/pm/PackageStats.java b/core/java/android/content/pm/PackageStats.java
index 7c12527..81cfc07 100644
--- a/core/java/android/content/pm/PackageStats.java
+++ b/core/java/android/content/pm/PackageStats.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.annotation.Nullable;
import android.app.usage.StorageStatsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -186,7 +187,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof PackageStats)) {
return false;
}
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 3ed21b0..2fca980 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -369,7 +369,7 @@
}
@Override
- final public boolean equals(Object obj) {
+ final public boolean equals(@Nullable Object obj) {
if (!(obj instanceof PackageUserState)) {
return false;
}
@@ -527,7 +527,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index b2875e9..fc5d945 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -248,7 +249,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
try {
if (obj != null) {
Signature other = (Signature)obj;
diff --git a/core/java/android/content/pm/VerifierDeviceIdentity.java b/core/java/android/content/pm/VerifierDeviceIdentity.java
index f29aaec..3ce0b65 100644
--- a/core/java/android/content/pm/VerifierDeviceIdentity.java
+++ b/core/java/android/content/pm/VerifierDeviceIdentity.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -194,7 +195,7 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (!(other instanceof VerifierDeviceIdentity)) {
return false;
}
diff --git a/core/java/android/content/pm/VersionedPackage.java b/core/java/android/content/pm/VersionedPackage.java
index 2987557..fa50fca 100644
--- a/core/java/android/content/pm/VersionedPackage.java
+++ b/core/java/android/content/pm/VersionedPackage.java
@@ -17,6 +17,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -96,7 +97,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return o instanceof VersionedPackage
&& ((VersionedPackage) o).mPackageName.equals(mPackageName)
&& ((VersionedPackage) o).mVersionCode == mVersionCode;
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 8c0bfef..511ee5d 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -368,8 +368,8 @@
}
result = intentResult;
} else if (!isReceiver && !isAlias && parser.getName().equals("layout")) {
- ParseResult<ActivityInfo.WindowLayout> layoutResult = parseLayout(resources, parser,
- input);
+ ParseResult<ActivityInfo.WindowLayout> layoutResult =
+ parseActivityWindowLayout(resources, parser, input);
if (layoutResult.isSuccess()) {
activity.windowLayout = layoutResult.getResult();
}
@@ -383,7 +383,8 @@
}
}
- ParseResult<ActivityInfo.WindowLayout> layoutResult = resolveWindowLayout(activity, input);
+ ParseResult<ActivityInfo.WindowLayout> layoutResult =
+ resolveActivityWindowLayout(activity, input);
if (layoutResult.isError()) {
return input.error(layoutResult);
}
@@ -468,7 +469,7 @@
}
@NonNull
- private static ParseResult<ActivityInfo.WindowLayout> parseLayout(Resources res,
+ private static ParseResult<ActivityInfo.WindowLayout> parseActivityWindowLayout(Resources res,
AttributeSet attrs, ParseInput input) {
TypedArray sw = res.obtainAttributes(attrs, R.styleable.AndroidManifestLayout);
try {
@@ -496,8 +497,13 @@
int minWidth = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minWidth, -1);
int minHeight = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minHeight,
-1);
- return input.success(new ActivityInfo.WindowLayout(width, widthFraction, height,
- heightFraction, gravity, minWidth, minHeight));
+ String windowLayoutAffinity =
+ sw.getNonConfigurationString(
+ R.styleable.AndroidManifestLayout_windowLayoutAffinity, 0);
+ final ActivityInfo.WindowLayout windowLayout = new ActivityInfo.WindowLayout(width,
+ widthFraction, height, heightFraction, gravity, minWidth, minHeight,
+ windowLayoutAffinity);
+ return input.success(windowLayout);
} finally {
sw.recycle();
}
@@ -509,7 +515,7 @@
* <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in
* Android R and some variants of pre-R.
*/
- private static ParseResult<ActivityInfo.WindowLayout> resolveWindowLayout(
+ private static ParseResult<ActivityInfo.WindowLayout> resolveActivityWindowLayout(
ParsedActivity activity, ParseInput input) {
// There isn't a metadata for us to fall back. Whatever is in layout is correct.
if (activity.metaData == null || !activity.metaData.containsKey(
@@ -528,9 +534,10 @@
if (layout == null) {
layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */,
-1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY,
- -1 /* minWidth */, -1 /* minHeight */);
+ -1 /* minWidth */, -1 /* minHeight */, windowLayoutAffinity);
+ } else {
+ layout.windowLayoutAffinity = windowLayoutAffinity;
}
- layout.windowLayoutAffinity = windowLayoutAffinity;
return input.success(layout);
}
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java
index ced3226..f99a0b1 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermission.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermission.java
@@ -54,22 +54,6 @@
this.parsedPermissionGroup = other.parsedPermissionGroup;
}
- public ParsedPermission(ParsedPermission other, PermissionInfo pendingPermissionInfo,
- String packageName, String name) {
- this(other);
-
- this.flags = pendingPermissionInfo.flags;
- this.descriptionRes = pendingPermissionInfo.descriptionRes;
-
- this.backgroundPermission = pendingPermissionInfo.backgroundPermission;
- this.group = pendingPermissionInfo.group;
- this.requestRes = pendingPermissionInfo.requestRes;
- this.protectionLevel = pendingPermissionInfo.protectionLevel;
-
- setName(name);
- setPackageName(packageName);
- }
-
public ParsedPermission setGroup(String group) {
this.group = TextUtils.safeIntern(group);
return this;
diff --git a/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java b/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java
index 421ae49..e7a554a 100644
--- a/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java
+++ b/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java
@@ -18,6 +18,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -122,7 +123,7 @@
@Override
@DataClass.Generated.Member
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
// You can override field equality logic by defining either of the methods like:
// boolean fieldNameEquals(SplitPermissionInfoParcelable other) { ... }
// boolean fieldNameEquals(FieldType otherValue) { ... }
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index c66c70a2..ccb8cdd 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -16,6 +16,7 @@
package android.content.res;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ApplicationInfo;
import android.graphics.Canvas;
@@ -549,7 +550,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 49f81f8..9f36344 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -2128,7 +2128,7 @@
return this.compareTo(that) == 0;
}
- public boolean equals(Object that) {
+ public boolean equals(@Nullable Object that) {
try {
return equals((Configuration)that);
} catch (ClassCastException e) {
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 0f1c876..c1f0a5f7 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1913,7 +1913,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index fcb80aa..05769dd 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -137,7 +137,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof ResourcesKey)) {
return false;
}
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index 2afb755..afa1c20 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -16,6 +16,8 @@
package android.database;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -23,6 +25,8 @@
import android.os.Bundle;
import java.io.Closeable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;
@@ -56,12 +60,23 @@
/** Value returned by {@link #getType(int)} if the specified column type is blob */
static final int FIELD_TYPE_BLOB = 4;
+ /** @hide */
+ @IntDef(prefix = { "FIELD_TYPE_" }, value = {
+ FIELD_TYPE_NULL,
+ FIELD_TYPE_INTEGER,
+ FIELD_TYPE_FLOAT,
+ FIELD_TYPE_STRING,
+ FIELD_TYPE_BLOB,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FieldType {}
+
/**
* Returns the numbers of rows in the cursor.
*
* @return the number of rows in the cursor.
*/
- int getCount();
+ @IntRange(from = 0) int getCount();
/**
* Returns the current position of the cursor in the row set.
@@ -72,7 +87,7 @@
*
* @return the current cursor position.
*/
- int getPosition();
+ @IntRange(from = -1) int getPosition();
/**
* Move the cursor by a relative amount, forward or backward, from the
@@ -101,7 +116,7 @@
* @param position the zero-based position to move to.
* @return whether the requested move fully succeeded.
*/
- boolean moveToPosition(int position);
+ boolean moveToPosition(@IntRange(from = -1) int position);
/**
* Move the cursor to the first row.
@@ -181,7 +196,7 @@
* the column name does not exist.
* @see #getColumnIndexOrThrow(String)
*/
- int getColumnIndex(String columnName);
+ @IntRange(from = -1) int getColumnIndex(String columnName);
/**
* Returns the zero-based index for the given column name, or throws
@@ -194,7 +209,8 @@
* @see #getColumnIndex(String)
* @throws IllegalArgumentException if the column does not exist
*/
- int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException;
+ @IntRange(from = 0) int getColumnIndexOrThrow(String columnName)
+ throws IllegalArgumentException;
/**
* Returns the column name at the given zero-based column index.
@@ -202,7 +218,7 @@
* @param columnIndex the zero-based index of the target column.
* @return the column name for the given column index.
*/
- String getColumnName(int columnIndex);
+ String getColumnName(@IntRange(from = 0) int columnIndex);
/**
* Returns a string array holding the names of all of the columns in the
@@ -216,7 +232,7 @@
* Return total number of columns
* @return number of columns
*/
- int getColumnCount();
+ @IntRange(from = 0) int getColumnCount();
/**
* Returns the value of the requested column as a byte array.
@@ -228,7 +244,7 @@
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a byte array.
*/
- byte[] getBlob(int columnIndex);
+ byte[] getBlob(@IntRange(from = 0) int columnIndex);
/**
* Returns the value of the requested column as a String.
@@ -240,7 +256,7 @@
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a String.
*/
- String getString(int columnIndex);
+ String getString(@IntRange(from = 0) int columnIndex);
/**
* Retrieves the requested column text and stores it in the buffer provided.
@@ -250,7 +266,7 @@
* if the target column is null, return buffer
* @param buffer the buffer to copy the text into.
*/
- void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer);
+ void copyStringToBuffer(@IntRange(from = 0) int columnIndex, CharArrayBuffer buffer);
/**
* Returns the value of the requested column as a short.
@@ -263,7 +279,7 @@
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a short.
*/
- short getShort(int columnIndex);
+ short getShort(@IntRange(from = 0) int columnIndex);
/**
* Returns the value of the requested column as an int.
@@ -276,7 +292,7 @@
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as an int.
*/
- int getInt(int columnIndex);
+ int getInt(@IntRange(from = 0) int columnIndex);
/**
* Returns the value of the requested column as a long.
@@ -289,7 +305,7 @@
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a long.
*/
- long getLong(int columnIndex);
+ long getLong(@IntRange(from = 0) int columnIndex);
/**
* Returns the value of the requested column as a float.
@@ -302,7 +318,7 @@
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a float.
*/
- float getFloat(int columnIndex);
+ float getFloat(@IntRange(from = 0) int columnIndex);
/**
* Returns the value of the requested column as a double.
@@ -315,28 +331,18 @@
* @param columnIndex the zero-based index of the target column.
* @return the value of that column as a double.
*/
- double getDouble(int columnIndex);
+ double getDouble(@IntRange(from = 0) int columnIndex);
/**
* Returns data type of the given column's value.
* The preferred type of the column is returned but the data may be converted to other types
* as documented in the get-type methods such as {@link #getInt(int)}, {@link #getFloat(int)}
* etc.
- *<p>
- * Returned column types are
- * <ul>
- * <li>{@link #FIELD_TYPE_NULL}</li>
- * <li>{@link #FIELD_TYPE_INTEGER}</li>
- * <li>{@link #FIELD_TYPE_FLOAT}</li>
- * <li>{@link #FIELD_TYPE_STRING}</li>
- * <li>{@link #FIELD_TYPE_BLOB}</li>
- *</ul>
- *</p>
*
* @param columnIndex the zero-based index of the target column.
* @return column value type
*/
- int getType(int columnIndex);
+ @FieldType int getType(@IntRange(from = 0) int columnIndex);
/**
* Returns <code>true</code> if the value in the indicated column is null.
@@ -344,7 +350,7 @@
* @param columnIndex the zero-based index of the target column.
* @return whether the column value is null.
*/
- boolean isNull(int columnIndex);
+ boolean isNull(@IntRange(from = 0) int columnIndex);
/**
* Deactivates the Cursor, making all calls on it fail until {@link #requery} is called.
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 063a2d0..ac05938 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -17,6 +17,7 @@
package android.database;
import android.annotation.BytesLong;
+import android.annotation.IntRange;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.database.sqlite.SQLiteClosable;
@@ -230,7 +231,7 @@
*
* @return The zero-based start position.
*/
- public int getStartPosition() {
+ public @IntRange(from = 0) int getStartPosition() {
return mStartPos;
}
@@ -243,7 +244,7 @@
*
* @param pos The new zero-based start position.
*/
- public void setStartPosition(int pos) {
+ public void setStartPosition(@IntRange(from = 0) int pos) {
mStartPos = pos;
}
@@ -252,7 +253,7 @@
*
* @return The number of rows in this cursor window.
*/
- public int getNumRows() {
+ public @IntRange(from = 0) int getNumRows() {
acquireReference();
try {
return nativeGetNumRows(mWindowPtr);
@@ -272,7 +273,7 @@
* @param columnNum The new number of columns.
* @return True if successful.
*/
- public boolean setNumColumns(int columnNum) {
+ public boolean setNumColumns(@IntRange(from = 0) int columnNum) {
acquireReference();
try {
return nativeSetNumColumns(mWindowPtr, columnNum);
@@ -317,7 +318,7 @@
* @deprecated Use {@link #getType(int, int)} instead.
*/
@Deprecated
- public boolean isNull(int row, int column) {
+ public boolean isNull(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
return getType(row, column) == Cursor.FIELD_TYPE_NULL;
}
@@ -332,7 +333,7 @@
* @deprecated Use {@link #getType(int, int)} instead.
*/
@Deprecated
- public boolean isBlob(int row, int column) {
+ public boolean isBlob(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
int type = getType(row, column);
return type == Cursor.FIELD_TYPE_BLOB || type == Cursor.FIELD_TYPE_NULL;
}
@@ -347,7 +348,7 @@
* @deprecated Use {@link #getType(int, int)} instead.
*/
@Deprecated
- public boolean isLong(int row, int column) {
+ public boolean isLong(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
return getType(row, column) == Cursor.FIELD_TYPE_INTEGER;
}
@@ -361,7 +362,7 @@
* @deprecated Use {@link #getType(int, int)} instead.
*/
@Deprecated
- public boolean isFloat(int row, int column) {
+ public boolean isFloat(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
return getType(row, column) == Cursor.FIELD_TYPE_FLOAT;
}
@@ -376,29 +377,20 @@
* @deprecated Use {@link #getType(int, int)} instead.
*/
@Deprecated
- public boolean isString(int row, int column) {
+ public boolean isString(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
int type = getType(row, column);
return type == Cursor.FIELD_TYPE_STRING || type == Cursor.FIELD_TYPE_NULL;
}
/**
* Returns the type of the field at the specified row and column index.
- * <p>
- * The returned field types are:
- * <ul>
- * <li>{@link Cursor#FIELD_TYPE_NULL}</li>
- * <li>{@link Cursor#FIELD_TYPE_INTEGER}</li>
- * <li>{@link Cursor#FIELD_TYPE_FLOAT}</li>
- * <li>{@link Cursor#FIELD_TYPE_STRING}</li>
- * <li>{@link Cursor#FIELD_TYPE_BLOB}</li>
- * </ul>
- * </p>
*
* @param row The zero-based row index.
* @param column The zero-based column index.
* @return The field type.
*/
- public int getType(int row, int column) {
+ public @Cursor.FieldType int getType(@IntRange(from = 0) int row,
+ @IntRange(from = 0) int column) {
acquireReference();
try {
return nativeGetType(mWindowPtr, row - mStartPos, column);
@@ -428,7 +420,7 @@
* @param column The zero-based column index.
* @return The value of the field as a byte array.
*/
- public byte[] getBlob(int row, int column) {
+ public byte[] getBlob(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
acquireReference();
try {
return nativeGetBlob(mWindowPtr, row - mStartPos, column);
@@ -463,7 +455,7 @@
* @param column The zero-based column index.
* @return The value of the field as a string.
*/
- public String getString(int row, int column) {
+ public String getString(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
acquireReference();
try {
return nativeGetString(mWindowPtr, row - mStartPos, column);
@@ -502,7 +494,8 @@
* @param buffer The {@link CharArrayBuffer} to hold the string. It is automatically
* resized if the requested string is larger than the buffer's current capacity.
*/
- public void copyStringToBuffer(int row, int column, CharArrayBuffer buffer) {
+ public void copyStringToBuffer(@IntRange(from = 0) int row, @IntRange(from = 0) int column,
+ CharArrayBuffer buffer) {
if (buffer == null) {
throw new IllegalArgumentException("CharArrayBuffer should not be null");
}
@@ -536,7 +529,7 @@
* @param column The zero-based column index.
* @return The value of the field as a <code>long</code>.
*/
- public long getLong(int row, int column) {
+ public long getLong(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
acquireReference();
try {
return nativeGetLong(mWindowPtr, row - mStartPos, column);
@@ -568,7 +561,7 @@
* @param column The zero-based column index.
* @return The value of the field as a <code>double</code>.
*/
- public double getDouble(int row, int column) {
+ public double getDouble(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
acquireReference();
try {
return nativeGetDouble(mWindowPtr, row - mStartPos, column);
@@ -589,7 +582,7 @@
* @param column The zero-based column index.
* @return The value of the field as a <code>short</code>.
*/
- public short getShort(int row, int column) {
+ public short getShort(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
return (short) getLong(row, column);
}
@@ -605,7 +598,7 @@
* @param column The zero-based column index.
* @return The value of the field as an <code>int</code>.
*/
- public int getInt(int row, int column) {
+ public int getInt(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
return (int) getLong(row, column);
}
@@ -621,7 +614,7 @@
* @param column The zero-based column index.
* @return The value of the field as an <code>float</code>.
*/
- public float getFloat(int row, int column) {
+ public float getFloat(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
return (float) getDouble(row, column);
}
@@ -633,7 +626,8 @@
* @param column The zero-based column index.
* @return True if successful.
*/
- public boolean putBlob(byte[] value, int row, int column) {
+ public boolean putBlob(byte[] value,
+ @IntRange(from = 0) int row, @IntRange(from = 0) int column) {
acquireReference();
try {
return nativePutBlob(mWindowPtr, value, row - mStartPos, column);
@@ -650,7 +644,8 @@
* @param column The zero-based column index.
* @return True if successful.
*/
- public boolean putString(String value, int row, int column) {
+ public boolean putString(String value,
+ @IntRange(from = 0) int row, @IntRange(from = 0) int column) {
acquireReference();
try {
return nativePutString(mWindowPtr, value, row - mStartPos, column);
@@ -667,7 +662,8 @@
* @param column The zero-based column index.
* @return True if successful.
*/
- public boolean putLong(long value, int row, int column) {
+ public boolean putLong(long value,
+ @IntRange(from = 0) int row, @IntRange(from = 0) int column) {
acquireReference();
try {
return nativePutLong(mWindowPtr, value, row - mStartPos, column);
@@ -685,7 +681,8 @@
* @param column The zero-based column index.
* @return True if successful.
*/
- public boolean putDouble(double value, int row, int column) {
+ public boolean putDouble(double value,
+ @IntRange(from = 0) int row, @IntRange(from = 0) int column) {
acquireReference();
try {
return nativePutDouble(mWindowPtr, value, row - mStartPos, column);
@@ -701,7 +698,7 @@
* @param column The zero-based column index.
* @return True if successful.
*/
- public boolean putNull(int row, int column) {
+ public boolean putNull(@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
acquireReference();
try {
return nativePutNull(mWindowPtr, row - mStartPos, column);
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 2159905..86031dd 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -2151,7 +2151,7 @@
* same as those of this size. {@code false} otherwise.
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof Size)) {
return false;
}
@@ -2222,7 +2222,7 @@
* the same as those of this area. {@code false} otherwise.
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof Area)) {
return false;
}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 8d0cc68..25c749b 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -16,14 +16,17 @@
package android.hardware.biometrics;
+import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.RemoteException;
import android.security.keystore.KeyGenParameterSpec;
@@ -32,6 +35,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
/**
* A class that contains biometric utilities. For authentication, see {@link BiometricPrompt}.
@@ -196,6 +201,28 @@
}
/**
+ * @return A list of {@link SensorProperties}
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ @RequiresPermission(TEST_BIOMETRIC)
+ public List<SensorProperties> getSensorProperties() {
+ return new ArrayList<>(); // TODO(169459906)
+ }
+
+ /**
+ * Retrieves a test session for BiometricManager/BiometricPrompt.
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ @RequiresPermission(TEST_BIOMETRIC)
+ public BiometricTestSession createTestSession(int sensorId) {
+ return null; // TODO(169459906)
+ }
+
+ /**
* Determine if biometrics can be used. In other words, determine if
* {@link BiometricPrompt} can be expected to be shown (hardware available, templates enrolled,
* user-enabled). This is the equivalent of {@link #canAuthenticate(int)} with
diff --git a/core/java/android/hardware/biometrics/BiometricTestSession.java b/core/java/android/hardware/biometrics/BiometricTestSession.java
new file mode 100644
index 0000000..4c7aa27
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricTestSession.java
@@ -0,0 +1,180 @@
+/*
+ * 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.hardware.biometrics;
+
+import static android.Manifest.permission.TEST_BIOMETRIC;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.ArraySet;
+
+/**
+ * Common set of interfaces to test biometric-related APIs, including {@link BiometricPrompt} and
+ * {@link android.hardware.fingerprint.FingerprintManager}.
+ * @hide
+ */
+@TestApi
+public class BiometricTestSession implements AutoCloseable {
+ private final Context mContext;
+ private final ITestSession mTestSession;
+
+ // Keep track of users that were tested, which need to be cleaned up when finishing.
+ private final ArraySet<Integer> mTestedUsers;
+
+ /**
+ * @hide
+ */
+ public BiometricTestSession(@NonNull Context context, @NonNull ITestSession testSession) {
+ mContext = context;
+ mTestSession = testSession;
+ mTestedUsers = new ArraySet<>();
+ enableTestHal(true);
+ }
+
+ /**
+ * Switches the specified sensor to use a test HAL. In this mode, the framework will not invoke
+ * any methods on the real HAL implementation. This allows the framework to test a substantial
+ * portion of the framework code that would otherwise require human interaction. Note that
+ * secure pathways such as HAT/Keystore are not testable, since they depend on the TEE or its
+ * equivalent for the secret key.
+ *
+ * @param enableTestHal If true, enable testing with a fake HAL instead of the real HAL.
+ */
+ @RequiresPermission(TEST_BIOMETRIC)
+ private void enableTestHal(boolean enableTestHal) {
+ try {
+ mTestSession.enableTestHal(enableTestHal);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Starts the enrollment process. This should generally be used when the test HAL is enabled.
+ *
+ * @param userId User that this command applies to.
+ */
+ @RequiresPermission(TEST_BIOMETRIC)
+ public void startEnroll(int userId) {
+ try {
+ mTestedUsers.add(userId);
+ mTestSession.startEnroll(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Finishes the enrollment process. Simulates the HAL's callback.
+ *
+ * @param userId User that this command applies to.
+ */
+ @RequiresPermission(TEST_BIOMETRIC)
+ public void finishEnroll(int userId) {
+ try {
+ mTestedUsers.add(userId);
+ mTestSession.finishEnroll(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Simulates a successful authentication, but does not provide a valid HAT.
+ *
+ * @param userId User that this command applies to.
+ */
+ @RequiresPermission(TEST_BIOMETRIC)
+ public void acceptAuthentication(int userId) {
+ try {
+ mTestSession.acceptAuthentication(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Simulates a rejected attempt.
+ *
+ * @param userId User that this command applies to.
+ */
+ @RequiresPermission(TEST_BIOMETRIC)
+ public void rejectAuthentication(int userId) {
+ try {
+ mTestSession.rejectAuthentication(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Simulates an acquired message from the HAL.
+ *
+ * @param userId User that this command applies to.
+ */
+ @RequiresPermission(TEST_BIOMETRIC)
+ public void notifyAcquired(int userId) {
+ try {
+ mTestSession.notifyAcquired(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Simulates an error message from the HAL.
+ *
+ * @param userId User that this command applies to.
+ */
+ @RequiresPermission(TEST_BIOMETRIC)
+ public void notifyError(int userId) {
+ try {
+ mTestSession.notifyError(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Matches the framework's cached enrollments against the HAL's enrollments. Any enrollment
+ * that isn't known by both sides are deleted. This should generally be used when the test
+ * HAL is disabled (e.g. to clean up after a test).
+ *
+ * @param userId User that this command applies to.
+ */
+ @RequiresPermission(TEST_BIOMETRIC)
+ public void cleanupInternalState(int userId) {
+ try {
+ mTestSession.cleanupInternalState(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ @RequiresPermission(TEST_BIOMETRIC)
+ public void close() {
+ for (int user : mTestedUsers) {
+ cleanupInternalState(user);
+ }
+
+ enableTestHal(false);
+ }
+}
diff --git a/core/java/android/hardware/biometrics/ITestSession.aidl b/core/java/android/hardware/biometrics/ITestSession.aidl
new file mode 100644
index 0000000..5677f65
--- /dev/null
+++ b/core/java/android/hardware/biometrics/ITestSession.aidl
@@ -0,0 +1,54 @@
+/*
+ * 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.hardware.biometrics;
+
+import android.hardware.biometrics.SensorPropertiesInternal;
+
+/**
+ * A test service for FingerprintManager and BiometricPrompt.
+ * @hide
+ */
+interface ITestSession {
+ // Switches the specified sensor to use a test HAL. In this mode, the framework will not invoke
+ // any methods on the real HAL implementation. This allows the framework to test a substantial
+ // portion of the framework code that would otherwise require human interaction. Note that
+ // secure pathways such as HAT/Keystore are not testable, since they depend on the TEE or its
+ // equivalent for the secret key.
+ void enableTestHal(boolean enableTestHal);
+
+ // Starts the enrollment process. This should generally be used when the test HAL is enabled.
+ void startEnroll(int userId);
+
+ // Finishes the enrollment process. Simulates the HAL's callback.
+ void finishEnroll(int userId);
+
+ // Simulates a successful authentication, but does not provide a valid HAT.
+ void acceptAuthentication(int userId);
+
+ // Simulates a rejected attempt.
+ void rejectAuthentication(int userId);
+
+ // Simulates an acquired message from the HAL.
+ void notifyAcquired(int userId);
+
+ // Simulates an error message from the HAL.
+ void notifyError(int userId);
+
+ // Matches the framework's cached enrollments against the HAL's enrollments. Any enrollment
+ // that isn't known by both sides are deleted. This should generally be used when the test
+ // HAL is disabled (e.g. to clean up after a test).
+ void cleanupInternalState(int userId);
+}
diff --git a/core/java/android/hardware/biometrics/SensorProperties.java b/core/java/android/hardware/biometrics/SensorProperties.java
index b3dcb8f..5b1b5e6 100644
--- a/core/java/android/hardware/biometrics/SensorProperties.java
+++ b/core/java/android/hardware/biometrics/SensorProperties.java
@@ -17,6 +17,7 @@
package android.hardware.biometrics;
import android.annotation.IntDef;
+import android.annotation.TestApi;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -25,19 +26,18 @@
* The base class containing all modality-agnostic information.
* @hide
*/
+@TestApi
public class SensorProperties {
/**
* A sensor that meets the requirements for Class 1 biometrics as defined in the CDD. This does
* not correspond to a public BiometricManager.Authenticators constant. Sensors of this strength
* are not available to applications via the public API surface.
- * @hide
*/
public static final int STRENGTH_CONVENIENCE = 0;
/**
* A sensor that meets the requirements for Class 2 biometrics as defined in the CDD.
* Corresponds to BiometricManager.Authenticators.BIOMETRIC_WEAK.
- * @hide
*/
public static final int STRENGTH_WEAK = 1;
@@ -46,7 +46,6 @@
* Corresponds to BiometricManager.Authenticators.BIOMETRIC_STRONG.
*
* Notably, this is the only strength that allows generation of HardwareAuthToken(s).
- * @hide
*/
public static final int STRENGTH_STRONG = 2;
@@ -70,7 +69,6 @@
/**
* @return The sensor's unique identifier.
- * @hide
*/
public int getSensorId() {
return mSensorId;
@@ -78,7 +76,6 @@
/**
* @return The sensor's strength.
- * @hide
*/
@Strength
public int getSensorStrength() {
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/core/java/android/hardware/biometrics/SensorPropertiesInternal.aidl
similarity index 80%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to core/java/android/hardware/biometrics/SensorPropertiesInternal.aidl
index 71cd0a7..d85c788 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/core/java/android/hardware/biometrics/SensorPropertiesInternal.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 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.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.hardware.biometrics;
-package android.media.tv;
-
-parcelable TvChannelInfo;
+parcelable SensorPropertiesInternal;
\ No newline at end of file
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 0d0bfb3..47dafa0 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -528,7 +528,7 @@
* @return True if the requests are the same, false otherwise.
*/
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
return other instanceof CaptureRequest
&& equals((CaptureRequest)other);
}
diff --git a/core/java/android/hardware/camera2/marshal/MarshalRegistry.java b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java
index 1565087..0575f37 100644
--- a/core/java/android/hardware/camera2/marshal/MarshalRegistry.java
+++ b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java
@@ -15,6 +15,7 @@
*/
package android.hardware.camera2.marshal;
+import android.annotation.Nullable;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.utils.TypeReference;
@@ -114,7 +115,7 @@
private final int hash;
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (other instanceof MarshalToken<?>) {
MarshalToken<?> otherToken = (MarshalToken<?>)other;
return typeReference.equals(otherToken.typeReference) &&
diff --git a/core/java/android/hardware/camera2/params/BlackLevelPattern.java b/core/java/android/hardware/camera2/params/BlackLevelPattern.java
index c3dc25f..4405df8 100644
--- a/core/java/android/hardware/camera2/params/BlackLevelPattern.java
+++ b/core/java/android/hardware/camera2/params/BlackLevelPattern.java
@@ -16,6 +16,8 @@
package android.hardware.camera2.params;
+import android.annotation.Nullable;
+
import java.util.Arrays;
import java.util.Objects;
@@ -107,7 +109,7 @@
* @return {@code true} if the objects were equal, {@code false} otherwise
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj == null) {
return false;
} else if (this == obj) {
diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java
index d95f889..0a50f97 100644
--- a/core/java/android/hardware/camera2/params/InputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/InputConfiguration.java
@@ -16,6 +16,7 @@
package android.hardware.camera2.params;
+import android.annotation.Nullable;
import android.hardware.camera2.utils.HashCodeHelpers;
/**
@@ -90,7 +91,7 @@
* @return {@code true} if the objects were equal, {@code false} otherwise.
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof InputConfiguration)) {
return false;
}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 226b8e5..a20a1bf 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -17,6 +17,8 @@
package android.hardware.camera2.params;
+import static com.android.internal.util.Preconditions.*;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -31,8 +33,6 @@
import android.util.Size;
import android.view.Surface;
-import static com.android.internal.util.Preconditions.*;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -677,7 +677,7 @@
* @return {@code true} if the objects were equal, {@code false} otherwise
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj == null) {
return false;
} else if (this == obj) {
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 47a897c..8fc919f 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -22,6 +22,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
@@ -190,7 +191,7 @@
* @return {@code true} if the objects were equal, {@code false} otherwise
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj == null) {
return false;
} else if (this == obj) {
diff --git a/core/java/android/hardware/camera2/params/TonemapCurve.java b/core/java/android/hardware/camera2/params/TonemapCurve.java
index 90e6355..a11d2c7 100644
--- a/core/java/android/hardware/camera2/params/TonemapCurve.java
+++ b/core/java/android/hardware/camera2/params/TonemapCurve.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.Preconditions.*;
+import android.annotation.Nullable;
import android.graphics.PointF;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
@@ -249,7 +250,7 @@
* @return {@code true} if the objects were equal, {@code false} otherwise
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj == null) {
return false;
}
diff --git a/core/java/android/hardware/camera2/utils/TypeReference.java b/core/java/android/hardware/camera2/utils/TypeReference.java
index 435ed15..3c1d206 100644
--- a/core/java/android/hardware/camera2/utils/TypeReference.java
+++ b/core/java/android/hardware/camera2/utils/TypeReference.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import java.lang.reflect.Array;
@@ -244,7 +245,7 @@
* is also equal.</p>
*/
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
// Note that this comparison could inaccurately return true when comparing types
// with nested type variables; therefore we ban type variables in the constructor.
return o instanceof TypeReference<?> && mType.equals(((TypeReference<?>)o).mType);
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
new file mode 100644
index 0000000..a52f983
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -0,0 +1,38 @@
+/*
+ * 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.hardware.devicestate;
+
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * Manages the state of the system for devices with user-configurable hardware like a foldable
+ * phone.
+ *
+ * @hide
+ */
+@SystemService(Context.DEVICE_STATE_SERVICE)
+public final class DeviceStateManager {
+ /** Invalid device state. */
+ public static final int INVALID_DEVICE_STATE = -1;
+
+ private DeviceStateManagerGlobal mGlobal;
+
+ public DeviceStateManager() {
+ mGlobal = DeviceStateManagerGlobal.getInstance();
+ }
+}
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
new file mode 100644
index 0000000..4e7cf4a
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -0,0 +1,58 @@
+/*
+ * 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.hardware.devicestate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.ServiceManager;
+
+/**
+ * Provides communication with the device state system service on behalf of applications.
+ *
+ * @see DeviceStateManager
+ * @hide
+ */
+final class DeviceStateManagerGlobal {
+ private static DeviceStateManagerGlobal sInstance;
+
+ /**
+ * Returns an instance of {@link DeviceStateManagerGlobal}. May return {@code null} if a
+ * connection with the device state service couldn't be established.
+ */
+ @Nullable
+ static DeviceStateManagerGlobal getInstance() {
+ synchronized (DeviceStateManagerGlobal.class) {
+ if (sInstance == null) {
+ IBinder b = ServiceManager.getService(Context.DEVICE_STATE_SERVICE);
+ if (b != null) {
+ sInstance = new DeviceStateManagerGlobal(IDeviceStateManager
+ .Stub.asInterface(b));
+ }
+ }
+ return sInstance;
+ }
+ }
+
+ @NonNull
+ private final IDeviceStateManager mDeviceStateManager;
+
+ private DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) {
+ mDeviceStateManager = deviceStateManager;
+ }
+}
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
similarity index 72%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index 71cd0a7..24913e9 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright 2020 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.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,7 @@
* limitations under the License.
*/
-package android.media.tv;
+package android.hardware.devicestate;
-parcelable TvChannelInfo;
+/** @hide */
+interface IDeviceStateManager {}
diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java
index 22df778..0879787 100644
--- a/core/java/android/hardware/display/BrightnessCorrection.java
+++ b/core/java/android/hardware/display/BrightnessCorrection.java
@@ -238,7 +238,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == this) {
return true;
}
diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java
index f8d3675..41126b7 100644
--- a/core/java/android/hardware/display/DeviceProductInfo.java
+++ b/core/java/android/hardware/display/DeviceProductInfo.java
@@ -16,6 +16,7 @@
package android.hardware.display;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -124,7 +125,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DeviceProductInfo that = (DeviceProductInfo) o;
@@ -227,7 +228,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ManufactureDate that = (ManufactureDate) o;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index dda1890b..fc14b89 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -16,6 +16,8 @@
package android.hardware.display;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -381,6 +383,7 @@
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL);
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);
+ addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL);
}
return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
} finally {
@@ -401,6 +404,9 @@
private void addPresentationDisplaysLocked(
ArrayList<Display> displays, int[] displayIds, int matchType) {
for (int i = 0; i < displayIds.length; i++) {
+ if (displayIds[i] == DEFAULT_DISPLAY) {
+ continue;
+ }
Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
if (display != null
&& (display.getFlags() & Display.FLAG_PRESENTATION) != 0
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index c7f8915..a86c5cb 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -267,6 +267,12 @@
public abstract void ignoreProximitySensorUntilChanged();
/**
+ * Sets the folded state of the device.
+ * TODO: b/168208522 - Remove in favor of DisplayStatePolicy when that is available.
+ */
+ public abstract void setDeviceFolded(boolean isFolded);
+
+ /**
* Describes the requested power state of the display.
*
* This object is intended to describe the general characteristics of the
@@ -372,7 +378,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return o instanceof DisplayPowerRequest
&& equals((DisplayPowerRequest)o);
}
diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java
index 8dcfc94..d2b66c3 100644
--- a/core/java/android/hardware/display/DisplayViewport.java
+++ b/core/java/android/hardware/display/DisplayViewport.java
@@ -105,7 +105,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == this) {
return true;
}
diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java
index 5bbbbf9..39fd0a8 100644
--- a/core/java/android/hardware/display/WifiDisplay.java
+++ b/core/java/android/hardware/display/WifiDisplay.java
@@ -16,6 +16,7 @@
package android.hardware.display;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -135,7 +136,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return o instanceof WifiDisplay && equals((WifiDisplay)o);
}
diff --git a/core/java/android/hardware/fingerprint/Fingerprint.java b/core/java/android/hardware/fingerprint/Fingerprint.java
index 57e52d9..9ce834ca 100644
--- a/core/java/android/hardware/fingerprint/Fingerprint.java
+++ b/core/java/android/hardware/fingerprint/Fingerprint.java
@@ -31,6 +31,10 @@
mGroupId = groupId;
}
+ public Fingerprint(CharSequence name, int fingerId, long deviceId) {
+ super(name, fingerId, deviceId);
+ }
+
private Fingerprint(Parcel in) {
super(in.readString(), in.readInt(), in.readLong());
mGroupId = in.readInt();
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index b76b877..2aefb1d 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
+import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
@@ -28,6 +29,7 @@
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -35,7 +37,9 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.BiometricTestSession;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.SensorProperties;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.CancellationSignal.OnCancelListener;
@@ -94,6 +98,40 @@
private Fingerprint mRemovalFingerprint;
private Handler mHandler;
+
+ /**
+ * Retrieves a list of properties for all fingerprint sensors on the device.
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ @RequiresPermission(TEST_BIOMETRIC)
+ public List<SensorProperties> getSensorProperties() {
+ final List<SensorProperties> properties = new ArrayList<>();
+ final List<FingerprintSensorPropertiesInternal> internalProperties
+ = getSensorPropertiesInternal();
+ for (FingerprintSensorPropertiesInternal internalProp : internalProperties) {
+ properties.add(FingerprintSensorProperties.from(internalProp));
+ }
+ return properties;
+ }
+
+ /**
+ * Retrieves a test session for FingerprintManager.
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ @RequiresPermission(TEST_BIOMETRIC)
+ public BiometricTestSession createTestSession(int sensorId) {
+ try {
+ return new BiometricTestSession(mContext,
+ mService.createTestSession(sensorId, mContext.getOpPackageName()));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private class OnEnrollCancelListener implements OnCancelListener {
@Override
public void onCancel() {
@@ -588,10 +626,10 @@
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
- public void generateChallenge(int sensorId, GenerateChallengeCallback callback) {
+ public void generateChallenge(int sensorId, int userId, GenerateChallengeCallback callback) {
if (mService != null) try {
mGenerateChallengeCallback = callback;
- mService.generateChallenge(mToken, sensorId, mServiceReceiver,
+ mService.generateChallenge(mToken, sensorId, userId, mServiceReceiver,
mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -604,7 +642,7 @@
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
- public void generateChallenge(GenerateChallengeCallback callback) {
+ public void generateChallenge(int userId, GenerateChallengeCallback callback) {
final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
getSensorPropertiesInternal();
if (fingerprintSensorProperties.isEmpty()) {
@@ -613,17 +651,36 @@
}
final int sensorId = fingerprintSensorProperties.get(0).sensorId;
- generateChallenge(sensorId, callback);
+ generateChallenge(sensorId, userId, callback);
}
/**
- * Finishes enrollment and cancels the current auth token.
+ * Revokes the current challenge.
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
- public void revokeChallenge() {
+ public void revokeChallenge(int userId) {
+ // On HALs with only single in-flight challenge such as IBiometricsFingerprint@2.1,
+ // this parameter is ignored.
+ revokeChallenge(userId, 0L);
+ }
+
+ /**
+ * Revokes the specified challenge.
+ * @hide
+ */
+ @RequiresPermission(MANAGE_FINGERPRINT)
+ public void revokeChallenge(int userId, long challenge) {
if (mService != null) try {
- mService.revokeChallenge(mToken, mContext.getOpPackageName());
+ final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
+ getSensorPropertiesInternal();
+ if (fingerprintSensorProperties.isEmpty()) {
+ Slog.e(TAG, "No sensors");
+ return;
+ }
+ final int sensorId = fingerprintSensorProperties.get(0).sensorId;
+ mService.revokeChallenge(mToken, sensorId, userId, mContext.getOpPackageName(),
+ challenge);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -848,21 +905,6 @@
}
/**
- * Retrieves a list of properties for all fingerprint sensors on the device.
- * @hide
- */
- @NonNull
- public List<FingerprintSensorProperties> getSensorProperties() {
- final List<FingerprintSensorProperties> properties = new ArrayList<>();
- final List<FingerprintSensorPropertiesInternal> internalProperties
- = getSensorPropertiesInternal();
- for (FingerprintSensorPropertiesInternal internalProp : internalProperties) {
- properties.add(FingerprintSensorProperties.from(internalProp));
- }
- return properties;
- }
-
- /**
* Get statically configured sensor properties.
* @hide
*/
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index f467b1d..9566837 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -17,6 +17,7 @@
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.ITestSession;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -30,6 +31,10 @@
* @hide
*/
interface IFingerprintService {
+
+ // Creates a test session with the specified sensorId
+ ITestSession createTestSession(int sensorId, String opPackageName);
+
// Retrieve static sensor properties for all fingerprint sensors
List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);
@@ -88,10 +93,10 @@
boolean isHardwareDetected(String opPackageName);
// Get a pre-enrollment authentication token
- void generateChallenge(IBinder token, int sensorId, IFingerprintServiceReceiver receiver, String opPackageName);
+ void generateChallenge(IBinder token, int sensorId, int userId, IFingerprintServiceReceiver receiver, String opPackageName);
// Finish an enrollment sequence and invalidate the authentication token
- void revokeChallenge(IBinder token, String opPackageName);
+ void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName, long challenge);
// Determine if a user has at least one enrolled fingerprint
boolean hasEnrolledFingerprints(int userId, String opPackageName);
diff --git a/core/java/android/hardware/input/InputDeviceIdentifier.java b/core/java/android/hardware/input/InputDeviceIdentifier.java
index 26f2393..c673e7a 100644
--- a/core/java/android/hardware/input/InputDeviceIdentifier.java
+++ b/core/java/android/hardware/input/InputDeviceIdentifier.java
@@ -16,12 +16,13 @@
package android.hardware.input;
-import java.util.Objects;
-
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import java.util.Objects;
+
/**
* Wrapper for passing identifying information for input devices.
*
@@ -69,7 +70,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || !(o instanceof InputDeviceIdentifier)) return false;
diff --git a/core/java/android/hardware/input/TouchCalibration.java b/core/java/android/hardware/input/TouchCalibration.java
index 7a6eba2..cec7cfc 100644
--- a/core/java/android/hardware/input/TouchCalibration.java
+++ b/core/java/android/hardware/input/TouchCalibration.java
@@ -16,6 +16,7 @@
package android.hardware.input;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -97,7 +98,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof TouchCalibration) {
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index e90b57c..1c95fb6 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -17,6 +17,7 @@
package android.hardware.lights;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
@@ -78,7 +79,7 @@
};
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof Light) {
Light light = (Light) obj;
return mId == light.mId && mOrdinal == light.mOrdinal && mType == light.mType;
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
index ee2d43c..3715a61 100644
--- a/core/java/android/hardware/location/GeofenceHardwareImpl.java
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -16,6 +16,7 @@
package android.hardware.location;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.IFusedGeofenceHardware;
@@ -878,7 +879,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj == null) return false;
if (obj == this) return true;
diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java
index ec318b7..3a042a5 100644
--- a/core/java/android/hardware/radio/ProgramList.java
+++ b/core/java/android/hardware/radio/ProgramList.java
@@ -466,7 +466,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (!(obj instanceof Chunk)) return false;
Chunk other = (Chunk) obj;
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 0f1c2a5..ecdbf4c 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -522,7 +522,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
@@ -699,7 +699,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
@@ -840,7 +840,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
@@ -1527,7 +1527,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj)
return true;
if (obj == null)
@@ -1630,7 +1630,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj)
return true;
if (obj == null)
@@ -1752,7 +1752,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
@@ -1819,7 +1819,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj)
return true;
if (obj == null)
@@ -1909,7 +1909,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj)
return true;
if (obj == null)
diff --git a/core/java/android/hardware/usb/AccessoryFilter.java b/core/java/android/hardware/usb/AccessoryFilter.java
index f22dad4..8345ff3 100644
--- a/core/java/android/hardware/usb/AccessoryFilter.java
+++ b/core/java/android/hardware/usb/AccessoryFilter.java
@@ -17,6 +17,7 @@
package android.hardware.usb;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.service.usb.UsbAccessoryFilterProto;
import com.android.internal.util.dump.DualDumpOutputStream;
@@ -120,7 +121,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
// can't compare if we have wildcard strings
if (mManufacturer == null || mModel == null || mVersion == null) {
return false;
diff --git a/core/java/android/hardware/usb/DeviceFilter.java b/core/java/android/hardware/usb/DeviceFilter.java
index da979c0c..66b0a42 100644
--- a/core/java/android/hardware/usb/DeviceFilter.java
+++ b/core/java/android/hardware/usb/DeviceFilter.java
@@ -17,6 +17,7 @@
package android.hardware.usb;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.service.usb.UsbDeviceFilterProto;
import android.util.Slog;
@@ -238,7 +239,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
// can't compare if we have wildcard strings
if (mVendorId == -1 || mProductId == -1 ||
mClass == -1 || mSubclass == -1 || mProtocol == -1) {
diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java
index f4cfc74..47c6978 100644
--- a/core/java/android/hardware/usb/UsbAccessory.java
+++ b/core/java/android/hardware/usb/UsbAccessory.java
@@ -186,7 +186,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof UsbAccessory) {
UsbAccessory accessory = (UsbAccessory)obj;
return (compare(mManufacturer, accessory.getManufacturer()) &&
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index a0f64cd..71074df 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -308,7 +308,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof UsbDevice) {
return ((UsbDevice)o).mName.equals(mName);
} else if (o instanceof String) {
diff --git a/core/java/android/net/CaptivePortalData.java b/core/java/android/net/CaptivePortalData.java
index 1357803..09f4762 100644
--- a/core/java/android/net/CaptivePortalData.java
+++ b/core/java/android/net/CaptivePortalData.java
@@ -254,7 +254,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof CaptivePortalData)) return false;
final CaptivePortalData other = (CaptivePortalData) obj;
return mRefreshTimeMillis == other.mRefreshTimeMillis
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 5d4003a..b2eba63 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -899,6 +899,18 @@
}
/**
+ * @hide
+ * TODO: Expose for SystemServer when becomes a module.
+ */
+ public void systemReady() {
+ try {
+ mService.systemReady();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Checks if a given type uses the cellular data connection.
* This should be replaced in the future by a network property.
* @param networkType the type to check
diff --git a/core/java/android/net/DataUsageRequest.java b/core/java/android/net/DataUsageRequest.java
index 0ac8f7e7..b06d515 100644
--- a/core/java/android/net/DataUsageRequest.java
+++ b/core/java/android/net/DataUsageRequest.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.Nullable;
import android.net.NetworkTemplate;
import android.os.Parcel;
import android.os.Parcelable;
@@ -95,7 +96,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof DataUsageRequest == false) return false;
DataUsageRequest that = (DataUsageRequest) obj;
return that.requestId == this.requestId
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 1ef4f17..6819c34 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -159,7 +159,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (!(obj instanceof DhcpResults)) return false;
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index d7f178c..059ec28 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -233,4 +233,6 @@
void simulateDataStall(int detectionMethod, long timestampMillis, in Network network,
in PersistableBundle extras);
+
+ void systemReady();
}
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index c8ca618e..9876fc6 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -364,7 +364,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof Ikev2VpnProfile)) {
return false;
}
diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java
index 23d5ff7..fa31b80 100644
--- a/core/java/android/net/IpConfiguration.java
+++ b/core/java/android/net/IpConfiguration.java
@@ -166,7 +166,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == this) {
return true;
}
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
index 8cfe6df..06f5f27 100644
--- a/core/java/android/net/IpPrefix.java
+++ b/core/java/android/net/IpPrefix.java
@@ -18,6 +18,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
@@ -127,7 +128,7 @@
* @return {@code true} if both objects are equal, {@code false} otherwise.
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof IpPrefix)) {
return false;
}
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 38d9883..a4f7b74 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.StringDef;
+import android.content.res.Resources;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,6 +28,12 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
/**
* This class represents a single algorithm that can be used by an {@link IpSecTransform}.
@@ -52,6 +59,27 @@
public static final String CRYPT_AES_CBC = "cbc(aes)";
/**
+ * AES-CTR Encryption/Ciphering Algorithm.
+ *
+ * <p>Valid lengths for keying material are {160, 224, 288}.
+ *
+ * <p>As per <a href="https://tools.ietf.org/html/rfc3686#section-5.1">RFC3686 (Section
+ * 5.1)</a>, keying material consists of a 128, 192, or 256 bit AES key followed by a 32-bit
+ * nonce. RFC compliance requires that the nonce must be unique per security association.
+ *
+ * <p>This algorithm may be available on the device. Caller MUST check if it is supported before
+ * using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
+ * included in the returned algorithm set. The returned algorithm set will not change unless the
+ * device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
+ * requested on an unsupported device.
+ *
+ * <p>@see {@link #getSupportedAlgorithms()}
+ */
+ // This algorithm may be available on devices released before Android 12, and is guaranteed
+ // to be available on devices first shipped with Android 12 or later.
+ public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))";
+
+ /**
* MD5 HMAC Authentication/Integrity Algorithm. <b>This algorithm is not recommended for use in
* new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
*
@@ -99,6 +127,25 @@
public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
/**
+ * AES-XCBC Authentication/Integrity Algorithm.
+ *
+ * <p>Keys for this algorithm must be 128 bits in length.
+ *
+ * <p>The only valid truncation length is 96 bits.
+ *
+ * <p>This algorithm may be available on the device. Caller MUST check if it is supported before
+ * using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
+ * included in the returned algorithm set. The returned algorithm set will not change unless the
+ * device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
+ * requested on an unsupported device.
+ *
+ * <p>@see {@link #getSupportedAlgorithms()}
+ */
+ // This algorithm may be available on devices released before Android 12, and is guaranteed
+ // to be available on devices first shipped with Android 12 or later.
+ public static final String AUTH_AES_XCBC = "xcbc(aes)";
+
+ /**
* AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm.
*
* <p>Valid lengths for keying material are {160, 224, 288}.
@@ -111,19 +158,67 @@
*/
public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
+ /**
+ * ChaCha20-Poly1305 Authentication/Integrity + Encryption/Ciphering Algorithm.
+ *
+ * <p>Keys for this algorithm must be 288 bits in length.
+ *
+ * <p>As per <a href="https://tools.ietf.org/html/rfc7634#section-2">RFC7634 (Section 2)</a>,
+ * keying material consists of a 256 bit key followed by a 32-bit salt. The salt is fixed per
+ * security association.
+ *
+ * <p>The only valid ICV (truncation) length is 128 bits.
+ *
+ * <p>This algorithm may be available on the device. Caller MUST check if it is supported before
+ * using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is
+ * included in the returned algorithm set. The returned algorithm set will not change unless the
+ * device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is
+ * requested on an unsupported device.
+ *
+ * <p>@see {@link #getSupportedAlgorithms()}
+ */
+ // This algorithm may be available on devices released before Android 12, and is guaranteed
+ // to be available on devices first shipped with Android 12 or later.
+ public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)";
+
/** @hide */
@StringDef({
CRYPT_AES_CBC,
+ CRYPT_AES_CTR,
AUTH_HMAC_MD5,
AUTH_HMAC_SHA1,
AUTH_HMAC_SHA256,
AUTH_HMAC_SHA384,
AUTH_HMAC_SHA512,
- AUTH_CRYPT_AES_GCM
+ AUTH_AES_XCBC,
+ AUTH_CRYPT_AES_GCM,
+ AUTH_CRYPT_CHACHA20_POLY1305
})
@Retention(RetentionPolicy.SOURCE)
public @interface AlgorithmName {}
+ /** @hide */
+ @VisibleForTesting
+ public static final Map<String, Integer> ALGO_TO_REQUIRED_FIRST_SDK = new HashMap<>();
+
+ static {
+ ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CBC, Build.VERSION_CODES.P);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_MD5, Build.VERSION_CODES.P);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA1, Build.VERSION_CODES.P);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA256, Build.VERSION_CODES.P);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA384, Build.VERSION_CODES.P);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA512, Build.VERSION_CODES.P);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_AES_GCM, Build.VERSION_CODES.P);
+
+ // STOPSHIP: b/170424293 Use Build.VERSION_CODES.S when it is defined
+ ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1);
+ }
+
+ private static final Set<String> ENABLED_ALGOS =
+ Collections.unmodifiableSet(loadAlgos(Resources.getSystem()));
+
private final String mName;
private final byte[] mKey;
private final int mTruncLenBits;
@@ -137,6 +232,7 @@
*
* @param algorithm name of the algorithm.
* @param key key padded to a multiple of 8 bits.
+ * @throws IllegalArgumentException if algorithm or key length is invalid.
*/
public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) {
this(algorithm, key, 0);
@@ -152,6 +248,7 @@
* @param algorithm name of the algorithm.
* @param key key padded to a multiple of 8 bits.
* @param truncLenBits number of bits of output hash to use.
+ * @throws IllegalArgumentException if algorithm, key length or truncation length is invalid.
*/
public IpSecAlgorithm(
@NonNull @AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
@@ -206,13 +303,59 @@
}
};
- private static void checkValidOrThrow(String name, int keyLen, int truncLen) {
- boolean isValidLen = true;
- boolean isValidTruncLen = true;
+ /**
+ * Returns supported IPsec algorithms for the current device.
+ *
+ * <p>Some algorithms may not be supported on old devices. Callers MUST check if an algorithm is
+ * supported before using it.
+ */
+ @NonNull
+ public static Set<String> getSupportedAlgorithms() {
+ return ENABLED_ALGOS;
+ }
- switch(name) {
+ /** @hide */
+ @VisibleForTesting
+ public static Set<String> loadAlgos(Resources systemResources) {
+ final Set<String> enabledAlgos = new HashSet<>();
+
+ // Load and validate the optional algorithm resource. Undefined or duplicate algorithms in
+ // the resource are not allowed.
+ final String[] resourceAlgos = systemResources.getStringArray(
+ com.android.internal.R.array.config_optionalIpSecAlgorithms);
+ for (String str : resourceAlgos) {
+ if (!ALGO_TO_REQUIRED_FIRST_SDK.containsKey(str) || !enabledAlgos.add(str)) {
+ // This error should be caught by CTS and never be thrown to API callers
+ throw new IllegalArgumentException("Invalid or repeated algorithm " + str);
+ }
+ }
+
+ for (Entry<String, Integer> entry : ALGO_TO_REQUIRED_FIRST_SDK.entrySet()) {
+ if (Build.VERSION.FIRST_SDK_INT >= entry.getValue()) {
+ enabledAlgos.add(entry.getKey());
+ }
+ }
+
+ return enabledAlgos;
+ }
+
+ private static void checkValidOrThrow(String name, int keyLen, int truncLen) {
+ final boolean isValidLen;
+ final boolean isValidTruncLen;
+
+ if (!getSupportedAlgorithms().contains(name)) {
+ throw new IllegalArgumentException("Unsupported algorithm: " + name);
+ }
+
+ switch (name) {
case CRYPT_AES_CBC:
isValidLen = keyLen == 128 || keyLen == 192 || keyLen == 256;
+ isValidTruncLen = true;
+ break;
+ case CRYPT_AES_CTR:
+ // The keying material for AES-CTR is a key plus a 32-bit salt
+ isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
+ isValidTruncLen = true;
break;
case AUTH_HMAC_MD5:
isValidLen = keyLen == 128;
@@ -234,12 +377,22 @@
isValidLen = keyLen == 512;
isValidTruncLen = truncLen >= 256 && truncLen <= 512;
break;
+ case AUTH_AES_XCBC:
+ isValidLen = keyLen == 128;
+ isValidTruncLen = truncLen == 96;
+ break;
case AUTH_CRYPT_AES_GCM:
// The keying material for GCM is a key plus a 32-bit salt
isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
isValidTruncLen = truncLen == 64 || truncLen == 96 || truncLen == 128;
break;
+ case AUTH_CRYPT_CHACHA20_POLY1305:
+ // The keying material for ChaCha20Poly1305 is a key plus a 32-bit salt
+ isValidLen = keyLen == 256 + 32;
+ isValidTruncLen = truncLen == 128;
+ break;
default:
+ // Should never hit here.
throw new IllegalArgumentException("Couldn't find an algorithm: " + name);
}
@@ -260,6 +413,7 @@
case AUTH_HMAC_SHA256:
case AUTH_HMAC_SHA384:
case AUTH_HMAC_SHA512:
+ case AUTH_AES_XCBC:
return true;
default:
return false;
@@ -268,12 +422,24 @@
/** @hide */
public boolean isEncryption() {
- return getName().equals(CRYPT_AES_CBC);
+ switch (getName()) {
+ case CRYPT_AES_CBC: // fallthrough
+ case CRYPT_AES_CTR:
+ return true;
+ default:
+ return false;
+ }
}
/** @hide */
public boolean isAead() {
- return getName().equals(AUTH_CRYPT_AES_GCM);
+ switch (getName()) {
+ case AUTH_CRYPT_AES_GCM: // fallthrough
+ case AUTH_CRYPT_CHACHA20_POLY1305:
+ return true;
+ default:
+ return false;
+ }
}
// Because encryption keys are sensitive and userdebug builds are used by large user pools
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 44a0a4f..fa1497d 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -153,7 +154,7 @@
/**
* Standard equals.
*/
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (this == other) return true;
if (!(other instanceof IpSecTransform)) return false;
final IpSecTransform rhs = (IpSecTransform) other;
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index a9d7f17..772c685 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -357,7 +357,7 @@
* @return {@code true} if both objects are equal, {@code false} otherwise.
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof LinkAddress)) {
return false;
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 651494d..555d710 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -1639,7 +1639,7 @@
* @return {@code true} if both objects are equal, {@code false} otherwise.
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (!(obj instanceof LinkProperties)) return false;
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 0eb3c1e..6949bf2 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -160,7 +160,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr;
}
diff --git a/core/java/android/net/MatchAllNetworkSpecifier.java b/core/java/android/net/MatchAllNetworkSpecifier.java
index 70c4a72..84985b6 100644
--- a/core/java/android/net/MatchAllNetworkSpecifier.java
+++ b/core/java/android/net/MatchAllNetworkSpecifier.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -55,7 +56,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return o instanceof MatchAllNetworkSpecifier;
}
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index b872617..10ee72e 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -500,7 +501,7 @@
};
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof Network)) return false;
Network other = (Network)obj;
return this.netId == other.netId;
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 0948a4da..9c10388 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -20,6 +20,7 @@
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeMobile;
+import android.annotation.Nullable;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
@@ -69,7 +70,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkIdentity) {
final NetworkIdentity ident = (NetworkIdentity) obj;
return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 4f05c9b..8a0211c 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -205,7 +206,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkPolicy) {
final NetworkPolicy other = (NetworkPolicy) obj;
return warningBytes == other.warningBytes
diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java
index 75086cf..d31218d 100644
--- a/core/java/android/net/NetworkProvider.java
+++ b/core/java/android/net/NetworkProvider.java
@@ -30,7 +30,7 @@
/**
* Base class for network providers such as telephony or Wi-Fi. NetworkProviders connect the device
- * to networks and makes them available to to the core network stack by creating
+ * to networks and makes them available to the core network stack by creating
* {@link NetworkAgent}s. The networks can then provide connectivity to apps and can be interacted
* with via networking APIs such as {@link ConnectivityManager}.
*
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 473e6c5..1d6e507 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -553,7 +553,7 @@
proto.end(token);
}
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkRequest == false) return false;
NetworkRequest that = (NetworkRequest)obj;
return (that.legacyType == this.legacyType &&
diff --git a/core/java/android/net/NetworkScorerAppData.java b/core/java/android/net/NetworkScorerAppData.java
index 116e39e..caa6e45 100644
--- a/core/java/android/net/NetworkScorerAppData.java
+++ b/core/java/android/net/NetworkScorerAppData.java
@@ -110,7 +110,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NetworkScorerAppData that = (NetworkScorerAppData) o;
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 34e48eb..cbee010 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -411,7 +411,7 @@
/** @hide */
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof Entry) {
final Entry e = (Entry) o;
return uid == e.uid && set == e.set && tag == e.tag && metered == e.metered
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index cd26079..a95ba12f 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -327,7 +327,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkTemplate) {
final NetworkTemplate other = (NetworkTemplate) obj;
return mMatchRule == other.mMatchRule
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index a32b41f..de5a180 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -275,7 +275,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof ProxyInfo)) return false;
ProxyInfo p = (ProxyInfo)o;
// If PAC URL is present in either then they must be equal.
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 9876076..d956875 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -539,7 +539,7 @@
* Compares this RouteInfo object against the specified object and indicates if they are equal.
* @return {@code true} if the objects are equal, {@code false} otherwise.
*/
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (!(obj instanceof RouteInfo)) return false;
@@ -575,7 +575,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof RouteKey)) {
return false;
}
diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java
index 3f2aa17..012410b 100644
--- a/core/java/android/net/StringNetworkSpecifier.java
+++ b/core/java/android/net/StringNetworkSpecifier.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -45,7 +46,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof StringNetworkSpecifier)) return false;
return TextUtils.equals(specifier, ((StringNetworkSpecifier) o).specifier);
}
diff --git a/core/java/android/net/TEST_MAPPING b/core/java/android/net/TEST_MAPPING
new file mode 100644
index 0000000..abac811
--- /dev/null
+++ b/core/java/android/net/TEST_MAPPING
@@ -0,0 +1,20 @@
+{
+ "imports": [
+ {
+ // Also includes cts/tests/tests/net
+ "path": "frameworks/base/tests/net"
+ },
+ {
+ "path": "packages/modules/NetworkStack"
+ },
+ {
+ "path": "packages/modules/CaptivePortalLogin"
+ },
+ {
+ "path": "frameworks/base/packages/Tethering"
+ },
+ {
+ "path": "frameworks/opt/net/wifi"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/core/java/android/net/TelephonyNetworkSpecifier.java b/core/java/android/net/TelephonyNetworkSpecifier.java
index 33c71d5..3348233 100644
--- a/core/java/android/net/TelephonyNetworkSpecifier.java
+++ b/core/java/android/net/TelephonyNetworkSpecifier.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -74,7 +75,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java
index d75c43d..3bc0f9c 100644
--- a/core/java/android/net/UidRange.java
+++ b/core/java/android/net/UidRange.java
@@ -18,6 +18,7 @@
import static android.os.UserHandle.PER_USER_RANGE;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -81,7 +82,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index efe2903..815e4f0 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -354,7 +354,7 @@
* default port explicitly and the other leaves it implicit, they will not
* be considered equal.
*/
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof Uri)) {
return false;
}
diff --git a/core/java/android/nfc/NdefMessage.java b/core/java/android/nfc/NdefMessage.java
index 35dae55..553f6c0 100644
--- a/core/java/android/nfc/NdefMessage.java
+++ b/core/java/android/nfc/NdefMessage.java
@@ -16,6 +16,7 @@
package android.nfc;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.proto.ProtoOutputStream;
@@ -23,7 +24,6 @@
import java.nio.ByteBuffer;
import java.util.Arrays;
-
/**
* Represents an immutable NDEF Message.
* <p>
@@ -239,7 +239,7 @@
* identical NDEF Records.
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 74f8cb4..421eb333 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -16,6 +16,7 @@
package android.nfc;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.net.Uri;
@@ -1032,7 +1033,7 @@
* identical tnf, type, id and payload fields.
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index d7c2e05..e3f996b 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -16,6 +16,7 @@
package android.nfc.cardemulation;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.pm.PackageManager;
@@ -507,7 +508,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (!(o instanceof ApduServiceInfo)) return false;
ApduServiceInfo thatService = (ApduServiceInfo) o;
diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
index 885a0b5..c2b33dd 100644
--- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
@@ -16,6 +16,7 @@
package android.nfc.cardemulation;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -250,7 +251,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (!(o instanceof NfcFServiceInfo)) return false;
NfcFServiceInfo thatService = (NfcFServiceInfo) o;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index d889b155..8852589 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1499,7 +1499,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 78ba7f0..bd18150 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressAutoDoc;
import android.annotation.SystemApi;
@@ -1215,7 +1216,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof Partition)) {
return false;
}
diff --git a/core/java/android/os/CoolingDevice.java b/core/java/android/os/CoolingDevice.java
index 0e86a38..4babd4b 100644
--- a/core/java/android/os/CoolingDevice.java
+++ b/core/java/android/os/CoolingDevice.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.thermal.V2_0.CoolingType;
import com.android.internal.util.Preconditions;
@@ -128,7 +129,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof CoolingDevice)) {
return false;
}
diff --git a/core/java/android/os/ExternalVibration.java b/core/java/android/os/ExternalVibration.java
index e62244f..7fd02116 100644
--- a/core/java/android/os/ExternalVibration.java
+++ b/core/java/android/os/ExternalVibration.java
@@ -17,6 +17,7 @@
package android.os;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.media.AudioAttributes;
import android.util.Slog;
@@ -137,7 +138,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == null || !(o instanceof ExternalVibration)) {
return false;
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 07363ed..6fe5777 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -86,6 +86,7 @@
Bundle getApplicationRestrictionsForUser(in String packageName, int userId);
void setDefaultGuestRestrictions(in Bundle restrictions);
Bundle getDefaultGuestRestrictions();
+ int removeUserOrSetEphemeral(int userId);
boolean markGuestForDeletion(int userId);
UserInfo findCurrentGuestUser();
boolean isQuietModeEnabled(int userId);
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index 9c0bc45..ee64551 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -93,7 +93,7 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (other == this)
return true;
if (!(other instanceof LocaleList))
diff --git a/core/java/android/os/Messenger.java b/core/java/android/os/Messenger.java
index ed5c470..ed851b3 100644
--- a/core/java/android/os/Messenger.java
+++ b/core/java/android/os/Messenger.java
@@ -16,6 +16,8 @@
package android.os;
+import android.annotation.Nullable;
+
/**
* Reference to a Handler, which others can use to send messages to it.
* This allows for the implementation of message-based communication across
@@ -71,7 +73,7 @@
* Comparison operator on two Messenger objects, such that true
* is returned then they both point to the same Handler.
*/
- public boolean equals(Object otherObj) {
+ public boolean equals(@Nullable Object otherObj) {
if (otherObj == null) {
return false;
}
diff --git a/core/java/android/os/ParcelUuid.java b/core/java/android/os/ParcelUuid.java
index 0b52c75..b529694 100644
--- a/core/java/android/os/ParcelUuid.java
+++ b/core/java/android/os/ParcelUuid.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import java.util.UUID;
@@ -91,7 +92,7 @@
* or {@code false} if not.
*/
@Override
- public boolean equals(Object object) {
+ public boolean equals(@Nullable Object object) {
if (object == null) {
return false;
}
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index 7caffcd..55785f3 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.thermal.V2_0.TemperatureType;
import android.hardware.thermal.V2_0.ThrottlingSeverity;
@@ -171,7 +172,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof Temperature)) {
return false;
}
diff --git a/core/java/android/os/TimestampedValue.java b/core/java/android/os/TimestampedValue.java
index 4c4335b..3d8a550 100644
--- a/core/java/android/os/TimestampedValue.java
+++ b/core/java/android/os/TimestampedValue.java
@@ -60,7 +60,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index ff1bcf6..60447e7 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -18,6 +18,7 @@
import android.annotation.AppIdInt;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
@@ -532,7 +533,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
try {
if (obj != null) {
UserHandle other = (UserHandle)obj;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ddc21ab..42ae930 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3978,6 +3978,23 @@
}
/**
+ * Immediately removes the user or, if the user cannot be removed, such as when the user is
+ * the current user, then set the user as ephemeral so that it will be removed when it is
+ * stopped.
+ *
+ * @return the {@link com.android.server.pm.UserManagerService.RemoveResult} code
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public int removeUserOrSetEphemeral(@UserIdInt int userId) {
+ try {
+ return mService.removeUserOrSetEphemeral(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Updates the user's name.
*
* @param userId the user's integer id
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index d77861f..2093077 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -237,7 +237,7 @@
};
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index e02fd28..487e468 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -539,7 +539,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof VibrationEffect.OneShot)) {
return false;
}
@@ -705,7 +705,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof VibrationEffect.Waveform)) {
return false;
}
@@ -872,7 +872,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof VibrationEffect.Prebaked)) {
return false;
}
@@ -998,7 +998,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Composed composed = (Composed) o;
@@ -1244,7 +1244,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PrimitiveEffect that = (PrimitiveEffect) o;
diff --git a/core/java/android/os/connectivity/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java
index 121fd33..fc17002 100644
--- a/core/java/android/os/connectivity/CellularBatteryStats.java
+++ b/core/java/android/os/connectivity/CellularBatteryStats.java
@@ -109,7 +109,7 @@
CellSignalStrength.getNumSignalStrengthLevels()));
mTxTimeMs = Arrays.copyOfRange(
txTimeMs, 0,
- Math.min(txTimeMs.length, ModemActivityInfo.TX_POWER_LEVELS));
+ Math.min(txTimeMs.length, ModemActivityInfo.getNumTxPowerLevels()));
mMonitoredRailChargeConsumedMaMs = monitoredRailChargeConsumedMaMs;
}
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index df3c4d5..67317c7 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -184,7 +184,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof DiskInfo) {
return Objects.equals(id, ((DiskInfo) o).id);
} else {
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index a63b82e..eed36d7 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -435,7 +435,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof StorageVolume && mPath != null) {
StorageVolume volume = (StorageVolume)obj;
return (mPath.equals(volume.mPath));
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index d7aaa4d6..74c0ecb 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -520,7 +520,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof VolumeInfo) {
return Objects.equals(id, ((VolumeInfo) o).id);
} else {
diff --git a/core/java/android/os/storage/VolumeRecord.java b/core/java/android/os/storage/VolumeRecord.java
index 60df981..0f58a71 100644
--- a/core/java/android/os/storage/VolumeRecord.java
+++ b/core/java/android/os/storage/VolumeRecord.java
@@ -16,6 +16,7 @@
package android.os.storage;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Environment;
@@ -149,7 +150,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof VolumeRecord) {
return Objects.equals(fsUuid, ((VolumeRecord) o).fsUuid);
} else {
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index d80a7e7..70b536d 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -587,7 +587,7 @@
}
@Override
- public boolean equals(Object rval) {
+ public boolean equals(@Nullable Object rval) {
// N.B. pid doesn't count toward equality!
if (rval == null) {
return false;
@@ -609,7 +609,7 @@
/** @hide */
private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache =
new PropertyInvalidatedCache<PermissionQuery, Integer>(
- 16, CACHE_KEY_PACKAGE_INFO, "checkPermission") {
+ 2048, CACHE_KEY_PACKAGE_INFO, "checkPermission") {
@Override
protected Integer recompute(PermissionQuery query) {
return checkPermissionUncached(query.permission, query.pid, query.uid);
@@ -660,7 +660,7 @@
}
@Override
- public boolean equals(Object rval) {
+ public boolean equals(@Nullable Object rval) {
if (rval == null) {
return false;
}
diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md
index 0495495..4224b7a 100644
--- a/core/java/android/permission/Permissions.md
+++ b/core/java/android/permission/Permissions.md
@@ -848,7 +848,7 @@
@Test
fun onlySomeAppsAreAllowedToHavePermissionGranted() {
- assertThat(whitelistedPkgs).containsAllIn(
+ assertThat(whitelistedPkgs).containsAtLeastElementsIn(
context.packageManager.getInstalledPackages(MATCH_ALL)
.filter { pkg ->
context.checkPermission(android.Manifest.permission.MY_PRIVILEGED_PERMISSION, -1,
diff --git a/core/java/android/print/PageRange.java b/core/java/android/print/PageRange.java
index 1345038..f5f6613 100644
--- a/core/java/android/print/PageRange.java
+++ b/core/java/android/print/PageRange.java
@@ -18,6 +18,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -128,7 +129,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index e607ced..934e642 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -370,7 +370,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
@@ -1019,7 +1019,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
@@ -1180,7 +1180,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
@@ -1304,7 +1304,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
index b988461..a817a7d 100644
--- a/core/java/android/print/PrintDocumentInfo.java
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -19,9 +19,11 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
@@ -235,7 +237,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/print/PrintJob.java b/core/java/android/print/PrintJob.java
index 66181e0..9c8e61f 100644
--- a/core/java/android/print/PrintJob.java
+++ b/core/java/android/print/PrintJob.java
@@ -184,7 +184,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/print/PrintJobId.java b/core/java/android/print/PrintJobId.java
index 606cbb8..35bec5c 100644
--- a/core/java/android/print/PrintJobId.java
+++ b/core/java/android/print/PrintJobId.java
@@ -17,6 +17,7 @@
package android.print;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -59,7 +60,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java
index e465d3b..8f1b864 100644
--- a/core/java/android/print/PrinterCapabilitiesInfo.java
+++ b/core/java/android/print/PrinterCapabilitiesInfo.java
@@ -17,6 +17,7 @@
package android.print;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.print.PrintAttributes.ColorMode;
@@ -24,6 +25,7 @@
import android.print.PrintAttributes.Margins;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
+
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
@@ -271,7 +273,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java
index 75ca750..25260c4 100644
--- a/core/java/android/print/PrinterId.java
+++ b/core/java/android/print/PrinterId.java
@@ -17,6 +17,7 @@
package android.print;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.os.Parcel;
@@ -85,7 +86,7 @@
}
@Override
- public boolean equals(Object object) {
+ public boolean equals(@Nullable Object object) {
if (this == object) {
return true;
}
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index 4d5ccc0..8e03e3e 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -360,7 +360,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index 3226f34..d0d41e6 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -27,6 +27,7 @@
import android.print.PrintJobInfo;
import android.text.TextUtils;
import android.util.Log;
+
import com.android.internal.util.Preconditions;
/**
@@ -436,7 +437,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index e1aa21e..8ac1d84e 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -2618,7 +2618,8 @@
intent.setData(ContentUris.withAppendedId(CalendarContract.CONTENT_URI, alarmTime));
intent.putExtra(ALARM_TIME, alarmTime);
intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
+ PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent,
+ PendingIntent.FLAG_IMMUTABLE);
manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime, pi);
}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index da9794d..8f69edb 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1811,7 +1811,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7df9a5f..68d8d82 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9088,6 +9088,7 @@
* @see#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
* @hide
*/
+ @TestApi
public static final String ACCESSIBILITY_MAGNIFICATION_MODE =
"accessibility_magnification_mode";
@@ -9095,12 +9096,14 @@
* Magnification mode value that magnifies whole display.
* @hide
*/
+ @TestApi
public static final int ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN = 0x1;
/**
* Magnification mode value that magnifies magnify particular region in a window
* @hide
*/
+ @TestApi
public static final int ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW = 0x2;
/**
@@ -9108,6 +9111,7 @@
* region in a window.
* @hide
*/
+ @TestApi
public static final int ACCESSIBILITY_MAGNIFICATION_MODE_ALL = 0x3;
/**
@@ -9119,10 +9123,20 @@
* @see#ACCESSIBILITY_MAGNIFICATION_MODE_ALL
* @hide
*/
+ @TestApi
public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY =
"accessibility_magnification_capability";
/**
+ * Whether to show the window magnification prompt dialog when the user uses full-screen
+ * magnification first time after database is upgraded .
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT =
+ "accessibility_show_window_magnification_prompt";
+
+ /**
* Whether the Adaptive connectivity option is enabled.
*
* @hide
@@ -13407,6 +13421,7 @@
*
* @hide
*/
+ @TestApi
public static final String HIDDEN_API_POLICY = "hidden_api_policy";
/**
@@ -14585,6 +14600,19 @@
*/
public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH =
"maximum_obscuring_opacity_for_touch";
+
+ /**
+ * LatencyTracker settings.
+ *
+ * The following strings are supported as keys:
+ * <pre>
+ * enabled (boolean)
+ * sampling_interval (int)
+ * </pre>
+ *
+ * @hide
+ */
+ public static final String LATENCY_TRACKER = "latency_tracker";
}
/**
diff --git a/core/java/android/security/net/config/Domain.java b/core/java/android/security/net/config/Domain.java
index 5bb727a3..c87c173 100644
--- a/core/java/android/security/net/config/Domain.java
+++ b/core/java/android/security/net/config/Domain.java
@@ -16,7 +16,10 @@
package android.security.net.config;
+import android.annotation.Nullable;
+
import java.util.Locale;
+
/** @hide */
public final class Domain {
/**
@@ -43,7 +46,7 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (other == this) {
return true;
}
diff --git a/core/java/android/security/net/config/Pin.java b/core/java/android/security/net/config/Pin.java
index 94520e2..7ac0ab0 100644
--- a/core/java/android/security/net/config/Pin.java
+++ b/core/java/android/security/net/config/Pin.java
@@ -16,6 +16,8 @@
package android.security.net.config;
+import android.annotation.Nullable;
+
import java.util.Arrays;
/** @hide */
@@ -56,7 +58,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index 1cd2d62..f226528 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -159,6 +159,7 @@
FieldClassification.writeArrayToParcel(parcel,
event.mDetectedFieldClassifications);
}
+ parcel.writeInt(event.mSaveDialogNotShowReason);
}
}
}
@@ -243,6 +244,40 @@
@Retention(RetentionPolicy.SOURCE)
@interface EventIds{}
+ /** No reason for save dialog. */
+ public static final int NO_SAVE_REASON_NONE = 0;
+
+ /** The SaveInfo associated with the FillResponse is null. */
+ public static final int NO_SAVE_REASON_NO_SAVE_INFO = 1;
+
+ /** The service asked to delay save. */
+ public static final int NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG = 2;
+
+ /** There was empty value for required ids. */
+ public static final int NO_SAVE_REASON_HAS_EMPTY_REQUIRED = 3;
+
+ /** No value has been changed. */
+ public static final int NO_SAVE_REASON_NO_VALUE_CHANGED = 4;
+
+ /** Fields failed validation. */
+ public static final int NO_SAVE_REASON_FIELD_VALIDATION_FAILED = 5;
+
+ /** All fields matched contents of datasets. */
+ public static final int NO_SAVE_REASON_DATASET_MATCH = 6;
+
+ /** @hide */
+ @IntDef(prefix = { "NO_SAVE_REASON_" }, value = {
+ NO_SAVE_REASON_NONE,
+ NO_SAVE_REASON_NO_SAVE_INFO,
+ NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG,
+ NO_SAVE_REASON_HAS_EMPTY_REQUIRED,
+ NO_SAVE_REASON_NO_VALUE_CHANGED,
+ NO_SAVE_REASON_FIELD_VALIDATION_FAILED,
+ NO_SAVE_REASON_DATASET_MATCH
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NoSaveReason{}
+
@EventIds private final int mEventType;
@Nullable private final String mDatasetId;
@Nullable private final Bundle mClientState;
@@ -261,6 +296,8 @@
@Nullable private final AutofillId[] mDetectedFieldIds;
@Nullable private final FieldClassification[] mDetectedFieldClassifications;
+ @NoSaveReason private final int mSaveDialogNotShowReason;
+
/**
* Returns the type of the event.
*
@@ -448,6 +485,18 @@
}
/**
+ * Returns the reason why a save dialog was not shown.
+ *
+ * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}. For the other
+ * event types, the reason is set to NO_SAVE_REASON_NONE.
+ *
+ * @return The reason why a save dialog was not shown.
+ */
+ public int getNoSaveReason() {
+ return mSaveDialogNotShowReason;
+ }
+
+ /**
* Creates a new event.
*
* @param eventType The type of the event
@@ -481,6 +530,48 @@
@Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
@Nullable AutofillId[] detectedFieldIds,
@Nullable FieldClassification[] detectedFieldClassifications) {
+ this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds,
+ changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
+ manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
+ NO_SAVE_REASON_NONE);
+ }
+
+ /**
+ * Creates a new event.
+ *
+ * @param eventType The type of the event
+ * @param datasetId The dataset the event was on, or {@code null} if the event was on the
+ * whole response.
+ * @param clientState The client state associated with the event.
+ * @param selectedDatasetIds The ids of datasets selected by the user.
+ * @param ignoredDatasetIds The ids of datasets NOT select by the user.
+ * @param changedFieldIds The ids of fields changed by the user.
+ * @param changedDatasetIds The ids of the datasets that havd values matching the
+ * respective entry on {@code changedFieldIds}.
+ * @param manuallyFilledFieldIds The ids of fields that were manually entered by the user
+ * and belonged to datasets.
+ * @param manuallyFilledDatasetIds The ids of datasets that had values matching the
+ * respective entry on {@code manuallyFilledFieldIds}.
+ * @param detectedFieldClassifications the field classification matches.
+ * @param saveDialogNotShowReason The reason why a save dialog was not shown.
+ *
+ * @throws IllegalArgumentException If the length of {@code changedFieldIds} and
+ * {@code changedDatasetIds} doesn't match.
+ * @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and
+ * {@code manuallyFilledDatasetIds} doesn't match.
+ *
+ * @hide
+ */
+ public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
+ @Nullable List<String> selectedDatasetIds,
+ @Nullable ArraySet<String> ignoredDatasetIds,
+ @Nullable ArrayList<AutofillId> changedFieldIds,
+ @Nullable ArrayList<String> changedDatasetIds,
+ @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
+ @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
+ @Nullable AutofillId[] detectedFieldIds,
+ @Nullable FieldClassification[] detectedFieldClassifications,
+ int saveDialogNotShowReason) {
mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_DATASETS_SHOWN,
"eventType");
mDatasetId = datasetId;
@@ -506,6 +597,10 @@
mDetectedFieldIds = detectedFieldIds;
mDetectedFieldClassifications = detectedFieldClassifications;
+
+ mSaveDialogNotShowReason = Preconditions.checkArgumentInRange(saveDialogNotShowReason,
+ NO_SAVE_REASON_NONE, NO_SAVE_REASON_DATASET_MATCH,
+ "saveDialogNotShowReason");
}
@Override
@@ -521,6 +616,7 @@
+ ", detectedFieldIds=" + Arrays.toString(mDetectedFieldIds)
+ ", detectedFieldClassifications ="
+ Arrays.toString(mDetectedFieldClassifications)
+ + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason
+ "]";
}
}
@@ -562,12 +658,14 @@
(detectedFieldIds != null)
? FieldClassification.readArrayFromParcel(parcel)
: null;
+ final int saveDialogNotShowReason = parcel.readInt();
selection.addEvent(new Event(eventType, datasetId, clientState,
selectedDatasetIds, ignoredDatasets,
changedFieldIds, changedDatasetIds,
manuallyFilledFieldIds, manuallyFilledDatasetIds,
- detectedFieldIds, detectedFieldClassifications));
+ detectedFieldIds, detectedFieldClassifications,
+ saveDialogNotShowReason));
}
return selection;
}
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
index 09d1bb9..96bbf8e 100644
--- a/core/java/android/service/dreams/DreamActivity.java
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.os.Bundle;
-import android.view.WindowInsets;
import com.android.internal.R;
@@ -63,8 +62,6 @@
@Override
public void onResume() {
super.onResume();
- // Hide all insets (nav bar, status bar, etc) when the dream is showing
- getWindow().getInsetsController().hide(WindowInsets.Type.systemBars());
overridePendingTransition(R.anim.dream_activity_open_enter,
R.anim.dream_activity_open_exit);
}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index d2dfb29..859bb51 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -48,6 +48,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
@@ -1061,11 +1062,17 @@
| (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0)
| (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0)
);
+ lp.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mWindow.setAttributes(lp);
// Workaround: Currently low-profile and in-window system bar backgrounds don't go
// along well. Dreams usually don't need such bars anyways, so disable them by default.
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ // Hide all insets when the dream is showing
+ mWindow.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
+ mWindow.setDecorFitsSystemWindows(false);
+
mWindow.getDecorView().addOnAttachStateChangeListener(
new View.OnAttachStateChangeListener() {
@Override
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index cf57e25..4f324f9 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -17,6 +17,7 @@
package android.service.notification;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.content.Context;
import android.net.Uri;
import android.os.Parcel;
@@ -183,7 +184,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof Condition)) return false;
if (o == this) return true;
final Condition other = (Condition) o;
diff --git a/core/java/android/service/notification/ConversationChannelWrapper.java b/core/java/android/service/notification/ConversationChannelWrapper.java
index ab465ab..3d0984c 100644
--- a/core/java/android/service/notification/ConversationChannelWrapper.java
+++ b/core/java/android/service/notification/ConversationChannelWrapper.java
@@ -16,6 +16,7 @@
package android.service.notification;
+import android.annotation.Nullable;
import android.app.NotificationChannel;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Drawable;
@@ -126,7 +127,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ConversationChannelWrapper that = (ConversationChannelWrapper) o;
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 5d34c47..e1b2dfb 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -64,6 +64,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -1802,7 +1803,7 @@
* {@link NotificationAssistantService}
*/
public @NonNull List<Notification.Action> getSmartActions() {
- return mSmartActions;
+ return mSmartActions == null ? Collections.emptyList() : mSmartActions;
}
/**
@@ -1810,7 +1811,7 @@
* {@link NotificationAssistantService}
*/
public @NonNull List<CharSequence> getSmartReplies() {
- return mSmartReplies;
+ return mSmartReplies == null ? Collections.emptyList() : mSmartReplies;
}
/**
@@ -1864,8 +1865,9 @@
}
/**
- * Returns whether this notification is a conversation notification.
- * @hide
+ * Returns whether this notification is a conversation notification, and would appear
+ * in the conversation section of the notification shade, on devices that separate that
+ * type of notification.
*/
public boolean isConversation() {
return mIsConversation;
@@ -1880,7 +1882,10 @@
}
/**
- * @hide
+ * Returns the shortcut information associated with this notification, if it is a
+ * {@link #isConversation() conversation notification}.
+ * <p>This might be null even if the notification is a conversation notification, if
+ * the posting app hasn't opted into the full conversation feature set yet.</p>
*/
public @Nullable ShortcutInfo getShortcutInfo() {
return mShortcutInfo;
@@ -2000,7 +2005,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -2076,7 +2081,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 675c5cd..c64f4c46 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -15,6 +15,7 @@
*/
package android.service.notification;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -42,7 +43,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
diff --git a/core/java/android/service/notification/NotifyingApp.java b/core/java/android/service/notification/NotifyingApp.java
index a4fc5fd..930d144 100644
--- a/core/java/android/service/notification/NotifyingApp.java
+++ b/core/java/android/service/notification/NotifyingApp.java
@@ -16,6 +16,7 @@
package android.service.notification;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -99,7 +100,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NotifyingApp that = (NotifyingApp) o;
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 49bc65b..a9ab33c 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -23,6 +23,7 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.NotificationManager;
@@ -430,7 +431,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof ZenModeConfig)) return false;
if (o == this) return true;
final ZenModeConfig other = (ZenModeConfig) o;
@@ -1527,7 +1528,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof ScheduleInfo)) return false;
final ScheduleInfo other = (ScheduleInfo) o;
return toDayList(days).equals(toDayList(other.days))
@@ -1629,7 +1630,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof EventInfo)) return false;
final EventInfo other = (EventInfo) o;
return userId == other.userId
@@ -1934,7 +1935,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof ZenRule)) return false;
if (o == this) return true;
final ZenRule other = (ZenRule) o;
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 6d0bcff..ed3a9ac 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationChannel;
import android.os.Parcel;
@@ -950,7 +951,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof ZenPolicy)) return false;
if (o == this) return true;
final ZenPolicy other = (ZenPolicy) o;
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 8f8e6cc..b94031a 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -379,16 +379,23 @@
* Callbacks for always-on hotword detection.
*/
public static abstract class Callback {
+
/**
- * Called when the hotword availability changes.
- * This indicates a change in the availability of recognition for the given keyphrase.
- * It's called at least once with the initial availability.<p/>
+ * Updates the availability state of the active keyphrase and locale on every keyphrase
+ * sound model change.
*
- * Availability implies whether the hardware on this system is capable of listening for
- * the given keyphrase or not. <p/>
+ * <p>This API is called whenever there's a possibility that the keyphrase associated
+ * with this detector has been updated. It is not guaranteed that there is in fact any
+ * change, as it may be called for other reasons.</p>
+ *
+ * <p>This API is also guaranteed to be called right after an AlwaysOnHotwordDetector
+ * instance is created to updated the current availability state.</p>
+ *
+ * <p>Availability implies the current enrollment state of the given keyphrase. If the
+ * hardware on this system is not capable of listening for the given keyphrase,
+ * {@link AlwaysOnHotwordDetector#STATE_HARDWARE_UNAVAILABLE} will be returned.
*
* @see AlwaysOnHotwordDetector#STATE_HARDWARE_UNAVAILABLE
- * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNSUPPORTED
* @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNENROLLED
* @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_ENROLLED
*/
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 0341b6d..0b6d371 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -2073,7 +2073,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 0f46ffc..4d1337b 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -227,9 +227,6 @@
SurfaceControl mSurfaceControl = new SurfaceControl();
- // Unused relayout out-param
- SurfaceControl mTmpSurfaceControl = new SurfaceControl();
-
final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
{
mRequestedFormat = PixelFormat.RGBX_8888;
@@ -905,7 +902,7 @@
final int relayoutResult = mSession.relayout(
mWindow, mLayout, mWidth, mHeight,
View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl,
- mInsetsState, mTempControls, mSurfaceSize, mTmpSurfaceControl);
+ mInsetsState, mTempControls, mSurfaceSize);
if (mSurfaceControl.isValid()) {
mSurfaceHolder.mSurface.copyFrom(mSurfaceControl);
}
diff --git a/core/java/android/speech/tts/Voice.java b/core/java/android/speech/tts/Voice.java
index fefc35e..7ffe5eb 100644
--- a/core/java/android/speech/tts/Voice.java
+++ b/core/java/android/speech/tts/Voice.java
@@ -16,6 +16,7 @@
package android.speech.tts;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -208,7 +209,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/telephony/DataConnectionRealTimeInfo.java b/core/java/android/telephony/DataConnectionRealTimeInfo.java
index 8106f5f..8767133 100644
--- a/core/java/android/telephony/DataConnectionRealTimeInfo.java
+++ b/core/java/android/telephony/DataConnectionRealTimeInfo.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -116,7 +117,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index ac27e3d..c5f7f581 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -1607,7 +1607,7 @@
// Same as SpannableStringInternal
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof Spanned &&
toString().equals(o.toString())) {
final Spanned other = (Spanned) o;
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index 4c9328a..0fe9b6a 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -16,6 +16,7 @@
package android.text;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.ArrayUtils;
@@ -501,7 +502,7 @@
// Same as SpannableStringBuilder
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof Spanned &&
toString().equals(o.toString())) {
final Spanned other = (Spanned) o;
diff --git a/core/java/android/text/StyledTextShaper.java b/core/java/android/text/StyledTextShaper.java
deleted file mode 100644
index bf90614..0000000
--- a/core/java/android/text/StyledTextShaper.java
+++ /dev/null
@@ -1,67 +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.text;
-
-import android.annotation.NonNull;
-import android.graphics.Paint;
-import android.graphics.text.PositionedGlyphs;
-import android.graphics.text.TextShaper;
-
-import java.util.List;
-
-/**
- * Provides text shaping for multi-styled text.
- *
- * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
- * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
- * @see StyledTextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint)
- */
-public class StyledTextShaper {
- private StyledTextShaper() {}
-
-
- /**
- * Shape multi-styled text.
- *
- * @param text a styled text.
- * @param start a start index of shaping target in the text.
- * @param count a length of shaping target in the text.
- * @param dir a text direction.
- * @param paint a paint
- * @return a shape result.
- */
- public static @NonNull List<PositionedGlyphs> shapeText(
- @NonNull CharSequence text, int start, int count,
- @NonNull TextDirectionHeuristic dir, @NonNull TextPaint paint) {
- MeasuredParagraph mp = MeasuredParagraph.buildForBidi(
- text, start, start + count, dir, null);
- TextLine tl = TextLine.obtain();
- try {
- tl.set(paint, text, start, start + count,
- mp.getParagraphDir(),
- mp.getDirections(start, start + count),
- false /* tabstop is not supported */,
- null,
- -1, -1 // ellipsis is not supported.
- );
- return tl.shape();
- } finally {
- TextLine.recycle(tl);
- }
- }
-
-}
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index b826832..4471056 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -24,7 +24,7 @@
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.text.PositionedGlyphs;
-import android.graphics.text.TextShaper;
+import android.graphics.text.TextRunShaper;
import android.os.Build;
import android.text.Layout.Directions;
import android.text.Layout.TabStops;
@@ -37,7 +37,6 @@
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
-import java.util.List;
/**
* Represents a line of styled text, for measuring in visual order and
@@ -312,8 +311,7 @@
/**
* Shape the TextLine.
*/
- List<PositionedGlyphs> shape() {
- List<PositionedGlyphs> glyphs = new ArrayList<>();
+ void shape(TextShaper.GlyphsConsumer consumer) {
float horizontal = 0;
float x = 0;
final int runCount = mDirections.getRunCount();
@@ -326,7 +324,7 @@
int segStart = runStart;
for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
if (j == runLimit || charAt(j) == TAB_CHAR) {
- horizontal += shapeRun(glyphs, segStart, j, runIsRtl, x + horizontal,
+ horizontal += shapeRun(consumer, segStart, j, runIsRtl, x + horizontal,
runIndex != (runCount - 1) || j != mLen);
if (j != runLimit) { // charAt(j) == TAB_CHAR
@@ -336,7 +334,6 @@
}
}
}
- return glyphs;
}
/**
@@ -546,7 +543,7 @@
/**
* Shape a unidirectional (but possibly multi-styled) run of text.
*
- * @param glyphs the output positioned glyphs list
+ * @param consumer the consumer of the shape result
* @param start the line-relative start
* @param limit the line-relative limit
* @param runIsRtl true if the run is right-to-left
@@ -555,16 +552,17 @@
* @return the signed width of the run, based on the paragraph direction.
* Only valid if needWidth is true.
*/
- private float shapeRun(List<PositionedGlyphs> glyphs, int start,
+ private float shapeRun(TextShaper.GlyphsConsumer consumer, int start,
int limit, boolean runIsRtl, float x, boolean needWidth) {
if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
float w = -measureRun(start, limit, limit, runIsRtl, null);
- handleRun(start, limit, limit, runIsRtl, null, glyphs, x + w, 0, 0, 0, null, false);
+ handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null, false);
return w;
}
- return handleRun(start, limit, limit, runIsRtl, null, glyphs, x, 0, 0, 0, null, needWidth);
+ return handleRun(start, limit, limit, runIsRtl, null, consumer, x, 0, 0, 0, null,
+ needWidth);
}
@@ -899,7 +897,7 @@
* @param end the end of the text
* @param runIsRtl true if the run is right-to-left
* @param c the canvas, can be null if rendering is not needed
- * @param glyphs the output positioned glyph list, can be null if not necessary
+ * @param consumer the output positioned glyph list, can be null if not necessary
* @param x the edge of the run closest to the leading margin
* @param top the top of the line
* @param y the baseline
@@ -913,7 +911,7 @@
*/
private float handleText(TextPaint wp, int start, int end,
int contextStart, int contextEnd, boolean runIsRtl,
- Canvas c, List<PositionedGlyphs> glyphs, float x, int top, int y, int bottom,
+ Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom,
FontMetricsInt fmi, boolean needWidth, int offset,
@Nullable ArrayList<DecorationInfo> decorations) {
@@ -946,8 +944,8 @@
rightX = x + totalWidth;
}
- if (glyphs != null) {
- shapeTextRun(glyphs, wp, start, end, contextStart, contextEnd, runIsRtl, leftX);
+ if (consumer != null) {
+ shapeTextRun(consumer, wp, start, end, contextStart, contextEnd, runIsRtl, leftX);
}
if (c != null) {
@@ -1135,7 +1133,7 @@
* @param limit the limit of the run
* @param runIsRtl true if the run is right-to-left
* @param c the canvas, can be null
- * @param glyphs the output positioned glyphs, can be null
+ * @param consumer the output positioned glyphs, can be null
* @param x the end of the run closest to the leading margin
* @param top the top of the line
* @param y the baseline
@@ -1147,7 +1145,7 @@
*/
private float handleRun(int start, int measureLimit,
int limit, boolean runIsRtl, Canvas c,
- List<PositionedGlyphs> glyphs, float x, int top, int y,
+ TextShaper.GlyphsConsumer consumer, float x, int top, int y,
int bottom, FontMetricsInt fmi, boolean needWidth) {
if (measureLimit < start || measureLimit > limit) {
@@ -1180,7 +1178,7 @@
wp.set(mPaint);
wp.setStartHyphenEdit(adjustStartHyphenEdit(start, wp.getStartHyphenEdit()));
wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit()));
- return handleText(wp, start, limit, start, limit, runIsRtl, c, glyphs, x, top,
+ return handleText(wp, start, limit, start, limit, runIsRtl, c, consumer, x, top,
y, bottom, fmi, needWidth, measureLimit, null);
}
@@ -1262,7 +1260,7 @@
activePaint.setEndHyphenEdit(
adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c,
- glyphs, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
+ consumer, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
Math.min(activeEnd, mlimit), mDecorations);
activeStart = j;
@@ -1288,7 +1286,7 @@
adjustStartHyphenEdit(activeStart, mPaint.getStartHyphenEdit()));
activePaint.setEndHyphenEdit(
adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
- x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, glyphs, x,
+ x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, consumer, x,
top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
Math.min(activeEnd, mlimit), mDecorations);
}
@@ -1327,7 +1325,7 @@
/**
* Shape a text run with the set-up paint.
*
- * @param glyphs the output positioned glyphs list
+ * @param consumer the output positioned glyphs list
* @param paint the paint used to render the text
* @param start the start of the run
* @param end the end of the run
@@ -1336,30 +1334,32 @@
* @param runIsRtl true if the run is right-to-left
* @param x the x position of the left edge of the run
*/
- private void shapeTextRun(List<PositionedGlyphs> glyphs, TextPaint paint,
+ private void shapeTextRun(TextShaper.GlyphsConsumer consumer, TextPaint paint,
int start, int end, int contextStart, int contextEnd, boolean runIsRtl, float x) {
int count = end - start;
int contextCount = contextEnd - contextStart;
+ PositionedGlyphs glyphs;
if (mCharsValid) {
- glyphs.add(TextShaper.shapeTextRun(
+ glyphs = TextRunShaper.shapeTextRun(
mChars,
start, count,
contextStart, contextCount,
x, 0f,
runIsRtl,
paint
- ));
+ );
} else {
- glyphs.add(TextShaper.shapeTextRun(
+ glyphs = TextRunShaper.shapeTextRun(
mText,
mStart + start, count,
mStart + contextStart, contextCount,
x, 0f,
runIsRtl,
paint
- ));
+ );
}
+ consumer.accept(start, count, glyphs, paint);
}
diff --git a/core/java/android/text/TextShaper.java b/core/java/android/text/TextShaper.java
new file mode 100644
index 0000000..dd25704
--- /dev/null
+++ b/core/java/android/text/TextShaper.java
@@ -0,0 +1,229 @@
+/*
+ * 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.text;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.graphics.Paint;
+import android.graphics.text.PositionedGlyphs;
+import android.graphics.text.TextRunShaper;
+
+/**
+ * Provides text shaping for multi-styled text.
+ *
+ * Here is an example of animating text size and letter spacing for simple text.
+ * <pre>
+ * <code>
+ * // In this example, shape the text once for start and end state, then animate between two shape
+ * // result without re-shaping in each frame.
+ * class SimpleAnimationView @JvmOverloads constructor(
+ * context: Context,
+ * attrs: AttributeSet? = null,
+ * defStyleAttr: Int = 0
+ * ) : View(context, attrs, defStyleAttr) {
+ * private val textDir = TextDirectionHeuristics.LOCALE
+ * private val text = "Hello, World." // The text to be displayed
+ *
+ * // Class for keeping drawing parameters.
+ * data class DrawStyle(val textSize: Float, val alpha: Int)
+ *
+ * // The start and end text shaping result. This class will animate between these two.
+ * private val start = mutableListOf<Pair<PositionedGlyphs, DrawStyle>>()
+ * private val end = mutableListOf<Pair<PositionedGlyphs, DrawStyle>>()
+ *
+ * init {
+ * val startPaint = TextPaint().apply {
+ * alpha = 0 // Alpha only affect text drawing but not text shaping
+ * textSize = 36f // TextSize affect both text shaping and drawing.
+ * letterSpacing = 0f // Letter spacing only affect text shaping but not drawing.
+ * }
+ *
+ * val endPaint = TextPaint().apply {
+ * alpha = 255
+ * textSize =128f
+ * letterSpacing = 0.1f
+ * }
+ *
+ * TextShaper.shapeText(text, 0, text.length, textDir, startPaint) { _, _, glyphs, paint ->
+ * start.add(Pair(glyphs, DrawStyle(paint.textSize, paint.alpha)))
+ * }
+ * TextShaper.shapeText(text, 0, text.length, textDir, endPaint) { _, _, glyphs, paint ->
+ * end.add(Pair(glyphs, DrawStyle(paint.textSize, paint.alpha)))
+ * }
+ * }
+ *
+ * override fun onDraw(canvas: Canvas) {
+ * super.onDraw(canvas)
+ *
+ * // Set the baseline to the vertical center of the view.
+ * canvas.translate(0f, height / 2f)
+ *
+ * // Assume the number of PositionedGlyphs are the same. If different, you may want to
+ * // animate in a different way, e.g. cross fading.
+ * start.zip(end) { (startGlyphs, startDrawStyle), (endGlyphs, endDrawStyle) ->
+ * // Tween the style and set to paint.
+ * paint.textSize = lerp(startDrawStyle.textSize, endDrawStyle.textSize, progress)
+ * paint.alpha = lerp(startDrawStyle.alpha, endDrawStyle.alpha, progress)
+ *
+ * // Assume the number of glyphs are the same. If different, you may want to animate in
+ * // a different way, e.g. cross fading.
+ * require(startGlyphs.glyphCount() == endGlyphs.glyphCount())
+ *
+ * if (startGlyphs.glyphCount() == 0) return@zip
+ *
+ * var curFont = startGlyphs.getFont(0)
+ * var drawStart = 0
+ * for (i in 1 until startGlyphs.glyphCount()) {
+ * // Assume the pair of Glyph ID and font is the same. If different, you may want
+ * // to animate in a different way, e.g. cross fading.
+ * require(startGlyphs.getGlyphId(i) == endGlyphs.getGlyphId(i))
+ * require(startGlyphs.getFont(i) === endGlyphs.getFont(i))
+ *
+ * val font = startGlyphs.getFont(i)
+ * if (curFont != font) {
+ * drawGlyphs(canvas, startGlyphs, endGlyphs, drawStart, i, curFont, paint)
+ * curFont = font
+ * drawStart = i
+ * }
+ * }
+ * if (drawStart != startGlyphs.glyphCount() - 1) {
+ * drawGlyphs(canvas, startGlyphs, endGlyphs, drawStart, startGlyphs.glyphCount(),
+ * curFont, paint)
+ * }
+ * }
+ * }
+ *
+ * // Draws Glyphs for the same font run.
+ * private fun drawGlyphs(canvas: Canvas, startGlyph: PositionedGlyphs,
+ * endGlyph: PositionedGlyphs, start: Int, end: Int, font: Font,
+ * paint: Paint) {
+ * var cacheIndex = 0
+ * for (i in start until end) {
+ * intArrayCache[cacheIndex] = startGlyph.getGlyphId(i)
+ * // The glyph positions are different from start to end since they are shaped
+ * // with different letter spacing. Use linear interpolation for positions
+ * // during animation.
+ * floatArrayCache[cacheIndex * 2] =
+ * lerp(startGlyph.getGlyphX(i), endGlyph.getGlyphX(i), progress)
+ * floatArrayCache[cacheIndex * 2 + 1] =
+ * lerp(startGlyph.getGlyphY(i), endGlyph.getGlyphY(i), progress)
+ * if (cacheIndex == CACHE_SIZE) { // Cached int array is full. Flashing.
+ * canvas.drawGlyphs(
+ * intArrayCache, 0, // glyphID array and its starting offset
+ * floatArrayCache, 0, // position array and its starting offset
+ * cacheIndex, // glyph count
+ * font,
+ * paint
+ * )
+ * cacheIndex = 0
+ * }
+ * cacheIndex++
+ * }
+ * if (cacheIndex != 0) {
+ * canvas.drawGlyphs(
+ * intArrayCache, 0, // glyphID array and its starting offset
+ * floatArrayCache, 0, // position array and its starting offset
+ * cacheIndex, // glyph count
+ * font,
+ * paint
+ * )
+ * }
+ * }
+ *
+ * // Linear Interpolator
+ * private fun lerp(start: Float, end: Float, t: Float) = start * (1f - t) + end * t
+ * private fun lerp(start: Int, end: Int, t: Float) = (start * (1f - t) + end * t).toInt()
+ *
+ * // The animation progress.
+ * var progress: Float = 0f
+ * set(value) {
+ * field = value
+ * invalidate()
+ * }
+ *
+ * // working copy of paint.
+ * private val paint = Paint()
+ *
+ * // Array cache for reducing allocation during drawing.
+ * private var intArrayCache = IntArray(CACHE_SIZE)
+ * private var floatArrayCache = FloatArray(CACHE_SIZE * 2)
+ * }
+ * </code>
+ * </pre>
+ * @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
+ * @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
+ * @see TextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint,
+ * GlyphsConsumer)
+ */
+public class TextShaper {
+ private TextShaper() {}
+
+ /**
+ * An consumer interface for accepting text shape result.
+ */
+ public interface GlyphsConsumer {
+ /**
+ * Accept text shape result.
+ *
+ * The implementation must not keep reference of paint since it will be mutated for the
+ * subsequent styles. Also, for saving heap size, keep only necessary members in the
+ * {@link TextPaint} instead of copying {@link TextPaint} object.
+ *
+ * @param start The start index of the shaped text.
+ * @param count The length of the shaped text.
+ * @param glyphs The shape result.
+ * @param paint The paint to be used for drawing.
+ */
+ void accept(
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int count,
+ @NonNull PositionedGlyphs glyphs,
+ @NonNull TextPaint paint);
+ }
+
+ /**
+ * Shape multi-styled text.
+ *
+ * @param text a styled text.
+ * @param start a start index of shaping target in the text.
+ * @param count a length of shaping target in the text.
+ * @param dir a text direction.
+ * @param paint a paint
+ * @param consumer a consumer of the shape result.
+ */
+ public static void shapeText(
+ @NonNull CharSequence text, @IntRange(from = 0) int start,
+ @IntRange(from = 0) int count, @NonNull TextDirectionHeuristic dir,
+ @NonNull TextPaint paint, @NonNull GlyphsConsumer consumer) {
+ MeasuredParagraph mp = MeasuredParagraph.buildForBidi(
+ text, start, start + count, dir, null);
+ TextLine tl = TextLine.obtain();
+ try {
+ tl.set(paint, text, start, start + count,
+ mp.getParagraphDir(),
+ mp.getDirections(start, start + count),
+ false /* tabstop is not supported */,
+ null,
+ -1, -1 // ellipsis is not supported.
+ );
+ tl.shape(consumer);
+ } finally {
+ TextLine.recycle(tl);
+ }
+ }
+
+}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index d441e6b..72b35b9 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -2080,6 +2080,94 @@
}
/**
+ * Simple alternative to {@link String#format} which purposefully supports
+ * only a small handful of substitutions to improve execution speed.
+ * Benchmarking reveals this optimized alternative performs 6.5x faster for
+ * a typical format string.
+ * <p>
+ * Below is a summary of the limited grammar supported by this method; if
+ * you need advanced features, please continue using {@link String#format}.
+ * <ul>
+ * <li>{@code %b} for {@code boolean}
+ * <li>{@code %c} for {@code char}
+ * <li>{@code %d} for {@code int} or {@code long}
+ * <li>{@code %f} for {@code float} or {@code double}
+ * <li>{@code %s} for {@code String}
+ * <li>{@code %x} for hex representation of {@code int} or {@code long}
+ * <li>{@code %%} for literal {@code %}
+ * </ul>
+ *
+ * @throws IllegalArgumentException if the format string or arguments don't
+ * match the supported grammar described above.
+ * @hide
+ */
+ public static @NonNull String formatSimple(@NonNull String format, Object... args) {
+ final StringBuilder sb = new StringBuilder(format);
+ int j = 0;
+ for (int i = 0; i < sb.length(); ) {
+ if (sb.charAt(i) == '%') {
+ final String repl;
+ final char code = sb.charAt(i + 1);
+ switch (code) {
+ case 'b': {
+ if (j == args.length) {
+ throw new IllegalArgumentException("Too few arguments");
+ }
+ final Object arg = args[j++];
+ if (arg instanceof Boolean) {
+ repl = Boolean.toString((boolean) arg);
+ } else {
+ repl = Boolean.toString(arg != null);
+ }
+ break;
+ }
+ case 'c':
+ case 'd':
+ case 'f':
+ case 's': {
+ if (j == args.length) {
+ throw new IllegalArgumentException("Too few arguments");
+ }
+ final Object arg = args[j++];
+ repl = String.valueOf(arg);
+ break;
+ }
+ case 'x': {
+ if (j == args.length) {
+ throw new IllegalArgumentException("Too few arguments");
+ }
+ final Object arg = args[j++];
+ if (arg instanceof Integer) {
+ repl = Integer.toHexString((int) arg);
+ } else if (arg instanceof Long) {
+ repl = Long.toHexString((long) arg);
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported hex type " + arg.getClass());
+ }
+ break;
+ }
+ case '%': {
+ repl = "%";
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unsupported format code " + code);
+ }
+ }
+ sb.replace(i, i + 2, repl);
+ i += repl.length();
+ } else {
+ i++;
+ }
+ }
+ if (j != args.length) {
+ throw new IllegalArgumentException("Too many arguments");
+ }
+ return sb.toString();
+ }
+
+ /**
* Returns whether or not the specified spanned text has a style span.
* @hide
*/
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 38e3b39..4a0bec1 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -17,10 +17,14 @@
package android.text.format;
import android.annotation.NonNull;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.icu.text.DateFormatSymbols;
import android.icu.text.DateTimePatternGenerator;
+import android.os.Build;
import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
@@ -159,6 +163,16 @@
private static boolean sIs24Hour;
/**
+ * {@link #getBestDateTimePattern(Locale, String)} does not allow non-consecutive repeated
+ * symbol in the skeleton. For example, please use a skeleton of {@code "jmm"} or
+ * {@code "hmma"} instead of {@code "ahmma"} or {@code "jmma"}, because the field 'j' could
+ * mean using 12-hour in some locales and, in this case, is duplicated as the 'a' field.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ static final long DISALLOW_DUPLICATE_FIELD_IN_SKELETON = 170233598L;
+
+ /**
* Returns true if times should be formatted as 24 hour times, false if times should be
* formatted as 12 hour (AM/PM) times. Based on the user's chosen locale and other preferences.
* @param context the context to use for the content resolver
@@ -251,7 +265,9 @@
*/
public static String getBestDateTimePattern(Locale locale, String skeleton) {
DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(locale);
- return dtpg.getBestPattern(skeleton);
+ boolean allowDuplicateFields = !CompatChanges.isChangeEnabled(
+ DISALLOW_DUPLICATE_FIELD_IN_SKELETON);
+ return dtpg.getBestPattern(skeleton, allowDuplicateFields);
}
/**
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index ac8005b..9378636 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -349,7 +349,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof SuggestionSpan) {
return ((SuggestionSpan)o).hashCode() == mHashCode;
}
diff --git a/core/java/android/text/util/Rfc822Token.java b/core/java/android/text/util/Rfc822Token.java
index 058757a..2f207db 100644
--- a/core/java/android/text/util/Rfc822Token.java
+++ b/core/java/android/text/util/Rfc822Token.java
@@ -191,7 +191,7 @@
}
}
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof Rfc822Token)) {
return false;
}
diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
index 44d1402..d0bcfda 100644
--- a/core/java/android/timezone/CountryTimeZones.java
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -66,7 +66,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -120,7 +120,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
@@ -272,7 +272,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/timezone/TelephonyNetwork.java b/core/java/android/timezone/TelephonyNetwork.java
index 3b65c6f..161ecb4 100644
--- a/core/java/android/timezone/TelephonyNetwork.java
+++ b/core/java/android/timezone/TelephonyNetwork.java
@@ -17,6 +17,7 @@
package android.timezone;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import java.util.Objects;
@@ -59,7 +60,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/timezone/TzDataSetVersion.java b/core/java/android/timezone/TzDataSetVersion.java
index e1fb932..edcbbb3 100644
--- a/core/java/android/timezone/TzDataSetVersion.java
+++ b/core/java/android/timezone/TzDataSetVersion.java
@@ -17,6 +17,7 @@
package android.timezone;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -128,7 +129,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
diff --git a/core/java/android/transition/ChangeTransform.java b/core/java/android/transition/ChangeTransform.java
index 5303855..02d0a6a 100644
--- a/core/java/android/transition/ChangeTransform.java
+++ b/core/java/android/transition/ChangeTransform.java
@@ -20,6 +20,7 @@
import android.animation.FloatArrayEvaluator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Matrix;
@@ -450,7 +451,7 @@
}
@Override
- public boolean equals(Object that) {
+ public boolean equals(@Nullable Object that) {
if (!(that instanceof Transforms)) {
return false;
}
diff --git a/core/java/android/transition/TransitionValues.java b/core/java/android/transition/TransitionValues.java
index 85fa67d..bb68b4c 100644
--- a/core/java/android/transition/TransitionValues.java
+++ b/core/java/android/transition/TransitionValues.java
@@ -17,6 +17,7 @@
package android.transition;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.util.ArrayMap;
import android.view.View;
import android.view.ViewGroup;
@@ -73,7 +74,7 @@
final ArrayList<Transition> targetedTransitions = new ArrayList<Transition>();
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (other instanceof TransitionValues) {
if (view == ((TransitionValues) other).view) {
if (values.equals(((TransitionValues) other).values)) {
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 4cf0a36..4edff27 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -16,6 +16,7 @@
package android.util;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.ArrayUtils;
@@ -826,7 +827,7 @@
* equal, the method returns false, otherwise it returns true.
*/
@Override
- public boolean equals(Object object) {
+ public boolean equals(@Nullable Object object) {
if (this == object) {
return true;
}
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 7f652ba..f53548a 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -778,7 +778,7 @@
* returns true.
*/
@Override
- public boolean equals(Object object) {
+ public boolean equals(@Nullable Object object) {
if (this == object) {
return true;
}
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 9f6065e..0a3e6b1 100755
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -16,10 +16,10 @@
package android.util;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.SystemProperties;
-
/**
* A structure describing general information about a display, such as its
* size, density, and font scaling.
@@ -362,7 +362,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return o instanceof DisplayMetrics && equals((DisplayMetrics)o);
}
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index ead4e46..ee98b65 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -307,7 +307,7 @@
* @hide
*/
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
// Not using ByteBuffer.equals since it takes buffer position into account and we
// always use absolute positions here.
if (this == o) return true;
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 7a117f1..12bcd8b 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -44,9 +44,7 @@
* You can then <a href="{@docRoot}studio/debug/am-logcat.html">view the logs in logcat</a>.
*
* <p>The order in terms of verbosity, from least to most is
- * ERROR, WARN, INFO, DEBUG, VERBOSE. Verbose should never be compiled
- * into an application except during development. Debug logs are compiled
- * in but stripped at runtime. Error, warning and info logs are always kept.
+ * ERROR, WARN, INFO, DEBUG, VERBOSE.
*
* <p><b>Tip:</b> A good convention is to declare a <code>TAG</code> constant
* in your class:
diff --git a/core/java/android/util/MapCollections.java b/core/java/android/util/MapCollections.java
index a521268..7ab3fca 100644
--- a/core/java/android/util/MapCollections.java
+++ b/core/java/android/util/MapCollections.java
@@ -16,6 +16,8 @@
package android.util;
+import android.annotation.Nullable;
+
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
@@ -249,7 +251,7 @@
}
@Override
- public boolean equals(Object object) {
+ public boolean equals(@Nullable Object object) {
return equalsSetHelper(this, object);
}
@@ -339,7 +341,7 @@
}
@Override
- public boolean equals(Object object) {
+ public boolean equals(@Nullable Object object) {
return equalsSetHelper(this, object);
}
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index 7d287e3..9073d3a 100644
--- a/core/java/android/util/MemoryIntArray.java
+++ b/core/java/android/util/MemoryIntArray.java
@@ -16,13 +16,15 @@
package android.util;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
-import libcore.io.IoUtils;
import dalvik.system.CloseGuard;
+import libcore.io.IoUtils;
+
import java.io.Closeable;
import java.io.IOException;
import java.util.UUID;
@@ -183,7 +185,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj == null) {
return false;
}
diff --git a/core/java/android/util/MergedConfiguration.java b/core/java/android/util/MergedConfiguration.java
index 2399ada..3ac44aa 100644
--- a/core/java/android/util/MergedConfiguration.java
+++ b/core/java/android/util/MergedConfiguration.java
@@ -17,6 +17,7 @@
package android.util;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.Configuration;
import android.os.Parcel;
import android.os.Parcelable;
@@ -167,7 +168,7 @@
}
@Override
- public boolean equals(Object that) {
+ public boolean equals(@Nullable Object that) {
if (!(that instanceof MergedConfiguration)) {
return false;
}
diff --git a/core/java/android/util/Pair.java b/core/java/android/util/Pair.java
index f96da72..b0866b4 100644
--- a/core/java/android/util/Pair.java
+++ b/core/java/android/util/Pair.java
@@ -16,6 +16,8 @@
package android.util;
+import android.annotation.Nullable;
+
import java.util.Objects;
/**
@@ -47,7 +49,7 @@
* equal
*/
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof Pair)) {
return false;
}
diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java
index f31ddd9..9fd0ab9 100644
--- a/core/java/android/util/Range.java
+++ b/core/java/android/util/Range.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.Preconditions.*;
+import android.annotation.Nullable;
import android.hardware.camera2.utils.HashCodeHelpers;
/**
@@ -146,7 +147,7 @@
* @return {@code true} if the ranges are equal, {@code false} otherwise
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj == null) {
return false;
} else if (this == obj) {
diff --git a/core/java/android/util/Rational.java b/core/java/android/util/Rational.java
index 5930000..aade620 100644
--- a/core/java/android/util/Rational.java
+++ b/core/java/android/util/Rational.java
@@ -17,6 +17,7 @@
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import java.io.IOException;
@@ -240,7 +241,7 @@
* @return A boolean that determines whether or not the two Rational objects are equal.
*/
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
return obj instanceof Rational && equals((Rational) obj);
}
diff --git a/core/java/android/util/RecurrenceRule.java b/core/java/android/util/RecurrenceRule.java
index a570e5e..0f2d8bc 100644
--- a/core/java/android/util/RecurrenceRule.java
+++ b/core/java/android/util/RecurrenceRule.java
@@ -16,6 +16,7 @@
package android.util;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -130,7 +131,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (obj instanceof RecurrenceRule) {
final RecurrenceRule other = (RecurrenceRule) obj;
return Objects.equals(start, other.start)
diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java
index 846df39..c145b20 100644
--- a/core/java/android/util/SparseBooleanArray.java
+++ b/core/java/android/util/SparseBooleanArray.java
@@ -16,6 +16,7 @@
package android.util;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import com.android.internal.util.ArrayUtils;
@@ -289,7 +290,7 @@
}
@Override
- public boolean equals(Object that) {
+ public boolean equals(@Nullable Object that) {
if (this == that) {
return true;
}
diff --git a/core/java/android/util/apk/VerbatimX509Certificate.java b/core/java/android/util/apk/VerbatimX509Certificate.java
index 391c5fc..d9a00b2 100644
--- a/core/java/android/util/apk/VerbatimX509Certificate.java
+++ b/core/java/android/util/apk/VerbatimX509Certificate.java
@@ -16,6 +16,8 @@
package android.util.apk;
+import android.annotation.Nullable;
+
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
@@ -39,7 +41,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (!(o instanceof VerbatimX509Certificate)) return false;
diff --git a/core/java/android/util/jar/StrictJarManifest.java b/core/java/android/util/jar/StrictJarManifest.java
index faec099..5c2fd9e 100644
--- a/core/java/android/util/jar/StrictJarManifest.java
+++ b/core/java/android/util/jar/StrictJarManifest.java
@@ -17,6 +17,10 @@
package android.util.jar;
+import android.annotation.Nullable;
+
+import libcore.io.Streams;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -29,7 +33,6 @@
import java.util.Iterator;
import java.util.Map;
import java.util.jar.Attributes;
-import libcore.io.Streams;
/**
* The {@code StrictJarManifest} class is used to obtain attribute information for a
@@ -219,7 +222,7 @@
* @return {@code true} if the manifests are equal, {@code false} otherwise
*/
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == null) {
return false;
}
diff --git a/core/java/android/uwb/UwbAddress.java b/core/java/android/uwb/UwbAddress.java
new file mode 100644
index 0000000..48fcb10e
--- /dev/null
+++ b/core/java/android/uwb/UwbAddress.java
@@ -0,0 +1,82 @@
+/*
+ * 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.uwb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * A class representing a UWB address
+ *
+ * @hide
+ */
+public final class UwbAddress {
+ public static final int SHORT_ADDRESS_BYTE_LENGTH = 2;
+ public static final int EXTENDED_ADDRESS_BYTE_LENGTH = 8;
+
+ /**
+ * Create a {@link UwbAddress} from a byte array.
+ *
+ * <p>If the provided array is {@link #SHORT_ADDRESS_BYTE_LENGTH} bytes, a short address is
+ * created. If the provided array is {@link #EXTENDED_ADDRESS_BYTE_LENGTH} bytes, then an
+ * extended address is created.
+ *
+ * @param address a byte array to convert to a {@link UwbAddress}
+ * @return a {@link UwbAddress} created from the input byte array
+ * @throw IllegableArumentException when the length is not one of
+ * {@link #SHORT_ADDRESS_BYTE_LENGTH} or {@link #EXTENDED_ADDRESS_BYTE_LENGTH} bytes
+ */
+ @NonNull
+ public static UwbAddress fromBytes(byte[] address) throws IllegalArgumentException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Get the address as a byte array
+ *
+ * @return the byte representation of this {@link UwbAddress}
+ */
+ @NonNull
+ public byte[] toBytes() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * The length of the address in bytes
+ * <p>Possible values are {@link #SHORT_ADDRESS_BYTE_LENGTH} and
+ * {@link #EXTENDED_ADDRESS_BYTE_LENGTH}.
+ */
+ public int size() {
+ throw new UnsupportedOperationException();
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int hashCode() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index e520d7c..b5080cd 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -181,7 +181,8 @@
private long mFrameIntervalNanos;
private boolean mDebugPrintNextFrameTimeDelta;
private int mFPSDivisor = 1;
- private long mLastVsyncId = FrameInfo.INVALID_VSYNC_ID;
+ private DisplayEventReceiver.VsyncEventData mLastVsyncEventData =
+ new DisplayEventReceiver.VsyncEventData();
/**
* Contains information about the current frame for jank-tracking,
@@ -664,7 +665,18 @@
* @hide
*/
public long getVsyncId() {
- return mLastVsyncId;
+ return mLastVsyncEventData.id;
+ }
+
+ /**
+ * Returns the frame deadline in {@link System#nanoTime()} timebase that it is allotted for the
+ * frame to be completed. Client are expected to call this function from their frame callback
+ * function. Calling this function from anywhere else will return an undefined value.
+ *
+ * @hide
+ */
+ public long getFrameDeadline() {
+ return mLastVsyncEventData.frameDeadline;
}
void setFPSDivisor(int divisor) {
@@ -673,7 +685,8 @@
ThreadedRenderer.setFPSDivisor(divisor);
}
- void doFrame(long frameTimeNanos, int frame, long frameTimelineVsyncId) {
+ void doFrame(long frameTimeNanos, int frame,
+ DisplayEventReceiver.VsyncEventData vsyncEventData) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
@@ -723,10 +736,11 @@
}
}
- mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, frameTimelineVsyncId);
+ mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
+ vsyncEventData.frameDeadline);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
- mLastVsyncId = frameTimelineVsyncId;
+ mLastVsyncEventData = vsyncEventData;
}
try {
@@ -910,7 +924,7 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
- doFrame(System.nanoTime(), 0, FrameInfo.INVALID_VSYNC_ID);
+ doFrame(System.nanoTime(), 0, new DisplayEventReceiver.VsyncEventData());
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
@@ -927,7 +941,7 @@
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
- private long mFrameTimelineVsyncId;
+ private VsyncEventData mLastVsyncEventData = new VsyncEventData();
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS);
@@ -938,7 +952,7 @@
// for the internal display implicitly.
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
- long frameTimelineVsyncId) {
+ 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
@@ -961,7 +975,7 @@
mTimestampNanos = timestampNanos;
mFrame = frame;
- mFrameTimelineVsyncId = frameTimelineVsyncId;
+ mLastVsyncEventData = vsyncEventData;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
@@ -970,7 +984,7 @@
@Override
public void run() {
mHavePendingVsync = false;
- doFrame(mTimestampNanos, mFrame, mFrameTimelineVsyncId);
+ doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
}
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index c4048e5..34e8221 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1466,7 +1466,7 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
@@ -1620,7 +1620,7 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
diff --git a/core/java/android/view/DisplayAddress.java b/core/java/android/view/DisplayAddress.java
index 92f1adc..91a24c6 100644
--- a/core/java/android/view/DisplayAddress.java
+++ b/core/java/android/view/DisplayAddress.java
@@ -110,7 +110,7 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
return other instanceof Physical
&& mPhysicalDisplayId == ((Physical) other).mPhysicalDisplayId;
}
@@ -171,7 +171,7 @@
private final String mMacAddress;
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
return other instanceof Network && mMacAddress.equals(((Network) other).mMacAddress);
}
diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java
index 7c01f7a8..5d5771c 100644
--- a/core/java/android/view/DisplayAdjustments.java
+++ b/core/java/android/view/DisplayAdjustments.java
@@ -168,7 +168,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof DisplayAdjustments)) {
return false;
}
@@ -220,7 +220,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof FixedRotationAdjustments)) {
return false;
}
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index b4863f9..3f2dd4d 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -205,7 +205,7 @@
return result;
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == this) {
return true;
}
@@ -535,7 +535,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == this) {
return true;
}
@@ -877,7 +877,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return o instanceof ParcelableWrapper
&& mInner.equals(((ParcelableWrapper) o).mInner);
}
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 51474d3..467d93e 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -17,6 +17,7 @@
package android.view;
import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.FrameInfo;
import android.os.Looper;
import android.os.MessageQueue;
import android.util.Log;
@@ -145,6 +146,26 @@
mMessageQueue = null;
}
+ static final class VsyncEventData {
+ // The frame timeline vsync id, used to correlate a frame
+ // produced by HWUI with the timeline data stored in Surface Flinger.
+ public final long id;
+
+ // The frame deadline timestamp in {@link System#nanoTime()} timebase that it is
+ // allotted for the frame to be completed.
+ public final long frameDeadline;
+
+ VsyncEventData(long id, long frameDeadline) {
+ this.id = id;
+ this.frameDeadline = frameDeadline;
+ }
+
+ VsyncEventData() {
+ this.id = FrameInfo.INVALID_VSYNC_ID;
+ this.frameDeadline = Long.MAX_VALUE;
+ }
+ }
+
/**
* Called when a vertical sync pulse is received.
* The recipient should render a frame and then call {@link #scheduleVsync}
@@ -154,11 +175,10 @@
* timebase.
* @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
* @param frame The frame number. Increases by one for each vertical sync interval.
- * @param frameTimelineVsyncId The frame timeline vsync id, used to correlate a frame
- * produced by HWUI with the timeline data stored in Surface Flinger.
+ * @param vsyncEventData The vsync event data.
*/
public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
- long frameTimelineVsyncId) {
+ VsyncEventData vsyncEventData) {
}
/**
@@ -201,8 +221,9 @@
// Called from native code.
@SuppressWarnings("unused")
private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
- long frameTimelineVsyncId) {
- onVsync(timestampNanos, physicalDisplayId, frame, frameTimelineVsyncId);
+ long frameTimelineVsyncId, long frameDeadline) {
+ onVsync(timestampNanos, physicalDisplayId, frame,
+ new VsyncEventData(frameTimelineVsyncId, frameDeadline));
}
// Called from native code.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index b1ede41..fe9a1a7 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -295,7 +295,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return o instanceof DisplayInfo && equals((DisplayInfo)o);
}
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 280a1c0..387787e 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -198,6 +198,7 @@
Index.ANIMATION_START,
Index.PERFORM_TRAVERSALS_START,
Index.DRAW_START,
+ Index.FRAME_DEADLINE,
Index.SYNC_QUEUED,
Index.SYNC_START,
Index.ISSUE_DRAW_COMMANDS_START,
@@ -216,13 +217,15 @@
int ANIMATION_START = 7;
int PERFORM_TRAVERSALS_START = 8;
int DRAW_START = 9;
- int SYNC_QUEUED = 10;
- int SYNC_START = 11;
- int ISSUE_DRAW_COMMANDS_START = 12;
- int SWAP_BUFFERS = 13;
- int FRAME_COMPLETED = 14;
+ int FRAME_DEADLINE = 10;
+ int SYNC_QUEUED = 11;
+ int SYNC_START = 12;
+ int ISSUE_DRAW_COMMANDS_START = 13;
+ int SWAP_BUFFERS = 14;
+ int FRAME_COMPLETED = 15;
- int FRAME_STATS_COUNT = 18; // must always be last
+ int FRAME_STATS_COUNT = 19; // must always be last and in sync with
+ // FrameInfoIndex::NumIndexes in libs/hwui/FrameInfo.h
}
/*
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index daab70a..c460f74 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -337,11 +337,16 @@
*/
boolean isDisplayRotationFrozen(int displayId);
- /**
+ /**
* Sets if display rotation is fixed to user specified value for given displayId.
*/
void setFixedToUserRotation(int displayId, int fixedToUserRotation);
+ /**
+ * Sets if all requested fixed orientation should be ignored for given displayId.
+ */
+ void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest);
+
/**
* Screenshot the current wallpaper layer, including the whole screen.
*/
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 69a5faf..910fd90 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -98,9 +98,6 @@
* @param outSurface Object in which is placed the new display surface.
* @param insetsState The current insets state in the system.
* @param outSurfaceSize The width and height of the surface control
- * @param outBlastSurfaceControl A BLAST SurfaceControl allocated by the WindowManager
- * the SurfaceControl willl be managed by the client side, but the WindowManager
- * may use it as a deferTransaction barrier.
*
* @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
* {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
@@ -110,7 +107,7 @@
int flags, long frameNumber, out ClientWindowFrames outFrames,
out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
out InsetsState insetsState, out InsetsSourceControl[] activeControls,
- out Point outSurfaceSize, out SurfaceControl outBlastSurfaceControl);
+ out Point outSurfaceSize);
/*
* Notify the window manager that an application is relaunching and
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index ec54007..7a90bc0 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -217,7 +217,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return equals(o, false);
}
@@ -225,7 +225,7 @@
* @param excludeInvisibleImeFrames If {@link InsetsState#ITYPE_IME} frames should be ignored
* when IME is not visible.
*/
- public boolean equals(Object o, boolean excludeInvisibleImeFrames) {
+ public boolean equals(@Nullable Object o, boolean excludeInvisibleImeFrames) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index eabb718..38441d1 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -606,7 +606,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return equals(o, false, false);
}
@@ -621,7 +621,7 @@
* @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise.
*/
@VisibleForTesting
- public boolean equals(Object o, boolean excludingCaptionInsets,
+ public boolean equals(@Nullable Object o, boolean excludingCaptionInsets,
boolean excludeInvisibleImeFrames) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
diff --git a/core/java/android/view/KeyEvent.aidl b/core/java/android/view/KeyEvent.aidl
index dc15ecf..1f6d16e 100644
--- a/core/java/android/view/KeyEvent.aidl
+++ b/core/java/android/view/KeyEvent.aidl
@@ -17,4 +17,4 @@
package android.view;
-parcelable KeyEvent;
+@JavaOnlyStableParcelable parcelable KeyEvent;
diff --git a/core/java/android/view/MagnificationSpec.java b/core/java/android/view/MagnificationSpec.java
index aea337e..034598a 100644
--- a/core/java/android/view/MagnificationSpec.java
+++ b/core/java/android/view/MagnificationSpec.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pools.SynchronizedPool;
@@ -106,7 +107,7 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 51b0c6b..abb82bc 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -21,6 +21,7 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Matrix;
@@ -4160,7 +4161,7 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (other instanceof PointerProperties) {
return equals((PointerProperties)other);
}
diff --git a/core/java/android/view/OnReceiveContentCallback.java b/core/java/android/view/OnReceiveContentCallback.java
index 73bcb93..a217ff6 100644
--- a/core/java/android/view/OnReceiveContentCallback.java
+++ b/core/java/android/view/OnReceiveContentCallback.java
@@ -134,46 +134,52 @@
final class Payload {
/**
- * Specifies the UI through which content is being inserted.
+ * Specifies the UI through which content is being inserted. Future versions of Android may
+ * support additional values.
*
* @hide
*/
- @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD,
+ @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_APP, SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD,
SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT})
@Retention(RetentionPolicy.SOURCE)
public @interface Source {}
/**
+ * Specifies that the operation was triggered by the app that contains the target view.
+ */
+ public static final int SOURCE_APP = 0;
+
+ /**
* Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or
* "Paste as plain text" action in the insertion/selection menu).
*/
- public static final int SOURCE_CLIPBOARD = 0;
+ public static final int SOURCE_CLIPBOARD = 1;
/**
* Specifies that the operation was triggered from the soft keyboard (also known as input
* method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard
* for more info.
*/
- public static final int SOURCE_INPUT_METHOD = 1;
+ public static final int SOURCE_INPUT_METHOD = 2;
/**
* Specifies that the operation was triggered by the drag/drop framework. See
* https://developer.android.com/guide/topics/ui/drag-drop for more info.
*/
- public static final int SOURCE_DRAG_AND_DROP = 2;
+ public static final int SOURCE_DRAG_AND_DROP = 3;
/**
* Specifies that the operation was triggered by the autofill framework. See
* https://developer.android.com/guide/topics/text/autofill for more info.
*/
- public static final int SOURCE_AUTOFILL = 3;
+ public static final int SOURCE_AUTOFILL = 4;
/**
* Specifies that the operation was triggered by a result from a
* {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection
* menu.
*/
- public static final int SOURCE_PROCESS_TEXT = 4;
+ public static final int SOURCE_PROCESS_TEXT = 5;
/**
* Returns the symbolic name of the given source.
@@ -182,6 +188,7 @@
*/
static String sourceToString(@Source int source) {
switch (source) {
+ case SOURCE_APP: return "SOURCE_APP";
case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD";
case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD";
case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP";
@@ -217,37 +224,11 @@
return String.valueOf(flags);
}
- /**
- * The data to be inserted.
- */
@NonNull private final ClipData mClip;
-
- /**
- * The source of the operation. See {@code SOURCE_} constants.
- */
private final @Source int mSource;
-
- /**
- * Optional flags that control the insertion behavior. See {@code FLAG_} constants.
- */
private final @Flags int mFlags;
-
- /**
- * Optional http/https URI for the content that may be provided by the IME. This is only
- * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty
- * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the
- * IME.
- */
- @Nullable
- private final Uri mLinkUri;
-
- /**
- * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will
- * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by
- * the IME.
- */
- @Nullable
- private final Bundle mExtras;
+ @Nullable private final Uri mLinkUri;
+ @Nullable private final Bundle mExtras;
private Payload(Builder b) {
this.mClip = Objects.requireNonNull(b.mClip);
@@ -278,7 +259,8 @@
}
/**
- * The source of the operation. See {@code SOURCE_} constants.
+ * The source of the operation. See {@code SOURCE_} constants. Future versions of Android
+ * may pass additional values.
*/
public @Source int getSource() {
return mSource;
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 18d0d7b..f60d98b 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -17,6 +17,7 @@
package android.view;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.XmlRes;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -405,7 +406,7 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
diff --git a/core/java/android/view/RemoteAccessibilityController.java b/core/java/android/view/RemoteAccessibilityController.java
new file mode 100644
index 0000000..bc0fab1b
--- /dev/null
+++ b/core/java/android/view/RemoteAccessibilityController.java
@@ -0,0 +1,168 @@
+/*
+ * 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.view;
+
+import android.graphics.Matrix;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
+
+class RemoteAccessibilityController {
+ private static final String TAG = "RemoteAccessibilityController";
+ private int mHostId;
+ private RemoteAccessibilityEmbeddedConnection mConnectionWrapper;
+ private Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
+ private final float[] mMatrixValues = new float[9];
+ private View mHostView;
+
+ RemoteAccessibilityController(View v) {
+ mHostView = v;
+ }
+
+ private void runOnUiThread(Runnable runnable) {
+ final Handler h = mHostView.getHandler();
+ if (h != null && h.getLooper() != Looper.myLooper()) {
+ h.post(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+
+ void assosciateHierarchy(IAccessibilityEmbeddedConnection connection,
+ IBinder leashToken, int hostId) {
+ mHostId = hostId;
+
+ try {
+ leashToken = connection.associateEmbeddedHierarchy(
+ leashToken, mHostId);
+ setRemoteAccessibilityEmbeddedConnection(connection, leashToken);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error in associateEmbeddedHierarchy " + e);
+ }
+ }
+
+ void disassosciateHierarchy() {
+ setRemoteAccessibilityEmbeddedConnection(null, null);
+ }
+
+ boolean alreadyAssociated(IAccessibilityEmbeddedConnection connection) {
+ if (mConnectionWrapper == null) {
+ return false;
+ }
+ return mConnectionWrapper.mConnection.equals(connection);
+ }
+
+ boolean connected() {
+ return mConnectionWrapper != null;
+ }
+
+ IBinder getLeashToken() {
+ return mConnectionWrapper.getLeashToken();
+ }
+
+ /**
+ * Wrapper of accessibility embedded connection for embedded view hierarchy.
+ */
+ private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient {
+ private final IAccessibilityEmbeddedConnection mConnection;
+ private final IBinder mLeashToken;
+
+ RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection,
+ IBinder leashToken) {
+ mConnection = connection;
+ mLeashToken = leashToken;
+ }
+
+ IAccessibilityEmbeddedConnection getConnection() {
+ return mConnection;
+ }
+
+ IBinder getLeashToken() {
+ return mLeashToken;
+ }
+
+ void linkToDeath() throws RemoteException {
+ mConnection.asBinder().linkToDeath(this, 0);
+ }
+
+ void unlinkToDeath() {
+ mConnection.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ unlinkToDeath();
+ runOnUiThread(() -> {
+ if (mConnectionWrapper == this) {
+ mConnectionWrapper = null;
+ }
+ });
+ }
+ }
+
+ private void setRemoteAccessibilityEmbeddedConnection(
+ IAccessibilityEmbeddedConnection connection, IBinder leashToken) {
+ try {
+ if (mConnectionWrapper != null) {
+ mConnectionWrapper.getConnection()
+ .disassociateEmbeddedHierarchy();
+ mConnectionWrapper.unlinkToDeath();
+ mConnectionWrapper = null;
+ }
+ if (connection != null && leashToken != null) {
+ mConnectionWrapper =
+ new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
+ mConnectionWrapper.linkToDeath();
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e);
+ }
+ }
+
+ private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() {
+ return mConnectionWrapper;
+ }
+
+ void setScreenMatrix(Matrix m) {
+ // If the screen matrix is identity or doesn't change, do nothing.
+ if (m.isIdentity() || m.equals(mScreenMatrixForEmbeddedHierarchy)) {
+ return;
+ }
+
+ try {
+ final RemoteAccessibilityEmbeddedConnection wrapper =
+ getRemoteAccessibilityEmbeddedConnection();
+ if (wrapper == null) {
+ return;
+ }
+ m.getValues(mMatrixValues);
+ wrapper.getConnection().setScreenMatrix(mMatrixValues);
+ mScreenMatrixForEmbeddedHierarchy.set(m);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error while setScreenMatrix " + e);
+ }
+ }
+
+
+
+
+
+
+}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 5b0d950..0847a17 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -739,7 +739,7 @@
* Set the scaling mode to be used for this surfaces buffers
* @hide
*/
- void setScalingMode(@ScalingMode int scalingMode) {
+ public void setScalingMode(@ScalingMode int scalingMode) {
synchronized (mLock) {
checkNotReleasedLocked();
int err = nativeSetScalingMode(mNativeObject, scalingMode);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ed9deec..bbdd7d3 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -187,8 +187,6 @@
private static native void nativeReparent(long transactionObj, long nativeObject,
long newParentNativeObject);
private static native void nativeSeverChildren(long transactionObj, long nativeObject);
- private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
- int scalingMode);
private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken);
@@ -1521,16 +1519,6 @@
/**
* @hide
*/
- public void setOverrideScalingMode(int scalingMode) {
- checkNotReleased();
- synchronized(SurfaceControl.class) {
- sGlobalTransaction.setOverrideScalingMode(this, scalingMode);
- }
- }
-
- /**
- * @hide
- */
@UnsupportedAppUsage
public void setLayer(int zorder) {
checkNotReleased();
@@ -1748,7 +1736,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DisplayInfo that = (DisplayInfo) o;
@@ -1926,7 +1914,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
return o instanceof DesiredDisplayConfigSpecs && equals((DesiredDisplayConfigSpecs) o);
}
@@ -2989,16 +2977,6 @@
}
/**
- * @hide
- */
- public Transaction setOverrideScalingMode(SurfaceControl sc, int overrideScalingMode) {
- checkPreconditions(sc);
- nativeSetOverrideScalingMode(mNativeObject, sc.mNativeObject,
- overrideScalingMode);
- return this;
- }
-
- /**
* Fills the surface with the specified color.
* @param color A float array with three values to represent r, g, b in range [0..1]. An
* invalid color will remove the color fill.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 78c71b8..7b6a4f8 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -41,7 +41,6 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.AttributeSet;
@@ -225,13 +224,12 @@
private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
- private int mParentSurfaceGenerationId;
+ private int mParentSurfaceSequenceId;
- private RemoteAccessibilityEmbeddedConnection mRemoteAccessibilityEmbeddedConnection;
+ private RemoteAccessibilityController mRemoteAccessibilityController =
+ new RemoteAccessibilityController(this);
- private final Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
private final Matrix mTmpMatrix = new Matrix();
- private final float[] mMatrixValues = new float[9];
SurfaceControlViewHost.SurfacePackage mSurfacePackage;
@@ -467,7 +465,7 @@
Transaction t = new SurfaceControl.Transaction();
t.setAlpha(mSurfaceControl, alpha);
t.deferTransactionUntil(mSurfaceControl,
- viewRoot.getRenderSurfaceControl(), frame);
+ viewRoot.getSurfaceControl(), frame);
t.apply();
}
}
@@ -827,7 +825,7 @@
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
updateRelativeZ(t);
t.deferTransactionUntil(mSurfaceControl,
- viewRoot.getRenderSurfaceControl(), frame);
+ viewRoot.getSurfaceControl(), frame);
t.apply();
}
}
@@ -927,6 +925,103 @@
}
}
+ private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
+ boolean creating, boolean sizeChanged, boolean needBLASTSync) {
+ boolean realSizeChanged = false;
+
+ mSurfaceLock.lock();
+ try {
+ mDrawingStopped = !mVisible;
+
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "Cur surface: " + mSurface);
+
+ // If we are creating the surface control or the parent surface has not
+ // changed, then set relative z. Otherwise allow the parent
+ // SurfaceChangedCallback to update the relative z. This is needed so that
+ // we do not change the relative z before the server is ready to swap the
+ // parent surface.
+ if (creating || (mParentSurfaceSequenceId == viewRoot.getSurfaceSequenceId())) {
+ updateRelativeZ(mTmpTransaction);
+ }
+ mParentSurfaceSequenceId = viewRoot.getSurfaceSequenceId();
+
+ if (mViewVisibility) {
+ mTmpTransaction.show(mSurfaceControl);
+ } else {
+ mTmpTransaction.hide(mSurfaceControl);
+ }
+
+ if (mSurfacePackage != null) {
+ reparentSurfacePackage(mTmpTransaction, mSurfacePackage);
+ }
+
+ updateBackgroundVisibility(mTmpTransaction);
+ updateBackgroundColor(mTmpTransaction);
+ if (mUseAlpha) {
+ float alpha = getFixedAlpha();
+ mTmpTransaction.setAlpha(mSurfaceControl, alpha);
+ mSurfaceAlpha = alpha;
+ }
+
+ // While creating the surface, we will set it's initial
+ // geometry. Outside of that though, we should generally
+ // leave it to the RenderThread.
+ //
+ // There is one more case when the buffer size changes we aren't yet
+ // prepared to sync (as even following the transaction applying
+ // we still need to latch a buffer).
+ // b/28866173
+ if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
+ onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl,
+ mScreenRect.left, /*positionLeft*/
+ mScreenRect.top /*positionTop*/ ,
+ mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
+ mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
+
+ // Set a window crop when creating the surface or changing its size to
+ // crop the buffer to the surface size since the buffer producer may
+ // use SCALING_MODE_SCALE and submit a larger size than the surface
+ // size.
+ if (mClipSurfaceToBounds && mClipBounds != null) {
+ mTmpTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
+ } else {
+ mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
+ mSurfaceHeight);
+ }
+ } else if (needBLASTSync) {
+ viewRoot.setUseBLASTSyncTransaction();
+ }
+ mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
+ if (sizeChanged && !creating) {
+ setBufferSize(mTmpTransaction);
+ }
+
+ mTmpTransaction.apply();
+ updateEmbeddedAccessibilityMatrix();
+
+ mSurfaceFrame.left = 0;
+ mSurfaceFrame.top = 0;
+ if (translator == null) {
+ mSurfaceFrame.right = mSurfaceWidth;
+ mSurfaceFrame.bottom = mSurfaceHeight;
+ } else {
+ float appInvertedScale = translator.applicationInvertedScale;
+ mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
+ mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
+ }
+ final int surfaceWidth = mSurfaceFrame.right;
+ final int surfaceHeight = mSurfaceFrame.bottom;
+ realSizeChanged = mLastSurfaceWidth != surfaceWidth
+ || mLastSurfaceHeight != surfaceHeight;
+ mLastSurfaceWidth = surfaceWidth;
+ mLastSurfaceHeight = surfaceHeight;
+ } finally {
+ mSurfaceLock.unlock();
+ }
+ return realSizeChanged;
+ }
+
/** @hide */
protected void updateSurface() {
if (!mHaveFrame) {
@@ -965,7 +1060,6 @@
&& mRequestedVisible;
final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
- boolean redrawNeeded = false;
getLocationInSurface(mLocation);
final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
|| mWindowSpaceTop != mLocation[1];
@@ -988,7 +1082,7 @@
+ " top=" + (mWindowSpaceTop != mLocation[1]));
try {
- final boolean visible = mVisible = mRequestedVisible;
+ mVisible = mRequestedVisible;
mWindowSpaceLeft = mLocation[0];
mWindowSpaceTop = mLocation[1];
mSurfaceWidth = myWidth;
@@ -1014,119 +1108,26 @@
return;
}
- boolean realSizeChanged = false;
-
- mSurfaceLock.lock();
- try {
- mDrawingStopped = !visible;
-
- if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
- + "Cur surface: " + mSurface);
-
- // If we are creating the surface control or the parent surface has not
- // changed, then set relative z. Otherwise allow the parent
- // SurfaceChangedCallback to update the relative z. This is needed so that
- // we do not change the relative z before the server is ready to swap the
- // parent surface.
- if (creating || (mParentSurfaceGenerationId
- == viewRoot.mSurface.getGenerationId())) {
- updateRelativeZ(mTmpTransaction);
- }
- mParentSurfaceGenerationId = viewRoot.mSurface.getGenerationId();
-
- if (mViewVisibility) {
- mTmpTransaction.show(mSurfaceControl);
- } else {
- mTmpTransaction.hide(mSurfaceControl);
- }
-
- if (mSurfacePackage != null) {
- reparentSurfacePackage(mTmpTransaction, mSurfacePackage);
- }
-
- updateBackgroundVisibility(mTmpTransaction);
- updateBackgroundColor(mTmpTransaction);
- if (mUseAlpha) {
- mTmpTransaction.setAlpha(mSurfaceControl, alpha);
- mSurfaceAlpha = alpha;
- }
-
- // While creating the surface, we will set it's initial
- // geometry. Outside of that though, we should generally
- // leave it to the RenderThread.
- //
- // There is one more case when the buffer size changes we aren't yet
- // prepared to sync (as even following the transaction applying
- // we still need to latch a buffer).
- // b/28866173
- if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
- onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl,
- mScreenRect.left, /*positionLeft*/
- mScreenRect.top /*positionTop*/ ,
- mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
- mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/);
-
- // Set a window crop when creating the surface or changing its size to
- // crop the buffer to the surface size since the buffer producer may
- // use SCALING_MODE_SCALE and submit a larger size than the surface
- // size.
- if (mClipSurfaceToBounds && mClipBounds != null) {
- mTmpTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
- } else {
- mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
- mSurfaceHeight);
- }
- } else if ((layoutSizeChanged || positionChanged || visibleChanged) &&
- viewRoot.useBLAST()) {
- viewRoot.setUseBLASTSyncTransaction();
- }
- mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
- if (sizeChanged && !creating) {
- setBufferSize(mTmpTransaction);
- }
-
- mTmpTransaction.apply();
- updateScreenMatrixForEmbeddedHierarchy();
-
- if (sizeChanged || creating) {
- redrawNeeded = true;
- }
-
- mSurfaceFrame.left = 0;
- mSurfaceFrame.top = 0;
- if (translator == null) {
- mSurfaceFrame.right = mSurfaceWidth;
- mSurfaceFrame.bottom = mSurfaceHeight;
- } else {
- float appInvertedScale = translator.applicationInvertedScale;
- mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
- mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
- }
-
- final int surfaceWidth = mSurfaceFrame.right;
- final int surfaceHeight = mSurfaceFrame.bottom;
- realSizeChanged = mLastSurfaceWidth != surfaceWidth
- || mLastSurfaceHeight != surfaceHeight;
- mLastSurfaceWidth = surfaceWidth;
- mLastSurfaceHeight = surfaceHeight;
- } finally {
- mSurfaceLock.unlock();
- }
+ final boolean needBLASTSync =
+ (layoutSizeChanged || positionChanged || visibleChanged) &&
+ viewRoot.useBLAST();
+ final boolean realSizeChanged = performSurfaceTransaction(viewRoot,
+ translator, creating, sizeChanged, needBLASTSync);
+ final boolean redrawNeeded = sizeChanged || creating ||
+ (mVisible && !mDrawFinished);
try {
- redrawNeeded |= visible && !mDrawFinished;
-
SurfaceHolder.Callback[] callbacks = null;
final boolean surfaceChanged = creating;
- if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
+ if (mSurfaceCreated && (surfaceChanged || (!mVisible && visibleChanged))) {
mSurfaceCreated = false;
notifySurfaceDestroyed();
}
copySurface(creating /* surfaceControlCreated */, sizeChanged);
- if (visible && mSurface.isValid()) {
+ if (mVisible && mSurface.isValid()) {
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
mSurfaceCreated = true;
mIsCreating = true;
@@ -1352,7 +1353,7 @@
Rect position, long frameNumber) {
final ViewRootImpl viewRoot = getViewRootImpl();
if (frameNumber > 0 && viewRoot != null && !viewRoot.useBLAST()) {
- t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(),
+ t.deferTransactionUntil(surface, viewRoot.getSurfaceControl(),
frameNumber);
}
@@ -1470,7 +1471,7 @@
} else {
if (frameNumber > 0 && viewRoot != null && viewRoot.mSurface.isValid()) {
mRtTransaction.deferTransactionUntil(mSurfaceControl,
- viewRoot.getRenderSurfaceControl(), frameNumber);
+ viewRoot.getSurfaceControl(), frameNumber);
}
mRtTransaction.hide(mSurfaceControl);
if (mRtReleaseSurfaces) {
@@ -1754,7 +1755,7 @@
@Override
public void surfaceDestroyed() {
setWindowStopped(true);
- setRemoteAccessibilityEmbeddedConnection(null, null);
+ mRemoteAccessibilityController.disassosciateHierarchy();
}
/**
@@ -1834,14 +1835,12 @@
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
- final RemoteAccessibilityEmbeddedConnection wrapper =
- getRemoteAccessibilityEmbeddedConnection();
- if (wrapper == null) {
+ if (!mRemoteAccessibilityController.connected()) {
return;
}
// Add a leashed child when this SurfaceView embeds another view hierarchy. Getting this
// leashed child would return the root node in the embedded hierarchy
- info.addChild(wrapper.getLeashToken());
+ info.addChild(mRemoteAccessibilityController.getLeashToken());
}
@Override
@@ -1850,7 +1849,7 @@
// If developers explicitly set the important mode for it, don't change the mode.
// Only change the mode to important when this SurfaceView isn't explicitly set and has
// an embedded hierarchy.
- if (mRemoteAccessibilityEmbeddedConnection == null
+ if (!mRemoteAccessibilityController.connected()
|| mode != IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
return mode;
}
@@ -1859,74 +1858,13 @@
private void initEmbeddedHierarchyForAccessibility(SurfaceControlViewHost.SurfacePackage p) {
final IAccessibilityEmbeddedConnection connection = p.getAccessibilityEmbeddedConnection();
- final RemoteAccessibilityEmbeddedConnection wrapper =
- getRemoteAccessibilityEmbeddedConnection();
-
- // Do nothing if package is embedding the same view hierarchy.
- if (wrapper != null && wrapper.getConnection().equals(connection)) {
+ if (mRemoteAccessibilityController.alreadyAssociated(connection)) {
return;
}
+ mRemoteAccessibilityController.assosciateHierarchy(connection,
+ getViewRootImpl().mLeashToken, getAccessibilityViewId());
- // If this SurfaceView embeds a different view hierarchy, unlink the previous one first.
- setRemoteAccessibilityEmbeddedConnection(null, null);
-
- try {
- final IBinder leashToken = connection.associateEmbeddedHierarchy(
- getViewRootImpl().mLeashToken, getAccessibilityViewId());
- setRemoteAccessibilityEmbeddedConnection(connection, leashToken);
- } catch (RemoteException e) {
- Log.d(TAG, "Error while associateEmbeddedHierarchy " + e);
- }
- updateScreenMatrixForEmbeddedHierarchy();
- }
-
- private void setRemoteAccessibilityEmbeddedConnection(
- IAccessibilityEmbeddedConnection connection, IBinder leashToken) {
- try {
- if (mRemoteAccessibilityEmbeddedConnection != null) {
- mRemoteAccessibilityEmbeddedConnection.getConnection()
- .disassociateEmbeddedHierarchy();
- mRemoteAccessibilityEmbeddedConnection.unlinkToDeath();
- mRemoteAccessibilityEmbeddedConnection = null;
- }
- if (connection != null && leashToken != null) {
- mRemoteAccessibilityEmbeddedConnection =
- new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
- mRemoteAccessibilityEmbeddedConnection.linkToDeath();
- }
- } catch (RemoteException e) {
- Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e);
- }
- }
-
- private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() {
- return mRemoteAccessibilityEmbeddedConnection;
- }
-
- private void updateScreenMatrixForEmbeddedHierarchy() {
- getBoundsOnScreen(mTmpRect);
- mTmpMatrix.reset();
- mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top);
- mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
- mScreenRect.height() / (float) mSurfaceHeight);
-
- // If the screen matrix is identity or doesn't change, do nothing.
- if (mTmpMatrix.isIdentity() || mTmpMatrix.equals(mScreenMatrixForEmbeddedHierarchy)) {
- return;
- }
-
- try {
- final RemoteAccessibilityEmbeddedConnection wrapper =
- getRemoteAccessibilityEmbeddedConnection();
- if (wrapper == null) {
- return;
- }
- mTmpMatrix.getValues(mMatrixValues);
- wrapper.getConnection().setScreenMatrix(mMatrixValues);
- mScreenMatrixForEmbeddedHierarchy.set(mTmpMatrix);
- } catch (RemoteException e) {
- Log.d(TAG, "Error while setScreenMatrix " + e);
- }
+ updateEmbeddedAccessibilityMatrix();
}
private void notifySurfaceDestroyed() {
@@ -1954,6 +1892,18 @@
}
}
+ void updateEmbeddedAccessibilityMatrix() {
+ if (!mRemoteAccessibilityController.connected()) {
+ return;
+ }
+ getBoundsOnScreen(mTmpRect);
+ mTmpMatrix.reset();
+ mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top);
+ mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
+ mScreenRect.height() / (float) mSurfaceHeight);
+ mRemoteAccessibilityController.setScreenMatrix(mTmpMatrix);
+ }
+
@Override
protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction,
@Nullable Rect previouslyFocusedRect) {
@@ -1970,44 +1920,4 @@
+ "Exception requesting focus on embedded window", e);
}
}
-
- /**
- * Wrapper of accessibility embedded connection for embedded view hierarchy.
- */
- private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient {
- private final IAccessibilityEmbeddedConnection mConnection;
- private final IBinder mLeashToken;
-
- RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection,
- IBinder leashToken) {
- mConnection = connection;
- mLeashToken = leashToken;
- }
-
- IAccessibilityEmbeddedConnection getConnection() {
- return mConnection;
- }
-
- IBinder getLeashToken() {
- return mLeashToken;
- }
-
- void linkToDeath() throws RemoteException {
- mConnection.asBinder().linkToDeath(this, 0);
- }
-
- void unlinkToDeath() {
- mConnection.asBinder().unlinkToDeath(this, 0);
- }
-
- @Override
- public void binderDied() {
- unlinkToDeath();
- runOnUiThread(() -> {
- if (mRemoteAccessibilityEmbeddedConnection == this) {
- mRemoteAccessibilityEmbeddedConnection = null;
- }
- });
- }
- }
}
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index 062285f..bce78b5 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -60,7 +60,7 @@
if (mTargetViewRootImpl == null) {
return;
}
- mTargetSc = mTargetViewRootImpl.getRenderSurfaceControl();
+ mTargetSc = mTargetViewRootImpl.getSurfaceControl();
mTargetViewRootImpl.registerRtFrameCallback(frame -> {
if (mTargetSc == null || !mTargetSc.isValid()) {
return;
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 14a324d..57ca71a 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.HardwareRenderer;
@@ -26,7 +27,6 @@
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
-import android.os.SystemProperties;
import android.os.Trace;
import android.util.Log;
import android.view.Surface.OutOfResourcesException;
@@ -186,37 +186,12 @@
public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102;
public static int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
- static {
- // Try to check OpenGL support early if possible.
- isAvailable();
- }
-
- /**
- * A process can set this flag to false to prevent the use of threaded
- * rendering.
- *
- * @hide
- */
- public static boolean sRendererDisabled = false;
-
/**
* Further threaded renderer disabling for the system process.
*
* @hide
*/
- public static boolean sSystemRendererDisabled = false;
-
- /**
- * Invoke this method to disable threaded rendering in the current process.
- *
- * @hide
- */
- public static void disable(boolean system) {
- sRendererDisabled = true;
- if (system) {
- sSystemRendererDisabled = true;
- }
- }
+ public static boolean sRendererEnabled = true;
public static boolean sTrimForeground = false;
@@ -230,16 +205,19 @@
sTrimForeground = true;
}
-
/**
- * Indicates whether threaded rendering is available under any form for
- * the view hierarchy.
- *
- * @return True if the view hierarchy can potentially be defer rendered,
- * false otherwise
+ * Initialize HWUI for being in a system process like system_server
+ * Should not be called in non-system processes
*/
- public static boolean isAvailable() {
- return true;
+ public static void initForSystemProcess() {
+ // The system process on low-memory devices do not get to use hardware
+ // accelerated drawing, since this can add too much overhead to the
+ // process.
+ if (!ActivityManager.isHighEndGfx()) {
+ sRendererEnabled = false;
+ } else {
+ enableForegroundTrimming();
+ }
}
/**
@@ -250,11 +228,7 @@
* @return A threaded renderer backed by OpenGL.
*/
public static ThreadedRenderer create(Context context, boolean translucent, String name) {
- ThreadedRenderer renderer = null;
- if (isAvailable()) {
- renderer = new ThreadedRenderer(context, translucent, name);
- }
- return renderer;
+ return new ThreadedRenderer(context, translucent, name);
}
private static final String[] VISUALIZERS = {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c430a4d..cf5ca56 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -15165,6 +15165,42 @@
}
/**
+ * Called by the {@link android.view.inputmethod.InputMethodManager} to notify the application
+ * that the system has successfully initialized an {@link InputConnection} and it is ready for
+ * use.
+ *
+ * <p>The default implementation does nothing, since a view doesn't support input methods by
+ * default (see {@link #onCreateInputConnection}).
+ *
+ * @param inputConnection The {@link InputConnection} from {@link #onCreateInputConnection},
+ * after it's been fully initialized by the system.
+ * @param editorInfo The {@link EditorInfo} that was used to create the {@link InputConnection}.
+ * @param handler The dedicated {@link Handler} on which IPC method calls from input methods
+ * will be dispatched. This is the handler returned by {@link InputConnection#getHandler()}. If
+ * that method returns null, this parameter will be null also.
+ *
+ * @hide
+ */
+ public void onInputConnectionOpenedInternal(@NonNull InputConnection inputConnection,
+ @NonNull EditorInfo editorInfo, @Nullable Handler handler) {}
+
+ /**
+ * Called by the {@link android.view.inputmethod.InputMethodManager} to notify the application
+ * that the {@link InputConnection} has been closed.
+ *
+ * <p>The default implementation does nothing, since a view doesn't support input methods by
+ * default (see {@link #onCreateInputConnection}).
+ *
+ * <p><strong>Note:</strong> This callback is not invoked if the view is already detached when
+ * the {@link InputConnection} is closed or the connection is not valid and managed by
+ * {@link com.android.server.inputmethod.InputMethodManagerService}.
+ * TODO(b/170645312): Before un-hiding this API, handle the detached view scenario.
+ *
+ * @hide
+ */
+ public void onInputConnectionClosedInternal() {}
+
+ /**
* Called by the {@link android.view.inputmethod.InputMethodManager}
* when a view who is not the current
* input connection target is trying to make a call on the manager. The
@@ -23537,8 +23573,7 @@
if ((privateFlags & PFLAG_SELECTED) != 0) viewStateIndex |= StateSet.VIEW_STATE_SELECTED;
if (hasWindowFocus()) viewStateIndex |= StateSet.VIEW_STATE_WINDOW_FOCUSED;
if ((privateFlags & PFLAG_ACTIVATED) != 0) viewStateIndex |= StateSet.VIEW_STATE_ACTIVATED;
- if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested &&
- ThreadedRenderer.isAvailable()) {
+ if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested) {
// This is set if HW acceleration is requested, even if the current
// process doesn't allow it. This is just to allow app preview
// windows to better match their app.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e00ff7e..77626c2 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -105,6 +105,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.FrameInfo;
+import android.graphics.HardwareRenderer;
import android.graphics.HardwareRenderer.FrameDrawingCallback;
import android.graphics.Insets;
import android.graphics.Matrix;
@@ -519,7 +520,6 @@
@UnsupportedAppUsage
public final Surface mSurface = new Surface();
private final SurfaceControl mSurfaceControl = new SurfaceControl();
- private SurfaceControl mBlastSurfaceControl = new SurfaceControl();
private BLASTBufferQueue mBlastBufferQueue;
@@ -702,6 +702,11 @@
private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks;
+ /**
+ * Increment this value when the surface has been replaced.
+ */
+ private int mSurfaceSequenceId = 0;
+
private String mTag = TAG;
public ViewRootImpl(Context context, Display display) {
@@ -1286,10 +1291,6 @@
(attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
if (hardwareAccelerated) {
- if (!ThreadedRenderer.isAvailable()) {
- return;
- }
-
// Persistent processes (including the system) should not do
// accelerated rendering on low-end devices. In that case,
// sRendererDisabled will be set. In addition, the system process
@@ -1309,8 +1310,7 @@
// shows for launching applications, so they will look more like
// the app being launched.
mAttachInfo.mHardwareAccelerationRequested = true;
- } else if (!ThreadedRenderer.sRendererDisabled
- || (ThreadedRenderer.sSystemRendererDisabled && forceHwAccelerated)) {
+ } else if (ThreadedRenderer.sRendererEnabled || forceHwAccelerated) {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.destroy();
}
@@ -1808,7 +1808,7 @@
mBoundsLayer = new SurfaceControl.Builder(mSurfaceSession)
.setContainerLayer()
.setName("Bounds for - " + getTitle().toString())
- .setParent(getRenderSurfaceControl())
+ .setParent(getSurfaceControl())
.setCallsite("ViewRootImpl.getBoundsLayer")
.build();
setBoundsLayerCrop(mTransaction);
@@ -1818,22 +1818,19 @@
}
Surface getOrCreateBLASTSurface(int width, int height) {
- if (mSurfaceControl == null
- || !mSurfaceControl.isValid()
- || mBlastSurfaceControl == null
- || !mBlastSurfaceControl.isValid()) {
+ if (!mSurfaceControl.isValid()) {
return null;
}
Surface ret = null;
if (mBlastBufferQueue == null) {
mBlastBufferQueue = new BLASTBufferQueue(mTag,
- mBlastSurfaceControl, width, height, mEnableTripleBuffering);
+ mSurfaceControl, width, height, mEnableTripleBuffering);
// 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(mBlastSurfaceControl, width, height);
+ mBlastBufferQueue.update(mSurfaceControl, width, height);
}
return ret;
@@ -1855,7 +1852,7 @@
private boolean updateBoundsLayer(SurfaceControl.Transaction t) {
if (mBoundsLayer != null) {
setBoundsLayerCrop(t);
- t.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(),
+ t.deferTransactionUntil(mBoundsLayer, getSurfaceControl(),
mSurface.getNextFrameNumber());
return true;
}
@@ -1864,7 +1861,7 @@
private void prepareSurfaces(boolean sizeChanged) {
final SurfaceControl.Transaction t = mTransaction;
- final SurfaceControl sc = getRenderSurfaceControl();
+ final SurfaceControl sc = getSurfaceControl();
if (!sc.isValid()) return;
boolean applyTransaction = updateBoundsLayer(t);
@@ -1885,7 +1882,6 @@
mSurface.release();
mSurfaceControl.release();
- mBlastSurfaceControl.release();
// We should probably add an explicit dispose.
mBlastBufferQueue = null;
}
@@ -2613,7 +2609,7 @@
boolean surfaceSizeChanged = false;
boolean surfaceCreated = false;
boolean surfaceDestroyed = false;
- /* True if surface generation id changes. */
+ // True if surface generation id changes or relayout result is RELAYOUT_RES_SURFACE_CHANGED.
boolean surfaceReplaced = false;
final boolean windowAttributesChanged = mWindowAttributesChanged;
@@ -2708,6 +2704,7 @@
updateColorModeIfNeeded(lp.getColorMode());
surfaceCreated = !hadSurface && mSurface.isValid();
surfaceDestroyed = hadSurface && !mSurface.isValid();
+
// When using Blast, the surface generation id may not change when there's a new
// SurfaceControl. In that case, we also check relayout flag
// RELAYOUT_RES_SURFACE_CHANGED since it should indicate that WMS created a new
@@ -2716,6 +2713,9 @@
|| (relayoutResult & RELAYOUT_RES_SURFACE_CHANGED)
== RELAYOUT_RES_SURFACE_CHANGED)
&& mSurface.isValid();
+ if (surfaceReplaced) {
+ mSurfaceSequenceId++;
+ }
if (cutoutChanged) {
mAttachInfo.mDisplayCutout.set(mPendingDisplayCutout);
@@ -3815,6 +3815,89 @@
}
}
+ /**
+ * The callback will run on the render thread.
+ */
+ private HardwareRenderer.FrameCompleteCallback createFrameCompleteCallback(Handler handler,
+ boolean reportNextDraw, ArrayList<Runnable> commitCallbacks) {
+ return frameNr -> {
+ // Use a new transaction here since mRtBLASTSyncTransaction can only be accessed by
+ // the render thread and mSurfaceChangedTransaction can only be accessed by the UI
+ // thread. The temporary transaction is used so mRtBLASTSyncTransaction can be merged
+ // with mSurfaceChangedTransaction without synchronization issues.
+ final Transaction t = new Transaction();
+ finishBLASTSyncOnRT(!mSendNextFrameToWm, t);
+ handler.postAtFrontOfQueue(() -> {
+ mSurfaceChangedTransaction.merge(t);
+ if (reportNextDraw) {
+ // TODO: Use the frame number
+ pendingDrawFinished();
+ }
+ if (commitCallbacks != null) {
+ for (int i = 0; i < commitCallbacks.size(); i++) {
+ commitCallbacks.get(i).run();
+ }
+ }
+ });
+ };
+ }
+
+ private boolean addFrameCompleteCallbackIfNeeded() {
+ if (mAttachInfo.mThreadedRenderer == null || !mAttachInfo.mThreadedRenderer.isEnabled()) {
+ return false;
+ }
+
+ ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
+ .captureFrameCommitCallbacks();
+ final boolean needFrameCompleteCallback =
+ mNextDrawUseBLASTSyncTransaction || mReportNextDraw
+ || (commitCallbacks != null && commitCallbacks.size() > 0);
+ if (needFrameCompleteCallback) {
+ mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(
+ createFrameCompleteCallback(mAttachInfo.mHandler, mReportNextDraw,
+ commitCallbacks));
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * The callback will run on a worker thread pool from the render thread.
+ */
+ private HardwareRenderer.FrameDrawingCallback createFrameDrawingCallback() {
+ return frame -> {
+ mRtNextFrameReportedConsumeWithBlast = true;
+ if (mBlastBufferQueue != null) {
+ // We don't need to synchronize mRtBLASTSyncTransaction here since it's not
+ // being modified and only sent to BlastBufferQueue.
+ mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
+ }
+ };
+ }
+
+ private void addFrameCallbackIfNeeded() {
+ // Frame callbacks will always occur after submitting draw requests and before
+ // the draw actually occurs. This will ensure that we set the next transaction
+ // for the frame that's about to get drawn and not on a previous frame that.
+ //
+ // This is thread safe since mRtNextFrameReportConsumeWithBlast will only be
+ // modified in onFrameDraw and then again in onFrameComplete. This is to ensure the
+ // next frame completed should be reported with the blast sync transaction.
+ if (mNextDrawUseBLASTSyncTransaction) {
+ registerRtFrameCallback(createFrameDrawingCallback());
+ mNextDrawUseBLASTSyncTransaction = false;
+ } else if (mReportNextDraw) {
+ registerRtFrameCallback(frame -> {
+ if (mBlastBufferQueue != null) {
+ // If we need to report next draw, wait for adapter to flush its shadow queue
+ // by processing previously queued buffers so that we can submit the
+ // transaction a timely manner.
+ mBlastBufferQueue.flushShadowQueue();
+ }
+ });
+ }
+ }
+
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
@@ -3828,58 +3911,14 @@
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
- boolean usingAsyncReport = false;
- boolean reportNextDraw = mReportNextDraw; // Capture the original value
- if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
- ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
- .captureFrameCommitCallbacks();
- final boolean needFrameCompleteCallback = mNextDrawUseBLASTSyncTransaction ||
- (commitCallbacks != null && commitCallbacks.size() > 0) ||
- mReportNextDraw;
- usingAsyncReport = mReportNextDraw;
- if (needFrameCompleteCallback) {
- final Handler handler = mAttachInfo.mHandler;
- mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
- finishBLASTSync(!mSendNextFrameToWm);
- handler.postAtFrontOfQueue(() -> {
- if (reportNextDraw) {
- // TODO: Use the frame number
- pendingDrawFinished();
- }
- if (commitCallbacks != null) {
- for (int i = 0; i < commitCallbacks.size(); i++) {
- commitCallbacks.get(i).run();
- }
- }
- });
- });
- }
- }
+ boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded();
+ addFrameCallbackIfNeeded();
try {
- if (mNextDrawUseBLASTSyncTransaction) {
- // Frame callbacks will always occur after submitting draw requests and before
- // the draw actually occurs. This will ensure that we set the next transaction
- // for the frame that's about to get drawn and not on a previous frame that.
- //
- // This is thread safe since mRtNextFrameReportConsumeWithBlast will only be
- // modified in onFrameDraw and then again in onFrameComplete. This is to ensure the
- // next frame completed should be reported with the blast sync transaction.
- registerRtFrameCallback(frame -> {
- mRtNextFrameReportedConsumeWithBlast = true;
- if (mBlastBufferQueue != null) {
- // We don't need to synchronize mRtBLASTSyncTransaction here since it's not
- // being modified and only sent to BlastBufferQueue.
- mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
- }
- });
- mNextDrawUseBLASTSyncTransaction = false;
- }
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
usingAsyncReport = false;
- finishBLASTSync(true /* apply */);
}
} finally {
mIsDrawing = false;
@@ -7447,7 +7486,7 @@
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
- mTempControls, mSurfaceSize, mBlastSurfaceControl);
+ mTempControls, mSurfaceSize);
mPendingDisplayCutout.set(mTmpFrames.displayCutout);
mPendingBackDropFrame.set(mTmpFrames.backdropFrame);
if (mSurfaceControl.isValid()) {
@@ -9844,7 +9883,12 @@
mNextDrawUseBLASTSyncTransaction = true;
}
- private void finishBLASTSync(boolean apply) {
+ /**
+ * This should only be called from the render thread.
+ */
+ private void finishBLASTSyncOnRT(boolean apply, Transaction t) {
+ // This is safe to modify on the render thread since the only other place it's modified
+ // is on the UI thread when the render thread is paused.
mSendNextFrameToWm = false;
if (mRtNextFrameReportedConsumeWithBlast) {
mRtNextFrameReportedConsumeWithBlast = false;
@@ -9855,7 +9899,7 @@
if (apply) {
mRtBLASTSyncTransaction.apply();
} else {
- mSurfaceChangedTransaction.merge(mRtBLASTSyncTransaction);
+ t.merge(mRtBLASTSyncTransaction);
}
}
}
@@ -9868,17 +9912,6 @@
return mRtBLASTSyncTransaction;
}
- /**
- * @hide
- */
- public SurfaceControl getRenderSurfaceControl() {
- if (useBLAST()) {
- return mBlastSurfaceControl;
- } else {
- return mSurfaceControl;
- }
- }
-
@Override
public void onDescendantUnbufferedRequested() {
mUnbufferedInputSource = mView.mUnbufferedInputSource;
@@ -9895,4 +9928,8 @@
boolean useBLAST() {
return mUseBLASTAdapter && !mForceDisableBLAST;
}
+
+ int getSurfaceSequenceId() {
+ return mSurfaceSequenceId;
+ }
}
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index d7b0afc..d9b55e4 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -307,7 +307,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 5e94758..94c5184 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -942,7 +942,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || !(o instanceof WindowInsets)) return false;
WindowInsets that = (WindowInsets) o;
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 8490f2a..f01cbcc 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -527,7 +527,7 @@
}
allViewsRemoved = mRoots.isEmpty();
}
- if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
+ if (ThreadedRenderer.sTrimForeground) {
doTrimForeground();
}
@@ -561,29 +561,28 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public void trimMemory(int level) {
- if (ThreadedRenderer.isAvailable()) {
- if (shouldDestroyEglContext(level)) {
- // Destroy all hardware surfaces and resources associated to
- // known windows
- synchronized (mLock) {
- for (int i = mRoots.size() - 1; i >= 0; --i) {
- mRoots.get(i).destroyHardwareResources();
- }
+
+ if (shouldDestroyEglContext(level)) {
+ // Destroy all hardware surfaces and resources associated to
+ // known windows
+ synchronized (mLock) {
+ for (int i = mRoots.size() - 1; i >= 0; --i) {
+ mRoots.get(i).destroyHardwareResources();
}
- // Force a full memory flush
- level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
}
+ // Force a full memory flush
+ level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
+ }
- ThreadedRenderer.trimMemory(level);
+ ThreadedRenderer.trimMemory(level);
- if (ThreadedRenderer.sTrimForeground) {
- doTrimForeground();
- }
+ if (ThreadedRenderer.sTrimForeground) {
+ doTrimForeground();
}
}
public static void trimForeground() {
- if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
+ if (ThreadedRenderer.sTrimForeground) {
WindowManagerGlobal wm = WindowManagerGlobal.getInstance();
wm.doTrimForeground();
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index dbd8184..0c221ed 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -227,8 +227,7 @@
int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
- SurfaceControl outBLASTSurfaceControl) {
+ InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
final State state;
synchronized (this) {
state = mStateForWindow.get(window.asBinder());
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index d80d230..f6d6fde 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -199,7 +199,7 @@
* <b>Window state changed</b> - represents the event of a change to a section of
* the user interface that is visually distinct. Should be sent from either the
* root view of a window or from a view that is marked as a pane
- * {@link android.view.View#setAccessibilityPaneTitle(CharSequence)}. Not that changes
+ * {@link android.view.View#setAccessibilityPaneTitle(CharSequence)}. Note that changes
* to true windows are represented by {@link #TYPE_WINDOWS_CHANGED}.</br>
* <em>Type:</em> {@link #TYPE_WINDOW_STATE_CHANGED}</br>
* <em>Properties:</em></br>
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 2d0f05e..303ba9e 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -4369,7 +4369,7 @@
}
@Override
- public boolean equals(Object object) {
+ public boolean equals(@Nullable Object object) {
if (this == object) {
return true;
}
@@ -5039,7 +5039,7 @@
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
if (other == null) {
return false;
}
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 813234f..ccc7a36 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -655,7 +655,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 32b9cf7..82d52b6 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -185,7 +185,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/view/autofill/AutofillValue.java b/core/java/android/view/autofill/AutofillValue.java
index 2967041..be1a2f2 100644
--- a/core/java/android/view/autofill/AutofillValue.java
+++ b/core/java/android/view/autofill/AutofillValue.java
@@ -184,7 +184,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java
index 54ebf55..1adef94 100644
--- a/core/java/android/view/contentcapture/ContentCaptureCondition.java
+++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java
@@ -17,6 +17,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.LocusId;
import android.os.Parcel;
import android.os.Parcelable;
@@ -91,7 +92,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/view/contentcapture/ContentCaptureSessionId.java b/core/java/android/view/contentcapture/ContentCaptureSessionId.java
index 2d350b2..413a2f3 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSessionId.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSessionId.java
@@ -17,6 +17,7 @@
package android.view.contentcapture;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -56,7 +57,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
index 21ead4c..c8c1d87 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -183,7 +183,7 @@
}
@Override
- public boolean equals(Object obj){
+ public boolean equals(@Nullable Object obj){
if (obj == null) {
return false;
}
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 7cc347d..5d876a6 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -17,6 +17,7 @@
package android.view.inputmethod;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -515,7 +516,7 @@
* {@link InputMethodInfo} and its Id is the same to this one.
*/
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o == this) return true;
if (o == null) return false;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index b8f04159..5785999 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1006,6 +1006,24 @@
return;
}
closeConnection();
+
+ // Notify the app that the InputConnection was closed.
+ final View servedView = mServedView.get();
+ if (servedView != null) {
+ final Handler handler = servedView.getHandler();
+ // The handler is null if the view is already detached. When that's the case, for
+ // now, we simply don't dispatch this callback.
+ if (handler != null) {
+ if (DEBUG) {
+ Log.v(TAG, "Calling View.onInputConnectionClosed: view=" + servedView);
+ }
+ if (handler.getLooper().isCurrentThread()) {
+ servedView.onInputConnectionClosedInternal();
+ } else {
+ handler.post(servedView::onInputConnectionClosedInternal);
+ }
+ }
+ }
}
@Override
@@ -1940,6 +1958,8 @@
InputConnection ic = view.onCreateInputConnection(tba);
if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
+ final Handler icHandler;
+ InputBindResult res = null;
synchronized (mH) {
// Now that we are locked again, validate that our state hasn't
// changed.
@@ -1976,7 +1996,6 @@
mCursorCandEnd = -1;
mCursorRect.setEmpty();
mCursorAnchorInfo = null;
- final Handler icHandler;
missingMethodFlags = InputConnectionInspector.getMissingMethodFlags(ic);
if ((missingMethodFlags & InputConnectionInspector.MissingMethodFlags.GET_HANDLER)
!= 0) {
@@ -1990,6 +2009,7 @@
} else {
servedContext = null;
missingMethodFlags = 0;
+ icHandler = null;
}
mServedInputConnectionWrapper = servedContext;
@@ -1997,7 +2017,7 @@
if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
+ ic + " tba=" + tba + " startInputFlags="
+ InputMethodDebug.startInputFlagsToString(startInputFlags));
- final InputBindResult res = mService.startInputOrWindowGainedFocus(
+ res = mService.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, startInputFlags,
softInputMode, windowFlags, tba, servedContext, missingMethodFlags,
view.getContext().getApplicationInfo().targetSdkVersion);
@@ -2036,6 +2056,15 @@
}
}
+ // Notify the app that the InputConnection is initialized and ready for use.
+ if (ic != null && res != null && res.method != null) {
+ if (DEBUG) {
+ Log.v(TAG, "Calling View.onInputConnectionOpened: view= " + view
+ + ", ic=" + ic + ", tba=" + tba + ", handler=" + icHandler);
+ }
+ view.onInputConnectionOpenedInternal(ic, tba, icHandler);
+ }
+
return true;
}
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index 5989847..14abbdb 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -597,7 +597,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof InputMethodSubtype) {
InputMethodSubtype subtype = (InputMethodSubtype) o;
if (subtype.mSubtypeId != 0 || mSubtypeId != 0) {
diff --git a/core/java/android/view/inputmethod/SparseRectFArray.java b/core/java/android/view/inputmethod/SparseRectFArray.java
index 596ea86..07d0b06 100644
--- a/core/java/android/view/inputmethod/SparseRectFArray.java
+++ b/core/java/android/view/inputmethod/SparseRectFArray.java
@@ -16,6 +16,7 @@
package android.view.inputmethod;
+import android.annotation.Nullable;
import android.graphics.RectF;
import android.os.Parcel;
import android.os.Parcelable;
@@ -92,7 +93,7 @@
}
@Override
- public boolean equals(Object obj){
+ public boolean equals(@Nullable Object obj){
if (obj == null) {
return false;
}
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index 6f9556b..858825b 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -645,7 +645,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/view/textclassifier/TextClassificationSessionId.java b/core/java/android/view/textclassifier/TextClassificationSessionId.java
index aa680c9..5fdcc31 100644
--- a/core/java/android/view/textclassifier/TextClassificationSessionId.java
+++ b/core/java/android/view/textclassifier/TextClassificationSessionId.java
@@ -17,6 +17,7 @@
package android.view.textclassifier;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
@@ -63,7 +64,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TextClassificationSessionId that = (TextClassificationSessionId) o;
diff --git a/core/java/android/view/textservice/SpellCheckerSubtype.java b/core/java/android/view/textservice/SpellCheckerSubtype.java
index 12fe626..38a140f 100644
--- a/core/java/android/view/textservice/SpellCheckerSubtype.java
+++ b/core/java/android/view/textservice/SpellCheckerSubtype.java
@@ -203,7 +203,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof SpellCheckerSubtype) {
SpellCheckerSubtype subtype = (SpellCheckerSubtype) o;
if (subtype.mSubtypeId != SUBTYPE_ID_NONE || mSubtypeId != SUBTYPE_ID_NONE) {
diff --git a/core/java/android/webkit/PacProcessor.java b/core/java/android/webkit/PacProcessor.java
index 28496c6..d7b4e8b 100644
--- a/core/java/android/webkit/PacProcessor.java
+++ b/core/java/android/webkit/PacProcessor.java
@@ -34,7 +34,7 @@
*
* <p> There can only be one default {@link PacProcessor} instance.
* This method will create a new instance if one did not already exist, or
- * if the previous instance was released with {@link #releasePacProcessor}.
+ * if the previous instance was released with {@link #release}.
*
* @return the default PacProcessor instance.
*/
@@ -47,7 +47,7 @@
* Create a new PacProcessor instance.
*
* <p> The created instance needs to be released manually once it is no longer needed
- * by calling {@link #releasePacProcessor} to prevent memory leaks.
+ * by calling {@link #release} to prevent memory leaks.
*
* <p> The created instance is not tied to any particular {@link Network}.
* To associate {@link PacProcessor} with a {@link Network} use {@link #setNetwork} method.
@@ -82,7 +82,7 @@
* {@link #getInstance} and {@link #getInstanceForNetwork}
* for the same network will create a new instance.
*/
- default void releasePacProcessor() {
+ default void release() {
throw new UnsupportedOperationException("Not implemented");
}
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 2e5ee04..5bcfa8b 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -34,7 +34,7 @@
private final UserInfo mUserInfo;
private final PackageInfo mPackageInfo;
- public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.R;
+ public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.S;
public UserPackage(UserInfo user, PackageInfo packageInfo) {
this.mUserInfo = user;
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 8790bbd..5fc9344 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -47,7 +47,7 @@
// visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
/** @hide */
private static final String CHROMIUM_WEBVIEW_FACTORY =
- "com.android.webview.chromium.WebViewChromiumFactoryProviderForR";
+ "com.android.webview.chromium.WebViewChromiumFactoryProviderForS";
private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java
index d87bdf4..9da337a 100644
--- a/core/java/android/widget/ActivityChooserModel.java
+++ b/core/java/android/widget/ActivityChooserModel.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -839,7 +840,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
@@ -908,7 +909,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index f132197..a6dce7f 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -31,6 +31,7 @@
import static java.lang.Math.min;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
@@ -2221,7 +2222,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -2496,7 +2497,7 @@
* {@code Interval}, {@code false} otherwise.
*/
@Override
- public boolean equals(Object that) {
+ public boolean equals(@Nullable Object that) {
if (this == that) {
return true;
}
@@ -2609,7 +2610,7 @@
* {@code Spec}; {@code false} otherwise
*/
@Override
- public boolean equals(Object that) {
+ public boolean equals(@Nullable Object that) {
if (this == that) {
return true;
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7016c5c..dcfb387 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.LayoutRes;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.StyleRes;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -348,7 +349,7 @@
public String methodName;
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof MethodKey)) {
return false;
}
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index b4379ec..b884936 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -19,6 +19,7 @@
import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID;
import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND;
+import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.app.IServiceConnection;
import android.appwidget.AppWidgetHostView;
@@ -814,7 +815,7 @@
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (!(o instanceof RemoteViewsCacheKey)) {
return false;
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 2eadb56..b8a3249 100755
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1695,7 +1695,7 @@
Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
queryIntent.setComponent(searchActivity);
PendingIntent pending = PendingIntent.getActivity(getContext(), 0, queryIntent,
- PendingIntent.FLAG_ONE_SHOT);
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
// Now set up the bundle that will be inserted into the pending intent
// when it's time to do the search. We always build it here (even if empty)
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index 12b16ff..3a84c1f 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -17,7 +17,9 @@
package android.window;
import android.app.ActivityManager;
+import android.content.pm.ParceledListSlice;
import android.window.ITaskOrganizer;
+import android.window.TaskAppearedInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -26,8 +28,11 @@
/**
* Register a TaskOrganizer to manage all the tasks with supported windowing modes.
+ *
+ * @return a list of the tasks that should be managed by the organizer, not including tasks
+ * created via {@link #createRootTask}.
*/
- void registerTaskOrganizer(ITaskOrganizer organizer);
+ ParceledListSlice<TaskAppearedInfo> registerTaskOrganizer(ITaskOrganizer organizer);
/**
* Unregisters a previously registered task organizer.
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/core/java/android/window/TaskAppearedInfo.aidl
similarity index 72%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to core/java/android/window/TaskAppearedInfo.aidl
index 71cd0a7..13eba25f 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/core/java/android/window/TaskAppearedInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 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,6 +14,11 @@
* limitations under the License.
*/
-package android.media.tv;
+package android.window;
-parcelable TvChannelInfo;
+/**
+ * Data object for the task info provided when a task is presented to an organizer.
+ * @hide
+ */
+parcelable TaskAppearedInfo;
+
diff --git a/core/java/android/window/TaskAppearedInfo.java b/core/java/android/window/TaskAppearedInfo.java
new file mode 100644
index 0000000..2ff331e
--- /dev/null
+++ b/core/java/android/window/TaskAppearedInfo.java
@@ -0,0 +1,86 @@
+/*
+ * 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.window;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.SurfaceControl;
+
+/**
+ * Data object for the task info provided when a task is presented to an organizer.
+ * @hide
+ */
+@TestApi
+public final class TaskAppearedInfo implements Parcelable {
+
+ @NonNull
+ private final RunningTaskInfo mTaskInfo;
+
+ @NonNull
+ private final SurfaceControl mLeash;
+
+ @NonNull
+ public static final Creator<TaskAppearedInfo> CREATOR = new Creator<TaskAppearedInfo>() {
+ @Override
+ public TaskAppearedInfo createFromParcel(Parcel source) {
+ final RunningTaskInfo taskInfo = source.readTypedObject(RunningTaskInfo.CREATOR);
+ final SurfaceControl leash = source.readTypedObject(SurfaceControl.CREATOR);
+ return new TaskAppearedInfo(taskInfo, leash);
+ }
+
+ @Override
+ public TaskAppearedInfo[] newArray(int size) {
+ return new TaskAppearedInfo[size];
+ }
+
+ };
+
+ public TaskAppearedInfo(@NonNull RunningTaskInfo taskInfo, @NonNull SurfaceControl leash) {
+ mTaskInfo = taskInfo;
+ mLeash = leash;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mTaskInfo, flags);
+ dest.writeTypedObject(mLeash, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @return the task info.
+ */
+ @NonNull
+ public RunningTaskInfo getTaskInfo() {
+ return mTaskInfo;
+ }
+
+ /**
+ * @return the leash for the task.
+ */
+ @NonNull
+ public SurfaceControl getLeash() {
+ return mLeash;
+ }
+}
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index a7cb642..909bb47 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -17,6 +17,7 @@
package android.window;
import android.annotation.BinderThread;
+import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -51,11 +52,16 @@
/**
* Register a TaskOrganizer to manage tasks as they enter a supported windowing mode.
+ *
+ * @return a list of the tasks that should be managed by the organizer, not including tasks
+ * created via {@link #createRootTask}.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
- public final void registerOrganizer() {
+ @CallSuper
+ @NonNull
+ public List<TaskAppearedInfo> registerOrganizer() {
try {
- mTaskOrganizerController.registerTaskOrganizer(mInterface);
+ return mTaskOrganizerController.registerTaskOrganizer(mInterface).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -63,7 +69,8 @@
/** Unregisters a previously registered task organizer. */
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
- public final void unregisterOrganizer() {
+ @CallSuper
+ public void unregisterOrganizer() {
try {
mTaskOrganizerController.unregisterTaskOrganizer(mInterface);
} catch (RemoteException e) {
diff --git a/core/java/android/window/WindowContainerToken.java b/core/java/android/window/WindowContainerToken.java
index 96e8b44..22b90b2 100644
--- a/core/java/android/window/WindowContainerToken.java
+++ b/core/java/android/window/WindowContainerToken.java
@@ -17,6 +17,7 @@
package android.window;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.IBinder;
import android.os.Parcel;
@@ -83,7 +84,7 @@
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (!(obj instanceof WindowContainerToken)) {
return false;
}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index ba90154..eba4fd2 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -238,10 +238,10 @@
}
/**
- * Sets whether a container should ignore the orientation request from apps below it. It
- * currently only applies to {@link com.android.server.wm.TaskDisplayArea}. When {@code false},
- * it may rotate based on the orientation request; When {@code true}, it can never specify
- * orientation, but shows the fixed-orientation apps in the letterbox.
+ * Sets whether a container should ignore the orientation request from apps and windows below
+ * it. It currently only applies to {@link com.android.server.wm.DisplayArea}. When
+ * {@code false}, it may rotate based on the orientation request; When {@code true}, it can
+ * never specify orientation, but shows the fixed-orientation apps below it in the letterbox.
* @hide
*/
@NonNull
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 491ddba..2b4e09d 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -80,6 +80,8 @@
"com.android.server.accessibility.MagnificationController";
public static final ComponentName MAGNIFICATION_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "Magnification");
+ public static final ComponentName REDUCE_BRIGHT_COLORS_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "ReduceBrightColors");
private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -126,6 +128,11 @@
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
"1" /* Value to enable */, "0" /* Value to disable */,
R.string.color_correction_feature_name));
+ featuresMap.put(REDUCE_BRIGHT_COLORS_COMPONENT_NAME,
+ new ToggleableFrameworkFeatureInfo(
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
+ "1" /* Value to enable */, "0" /* Value to disable */,
+ R.string.reduce_bright_colors_feature_name));
sFrameworkShortcutFeaturesMap = Collections.unmodifiableMap(featuresMap);
}
return sFrameworkShortcutFeaturesMap;
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index a7c5f6d..9d06bb9 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -21,6 +21,7 @@
import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType;
import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained;
@@ -112,7 +113,7 @@
@ShortcutType int shortcutType) {
final List<AccessibilityTarget> targets = new ArrayList<>();
targets.addAll(getAccessibilityFilteredTargets(context, shortcutType));
- targets.addAll(getWhiteListingFeatureTargets(context, shortcutType));
+ targets.addAll(getAllowListingFeatureTargets(context, shortcutType));
return targets;
}
@@ -196,12 +197,12 @@
return targets;
}
- private static List<AccessibilityTarget> getWhiteListingFeatureTargets(Context context,
+ private static List<AccessibilityTarget> getAllowListingFeatureTargets(Context context,
@ShortcutType int shortcutType) {
final List<AccessibilityTarget> targets = new ArrayList<>();
- final InvisibleToggleWhiteListingFeatureTarget magnification =
- new InvisibleToggleWhiteListingFeatureTarget(context,
+ final InvisibleToggleAllowListingFeatureTarget magnification =
+ new InvisibleToggleAllowListingFeatureTarget(context,
shortcutType,
isShortcutContained(context, shortcutType, MAGNIFICATION_CONTROLLER_NAME),
MAGNIFICATION_CONTROLLER_NAME,
@@ -209,8 +210,8 @@
context.getDrawable(R.drawable.ic_accessibility_magnification),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
- final ToggleWhiteListingFeatureTarget daltonizer =
- new ToggleWhiteListingFeatureTarget(context,
+ final ToggleAllowListingFeatureTarget daltonizer =
+ new ToggleAllowListingFeatureTarget(context,
shortcutType,
isShortcutContained(context, shortcutType,
DALTONIZER_COMPONENT_NAME.flattenToString()),
@@ -219,8 +220,8 @@
context.getDrawable(R.drawable.ic_accessibility_color_correction),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
- final ToggleWhiteListingFeatureTarget colorInversion =
- new ToggleWhiteListingFeatureTarget(context,
+ final ToggleAllowListingFeatureTarget colorInversion =
+ new ToggleAllowListingFeatureTarget(context,
shortcutType,
isShortcutContained(context, shortcutType,
COLOR_INVERSION_COMPONENT_NAME.flattenToString()),
@@ -229,9 +230,21 @@
context.getDrawable(R.drawable.ic_accessibility_color_inversion),
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+ // TODO: Update with shortcut icon
+ final ToggleAllowListingFeatureTarget reduceBrightColors =
+ new ToggleAllowListingFeatureTarget(context,
+ shortcutType,
+ isShortcutContained(context, shortcutType,
+ REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString()),
+ REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString(),
+ context.getString(R.string.reduce_bright_colors_feature_name),
+ null,
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED);
+
targets.add(magnification);
targets.add(daltonizer);
targets.add(colorInversion);
+ targets.add(reduceBrightColors);
return targets;
}
diff --git a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleWhiteListingFeatureTarget.java b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAllowListingFeatureTarget.java
similarity index 91%
rename from core/java/com/android/internal/accessibility/dialog/InvisibleToggleWhiteListingFeatureTarget.java
rename to core/java/com/android/internal/accessibility/dialog/InvisibleToggleAllowListingFeatureTarget.java
index acd101b..e78036d 100644
--- a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleWhiteListingFeatureTarget.java
+++ b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAllowListingFeatureTarget.java
@@ -26,9 +26,9 @@
* Extension for {@link AccessibilityTarget} with {@link AccessibilityFragmentType#INVISIBLE_TOGGLE}
* type.
*/
-class InvisibleToggleWhiteListingFeatureTarget extends AccessibilityTarget {
+class InvisibleToggleAllowListingFeatureTarget extends AccessibilityTarget {
- InvisibleToggleWhiteListingFeatureTarget(Context context, @ShortcutType int shortcutType,
+ InvisibleToggleAllowListingFeatureTarget(Context context, @ShortcutType int shortcutType,
boolean isShortcutSwitched, String id, CharSequence label, Drawable icon, String key) {
super(context, shortcutType, AccessibilityFragmentType.INVISIBLE_TOGGLE,
isShortcutSwitched, id, label, icon, key);
diff --git a/core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java b/core/java/com/android/internal/accessibility/dialog/ToggleAllowListingFeatureTarget.java
similarity index 94%
rename from core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java
rename to core/java/com/android/internal/accessibility/dialog/ToggleAllowListingFeatureTarget.java
index 5ab9eb8..38aac70 100644
--- a/core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java
+++ b/core/java/com/android/internal/accessibility/dialog/ToggleAllowListingFeatureTarget.java
@@ -32,9 +32,9 @@
* Extension for {@link AccessibilityTarget} with {@link AccessibilityFragmentType#TOGGLE}
* type.
*/
-class ToggleWhiteListingFeatureTarget extends AccessibilityTarget {
+class ToggleAllowListingFeatureTarget extends AccessibilityTarget {
- ToggleWhiteListingFeatureTarget(Context context, @ShortcutType int shortcutType,
+ ToggleAllowListingFeatureTarget(Context context, @ShortcutType int shortcutType,
boolean isShortcutSwitched, String id, CharSequence label, Drawable icon, String key) {
super(context, shortcutType, AccessibilityFragmentType.TOGGLE,
isShortcutSwitched, id, label, icon, key);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index e0f8a1a..cb4f285 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1655,18 +1655,24 @@
private void showTargetDetails(DisplayResolveInfo ti) {
if (ti == null) return;
- List<DisplayResolveInfo> targetList;
+ ArrayList<DisplayResolveInfo> targetList;
// For multiple targets, include info on all targets
if (ti instanceof MultiDisplayResolveInfo) {
MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) ti;
targetList = mti.getTargets();
} else {
- targetList = Collections.singletonList(ti);
+ targetList = new ArrayList<DisplayResolveInfo>();
+ targetList.add(ti);
}
- ChooserTargetActionsDialogFragment f = new ChooserTargetActionsDialogFragment(
- targetList, mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
+ ChooserTargetActionsDialogFragment f = new ChooserTargetActionsDialogFragment();
+ Bundle b = new Bundle();
+ b.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
+ mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
+ b.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
+ targetList);
+ f.setArguments(b);
f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
}
@@ -1725,9 +1731,14 @@
if (targetInfo instanceof MultiDisplayResolveInfo) {
MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo;
if (!mti.hasSelected()) {
- ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment(
- mti, which,
+ ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment();
+ Bundle b = new Bundle();
+ b.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
+ b.putObject(ChooserStackedAppDialogFragment.MULTI_DRI_KEY,
+ mti);
+ b.putInt(ChooserStackedAppDialogFragment.WHICH_KEY, which);
+ f.setArguments(b);
f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
return;
diff --git a/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
index fdeba8f..726622b 100644
--- a/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
+++ b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
@@ -20,6 +20,7 @@
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.os.UserHandle;
import com.android.internal.app.chooser.DisplayResolveInfo;
@@ -32,17 +33,26 @@
public class ChooserStackedAppDialogFragment extends ChooserTargetActionsDialogFragment
implements DialogInterface.OnClickListener {
+ static final String WHICH_KEY = "which_key";
+ static final String MULTI_DRI_KEY = "multi_dri_key";
+
private MultiDisplayResolveInfo mMultiDisplayResolveInfo;
private int mParentWhich;
- public ChooserStackedAppDialogFragment() {
+ public ChooserStackedAppDialogFragment() {}
+
+ void setStateFromBundle(Bundle b) {
+ mMultiDisplayResolveInfo = (MultiDisplayResolveInfo) b.get(MULTI_DRI_KEY);
+ mTargetInfos = mMultiDisplayResolveInfo.getTargets();
+ mUserHandle = (UserHandle) b.get(USER_HANDLE_KEY);
+ mParentWhich = b.getInt(WHICH_KEY);
}
- public ChooserStackedAppDialogFragment(MultiDisplayResolveInfo targets,
- int parentWhich, UserHandle userHandle) {
- super(targets.getTargets(), userHandle);
- mMultiDisplayResolveInfo = targets;
- mParentWhich = parentWhich;
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(WHICH_KEY, mParentWhich);
+ outState.putParcelable(MULTI_DRI_KEY, mMultiDisplayResolveInfo);
}
@Override
@@ -53,6 +63,7 @@
@Override
protected Drawable getItemIcon(DisplayResolveInfo dri) {
+
// Show no icon for the group disambig dialog, null hides the imageview
return null;
}
diff --git a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
index 21063d5..9afc0e9 100644
--- a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
+++ b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
@@ -32,7 +32,6 @@
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
-import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -59,25 +58,51 @@
public class ChooserTargetActionsDialogFragment extends DialogFragment
implements DialogInterface.OnClickListener {
- protected List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
+ protected ArrayList<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
protected UserHandle mUserHandle;
- public ChooserTargetActionsDialogFragment() {
+ public static final String USER_HANDLE_KEY = "user_handle";
+ public static final String TARGET_INFOS_KEY = "target_infos";
+
+ public ChooserTargetActionsDialogFragment() {}
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ setStateFromBundle(savedInstanceState);
+ } else {
+ setStateFromBundle(getArguments());
+ }
}
- public ChooserTargetActionsDialogFragment(List<DisplayResolveInfo> targets,
- UserHandle userHandle) {
- mUserHandle = userHandle;
- mTargetInfos = targets;
+ void setStateFromBundle(Bundle b) {
+ mTargetInfos = (ArrayList<DisplayResolveInfo>) b.get(TARGET_INFOS_KEY);
+ mUserHandle = (UserHandle) b.get(USER_HANDLE_KEY);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
+ mUserHandle);
+ outState.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
+ mTargetInfos);
}
/**
* Recreate the layout from scratch to match new Sharesheet redlines
*/
+ @Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
Bundle savedInstanceState) {
-
+ if (savedInstanceState != null) {
+ setStateFromBundle(savedInstanceState);
+ } else {
+ setStateFromBundle(getArguments());
+ }
// Make the background transparent to show dialog rounding
Optional.of(getDialog()).map(Dialog::getWindow)
.ifPresent(window -> {
@@ -204,12 +229,4 @@
mTargetInfos.get(0).getResolveInfo());
}
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- // Dismiss on config changed (eg: rotation)
- // TODO: Maintain state on config change
- super.onConfigurationChanged(newConfig);
- dismiss();
- }
-
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 6edf7a3..83cbe38 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1291,7 +1291,7 @@
private void safelyStartActivityInternal(TargetInfo cti) {
// If the target is suspended, the activity will not be successfully launched.
// Do not unregister from package manager updates in this case
- if (!cti.isSuspended()) {
+ if (!cti.isSuspended() && mRegistered) {
if (mPersonalPackageMonitor != null) {
mPersonalPackageMonitor.unregister();
}
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index fe0e7d0..b00148a 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -27,19 +27,22 @@
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.UserHandle;
import com.android.internal.app.ResolverActivity;
import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
* A TargetInfo plus additional information needed to render it (such as icon and label) and
* resolve it to an activity.
*/
-public class DisplayResolveInfo implements TargetInfo {
+public class DisplayResolveInfo implements TargetInfo, Parcelable {
// Temporary flag for new chooser delegate behavior. There are occassional token
// permission errors from bouncing through the delegate. Watch out before reenabling:
// b/157272342 is one example but this issue has been reported many times
@@ -202,4 +205,41 @@
mPinned = pinned;
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeCharSequence(mDisplayLabel);
+ dest.writeCharSequence(mExtendedInfo);
+ dest.writeParcelable(mResolvedIntent, 0);
+ dest.writeParcelableArray((Intent[]) mSourceIntents.toArray(), 0);
+ dest.writeBoolean(mIsSuspended);
+ dest.writeBoolean(mPinned);
+ dest.writeParcelable(mResolveInfo, 0);
+ }
+
+ public static final Parcelable.Creator<DisplayResolveInfo> CREATOR =
+ new Parcelable.Creator<DisplayResolveInfo>() {
+ public DisplayResolveInfo createFromParcel(Parcel in) {
+ return new DisplayResolveInfo(in);
+ }
+
+ public DisplayResolveInfo[] newArray(int size) {
+ return new DisplayResolveInfo[size];
+ }
+ };
+
+ private DisplayResolveInfo(Parcel in) {
+ mDisplayLabel = in.readCharSequence();
+ mExtendedInfo = in.readCharSequence();
+ mResolvedIntent = in.readParcelable(null /* ClassLoader */);
+ mSourceIntents.addAll(
+ Arrays.asList((Intent[]) in.readParcelableArray(null /* ClassLoader */)));
+ mIsSuspended = in.readBoolean();
+ mPinned = in.readBoolean();
+ mResolveInfo = in.readParcelable(null /* ClassLoader */);
+ }
}
diff --git a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
index cf921d7..2828e25 100644
--- a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
@@ -23,14 +23,13 @@
import com.android.internal.app.ResolverActivity;
import java.util.ArrayList;
-import java.util.List;
/**
* Represents a "stack" of chooser targets for various activities within the same component.
*/
public class MultiDisplayResolveInfo extends DisplayResolveInfo {
- List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
+ ArrayList<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
// We'll use this DRI for basic presentation info - eg icon, name.
final DisplayResolveInfo mBaseInfo;
// Index of selected target
@@ -61,7 +60,7 @@
/**
* List of all DisplayResolveInfos included in this target.
*/
- public List<DisplayResolveInfo> getTargets() {
+ public ArrayList<DisplayResolveInfo> getTargets() {
return mTargetInfos;
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index dd4409c..3624f0d 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -16,7 +16,18 @@
package com.android.internal.jank;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -63,18 +74,19 @@
// Use NO_STATSD_LOGGING in case the measurement for a given CUJ should not be logged to statsd.
@VisibleForTesting
public static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = {
+ // This should be mapping to CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE.
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE,
- NO_STATSD_LOGGING,
- NO_STATSD_LOGGING,
- NO_STATSD_LOGGING,
- NO_STATSD_LOGGING,
- NO_STATSD_LOGGING,
- NO_STATSD_LOGGING,
- NO_STATSD_LOGGING,
- NO_STATSD_LOGGING,
- NO_STATSD_LOGGING,
- NO_STATSD_LOGGING,
- NO_STATSD_LOGGING,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH,
};
private static volatile InteractionJankMonitor sInstance;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 17323ba..dbe9796 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -65,7 +65,6 @@
import android.telephony.CellSignalStrength;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
-import android.telephony.ModemActivityInfo.TransmitPower;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
@@ -7791,7 +7790,7 @@
public ControllerActivityCounterImpl getOrCreateModemControllerActivityLocked() {
if (mModemControllerActivity == null) {
mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
- ModemActivityInfo.TX_POWER_LEVELS);
+ ModemActivityInfo.getNumTxPowerLevels());
}
return mModemControllerActivity;
}
@@ -9257,7 +9256,7 @@
if (in.readInt() != 0) {
mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
- ModemActivityInfo.TX_POWER_LEVELS, in);
+ ModemActivityInfo.getNumTxPowerLevels(), in);
} else {
mModemControllerActivity = null;
}
@@ -10520,7 +10519,7 @@
mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
NUM_BT_TX_LEVELS);
mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
- ModemActivityInfo.TX_POWER_LEVELS);
+ ModemActivityInfo.getNumTxPowerLevels());
mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null, mOnBatteryTimeBase);
mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null,
mOnBatteryTimeBase);
@@ -11710,26 +11709,7 @@
}
}
- private ModemActivityInfo mLastModemActivityInfo =
- new ModemActivityInfo(0, 0, 0, new int[0], 0);
-
- private ModemActivityInfo getDeltaModemActivityInfo(ModemActivityInfo activityInfo) {
- if (activityInfo == null) {
- return null;
- }
- int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
- for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
- txTimeMs[i] = activityInfo.getTransmitPowerInfo().get(i).getTimeInMillis()
- - mLastModemActivityInfo.getTransmitPowerInfo().get(i).getTimeInMillis();
- }
- ModemActivityInfo deltaInfo = new ModemActivityInfo(activityInfo.getTimestamp(),
- activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis(),
- activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis(),
- txTimeMs,
- activityInfo.getReceiveTimeMillis() - mLastModemActivityInfo.getReceiveTimeMillis());
- mLastModemActivityInfo = activityInfo;
- return deltaInfo;
- }
+ private ModemActivityInfo mLastModemActivityInfo = null;
/**
* Distribute Cell radio energy info and network traffic to apps.
@@ -11746,7 +11726,9 @@
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
}
- ModemActivityInfo deltaInfo = getDeltaModemActivityInfo(activityInfo);
+ ModemActivityInfo deltaInfo = mLastModemActivityInfo == null ? activityInfo
+ : mLastModemActivityInfo.getDelta(activityInfo);
+ mLastModemActivityInfo = activityInfo;
// Add modem tx power to history.
addModemTxPowerToHistory(deltaInfo, elapsedRealtimeMs, uptimeMs);
@@ -11778,10 +11760,9 @@
mModemActivity.getSleepTimeCounter().addCountLocked(
deltaInfo.getSleepTimeMillis());
mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getReceiveTimeMillis());
- for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
+ for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); lvl++) {
mModemActivity.getTxTimeCounters()[lvl]
- .addCountLocked(deltaInfo.getTransmitPowerInfo()
- .get(lvl).getTimeInMillis());
+ .addCountLocked(deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl));
}
// POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
@@ -11795,11 +11776,11 @@
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE)
+ deltaInfo.getReceiveTimeMillis() *
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
- List<TransmitPower> txPowerInfo = deltaInfo.getTransmitPowerInfo();
- for (int i = 0; i < Math.min(txPowerInfo.size(),
+ for (int i = 0; i < Math.min(ModemActivityInfo.getNumTxPowerLevels(),
CellSignalStrength.getNumSignalStrengthLevels()); i++) {
- energyUsed += txPowerInfo.get(i).getTimeInMillis() * mPowerProfile
- .getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
+ energyUsed += deltaInfo.getTransmitDurationMillisAtPowerLevel(i)
+ * mPowerProfile.getAveragePower(
+ PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
}
// We store the power drain as mAms.
@@ -11894,10 +11875,10 @@
}
if (totalTxPackets > 0 && entry.txPackets > 0) {
- for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
- long txMs =
- entry.txPackets * deltaInfo.getTransmitPowerInfo()
- .get(lvl).getTimeInMillis();
+ for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels();
+ lvl++) {
+ long txMs = entry.txPackets
+ * deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl);
txMs /= totalTxPackets;
activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs);
}
@@ -11929,18 +11910,14 @@
if (activityInfo == null) {
return;
}
- List<TransmitPower> txPowerInfo = activityInfo.getTransmitPowerInfo();
- if (txPowerInfo == null || txPowerInfo.size() != ModemActivityInfo.TX_POWER_LEVELS) {
- return;
- }
int levelMaxTimeSpent = 0;
- for (int i = 1; i < txPowerInfo.size(); i++) {
- if (txPowerInfo.get(i).getTimeInMillis() > txPowerInfo.get(levelMaxTimeSpent)
- .getTimeInMillis()) {
+ for (int i = 1; i < ModemActivityInfo.getNumTxPowerLevels(); i++) {
+ if (activityInfo.getTransmitDurationMillisAtPowerLevel(i)
+ > activityInfo.getTransmitDurationMillisAtPowerLevel(levelMaxTimeSpent)) {
levelMaxTimeSpent = i;
}
}
- if (levelMaxTimeSpent == ModemActivityInfo.TX_POWER_LEVELS - 1) {
+ if (levelMaxTimeSpent == ModemActivityInfo.getNumTxPowerLevels() - 1) {
mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
@@ -12597,11 +12574,11 @@
// This could happen if the isolated uid mapping was removed before that process
// was actually killed.
mCpuUidUserSysTimeReader.removeUid(uid);
- Slog.d(TAG, "Got readings for an isolated uid with no mapping: " + uid);
+ if (DEBUG) Slog.d(TAG, "Got readings for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
- Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
+ if (DEBUG) Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
mCpuUidUserSysTimeReader.removeUid(uid);
return;
}
@@ -12706,11 +12683,11 @@
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
mCpuUidFreqTimeReader.removeUid(uid);
- Slog.d(TAG, "Got freq readings for an isolated uid with no mapping: " + uid);
+ if (DEBUG) Slog.d(TAG, "Got freq readings for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
- Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid);
+ if (DEBUG) Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid);
mCpuUidFreqTimeReader.removeUid(uid);
return;
}
@@ -12820,11 +12797,11 @@
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
mCpuUidActiveTimeReader.removeUid(uid);
- Slog.w(TAG, "Got active times for an isolated uid with no mapping: " + uid);
+ if (DEBUG) Slog.w(TAG, "Got active times for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
- Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
+ if (DEBUG) Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
mCpuUidActiveTimeReader.removeUid(uid);
return;
}
@@ -12850,11 +12827,11 @@
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
mCpuUidClusterTimeReader.removeUid(uid);
- Slog.w(TAG, "Got cluster times for an isolated uid with no mapping: " + uid);
+ if (DEBUG) Slog.w(TAG, "Got cluster times for an isolated uid: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
- Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
+ if (DEBUG) Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
mCpuUidClusterTimeReader.removeUid(uid);
return;
}
@@ -13462,7 +13439,7 @@
timeInRxSignalStrengthLevelMs[i] =
getPhoneSignalStrengthTime(i, rawRealTimeUs, which) / 1000;
}
- long[] txTimeMs = new long[Math.min(ModemActivityInfo.TX_POWER_LEVELS,
+ long[] txTimeMs = new long[Math.min(ModemActivityInfo.getNumTxPowerLevels(),
counter.getTxTimeCounters().length)];
long totalTxTimeMs = 0;
for (int i = 0; i < txTimeMs.length; i++) {
@@ -15601,7 +15578,7 @@
mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
NUM_BT_TX_LEVELS, in);
mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
- ModemActivityInfo.TX_POWER_LEVELS, in);
+ ModemActivityInfo.getNumTxPowerLevels(), in);
mHasWifiReporting = in.readInt() != 0;
mHasBluetoothReporting = in.readInt() != 0;
mHasModemReporting = in.readInt() != 0;
diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
index 4f687f1..e6a9623 100644
--- a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
@@ -45,6 +45,7 @@
private static final String TAG = "KernelSingleProcCpuThreadRdr";
private static final boolean DEBUG = false;
+ private static final boolean NATIVE_ENABLED = true;
/**
* The name of the file to read CPU statistics from, must be found in {@code
@@ -64,7 +65,7 @@
private static final Path INITIAL_TIME_IN_STATE_PATH = Paths.get("self/time_in_state");
/** See https://man7.org/linux/man-pages/man5/proc.5.html */
- private static final int[] PROCESS_FULL_STATS_FORMAT = new int[]{
+ private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
PROC_SPACE_TERM,
PROC_SPACE_TERM,
PROC_SPACE_TERM,
@@ -162,6 +163,7 @@
/**
* Get the total and per-thread CPU usage of the process with the PID specified in the
* constructor.
+ *
* @param selectedThreadIds a SORTED array of native Thread IDs whose CPU times should
* be aggregated as a group. This is expected to be a subset
* of all thread IDs owned by the process.
@@ -173,6 +175,20 @@
+ mPid);
}
+ int cpuFrequencyCount = getCpuFrequencyCount();
+ ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(cpuFrequencyCount);
+
+ if (NATIVE_ENABLED) {
+ boolean result = readProcessCpuUsage(mProcPath.toString(), mPid,
+ selectedThreadIds, processCpuUsage.processCpuTimesMillis,
+ processCpuUsage.threadCpuTimesMillis,
+ processCpuUsage.selectedThreadCpuTimesMillis);
+ if (!result) {
+ return null;
+ }
+ return processCpuUsage;
+ }
+
if (!isSorted(selectedThreadIds)) {
throw new IllegalArgumentException("selectedThreadIds is not sorted: "
+ Arrays.toString(selectedThreadIds));
@@ -189,8 +205,6 @@
long processCpuTimeMillis = (utime + stime) * mJiffyMillis;
- int cpuFrequencyCount = getCpuFrequencyCount();
- ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(cpuFrequencyCount);
try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(mThreadsDirectoryPath)) {
for (Path threadDirectory : threadPaths) {
readThreadCpuUsage(processCpuUsage, selectedThreadIds, threadDirectory);
@@ -274,4 +288,8 @@
}
return true;
}
+
+ private native boolean readProcessCpuUsage(String procPath, int pid, int[] selectedThreadIds,
+ long[] processCpuTimesMillis, long[] threadCpuTimesMillis,
+ long[] selectedThreadCpuTimesMillis);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index caae518..77c7ce8 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -102,6 +102,13 @@
void onCameraLaunchGestureDetected(int source);
/**
+ * Notifies the status bar that the Emergency Action launch gesture has been detected.
+ *
+ * TODO(b/169175022) Update method name and docs when feature name is locked.
+ */
+ void onEmergencyActionLaunchGestureDetected();
+
+ /**
* Shows the picture-in-picture menu if an activity is in picture-in-picture mode.
*/
void showPictureInPictureMenu();
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 555f62c..c7ac189 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -15,14 +15,22 @@
package com.android.internal.util;
import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
import android.os.Build;
import android.os.SystemClock;
import android.os.Trace;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.EventLog;
+import android.util.KeyValueListParser;
import android.util.Log;
import android.util.SparseLongArray;
import com.android.internal.logging.EventLogTags;
+import com.android.internal.os.BackgroundThread;
+
+import java.util.concurrent.ThreadLocalRandom;
/**
* Class to track various latencies in SystemUI. It then writes the latency to statsd and also
@@ -34,6 +42,12 @@
*/
public class LatencyTracker {
private static final String TAG = "LatencyTracker";
+ private static final String SETTINGS_ENABLED_KEY = "enabled";
+ private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+ /** Default to being enabled on debug builds. */
+ private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE;
+ /** Default to collecting data for 1/5 of all actions (randomly sampled). */
+ private static final int DEFAULT_SAMPLING_INTERVAL = 5;
/**
* Time it takes until the first frame of the notification panel to be displayed while expanding
@@ -76,7 +90,7 @@
*/
public static final int ACTION_FACE_WAKE_AND_UNLOCK = 7;
- private static final String[] NAMES = new String[] {
+ private static final String[] NAMES = new String[]{
"expand panel",
"toggle recents",
"fingerprint wake-and-unlock",
@@ -84,9 +98,9 @@
"check credential unlocked",
"turn on screen",
"rotate the screen",
- "face wake-and-unlock" };
+ "face wake-and-unlock"};
- private static final int[] STATSD_ACTION = new int[] {
+ private static final int[] STATSD_ACTION = new int[]{
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
@@ -100,20 +114,59 @@
private static LatencyTracker sLatencyTracker;
private final SparseLongArray mStartRtc = new SparseLongArray();
+ private final Context mContext;
+ private volatile int mSamplingInterval;
+ private volatile boolean mEnabled;
public static LatencyTracker getInstance(Context context) {
if (sLatencyTracker == null) {
- sLatencyTracker = new LatencyTracker();
+ synchronized (LatencyTracker.class) {
+ if (sLatencyTracker == null) {
+ sLatencyTracker = new LatencyTracker(context);
+ }
+ }
}
return sLatencyTracker;
}
+ public LatencyTracker(Context context) {
+ mContext = context;
+ mEnabled = DEFAULT_ENABLED;
+ mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
+
+ // Post initialization to the background in case we're running on the main thread.
+ BackgroundThread.getHandler().post(this::registerSettingsObserver);
+ BackgroundThread.getHandler().post(this::readSettings);
+ }
+
+ private void registerSettingsObserver() {
+ Uri settingsUri = Settings.Global.getUriFor(Settings.Global.LATENCY_TRACKER);
+ mContext.getContentResolver().registerContentObserver(
+ settingsUri, false, new SettingsObserver(this), UserHandle.myUserId());
+ }
+
+ private void readSettings() {
+ KeyValueListParser parser = new KeyValueListParser(',');
+ String settingsValue = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.LATENCY_TRACKER);
+
+ try {
+ parser.setString(settingsValue);
+ mSamplingInterval = parser.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
+ DEFAULT_SAMPLING_INTERVAL);
+ mEnabled = parser.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Incorrect settings format", e);
+ mEnabled = false;
+ }
+ }
+
public static boolean isEnabled(Context ctx) {
return getInstance(ctx).isEnabled();
}
public boolean isEnabled() {
- return Build.IS_DEBUGGABLE;
+ return mEnabled;
}
/**
@@ -145,19 +198,48 @@
}
mStartRtc.delete(action);
Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, NAMES[action], 0);
- logAction(action, (int)(endRtc - startRtc));
+ logAction(action, (int) (endRtc - startRtc));
}
/**
* Logs an action that has started and ended. This needs to be called from the main thread.
*
- * @param action The action to end. One of the ACTION_* values.
- * @param duration The duration of the action in ms.
+ * @param action The action to end. One of the ACTION_* values.
+ * @param duration The duration of the action in ms.
*/
- public static void logAction(int action, int duration) {
+ public void logAction(int action, int duration) {
+ boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+ logActionDeprecated(action, duration, shouldSample);
+ }
+
+ /**
+ * Logs an action that has started and ended. This needs to be called from the main thread.
+ *
+ * @param action The action to end. One of the ACTION_* values.
+ * @param duration The duration of the action in ms.
+ * @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog.
+ */
+ public static void logActionDeprecated(int action, int duration, boolean writeToStatsLog) {
Log.i(TAG, "action=" + action + " latency=" + duration);
EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
- FrameworkStatsLog.write(
- FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
+
+ if (writeToStatsLog) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
+ }
+ }
+
+ private static class SettingsObserver extends ContentObserver {
+ private final LatencyTracker mThisTracker;
+
+ SettingsObserver(LatencyTracker thisTracker) {
+ super(BackgroundThread.getHandler());
+ mThisTracker = thisTracker;
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, int userId) {
+ mThisTracker.readSettings();
+ }
}
}
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index dae649a..e80e545 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -31,6 +31,12 @@
*/
public class Preconditions {
+ /**
+ * Ensures that an expression checking an argument is true.
+ *
+ * @param expression the expression to check
+ * @throws IllegalArgumentException if {@code expression} is false
+ */
@UnsupportedAppUsage
public static void checkArgument(boolean expression) {
if (!expression) {
@@ -62,8 +68,9 @@
* @param messageArgs arguments for {@code messageTemplate}
* @throws IllegalArgumentException if {@code expression} is false
*/
- public static void checkArgument(boolean expression,
- final String messageTemplate,
+ public static void checkArgument(
+ final boolean expression,
+ final @NonNull String messageTemplate,
final Object... messageArgs) {
if (!expression) {
throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
@@ -114,7 +121,9 @@
* @throws IllegalArgumentException if {@code string} is empty
*/
public static @NonNull <T extends CharSequence> T checkStringNotEmpty(
- final T string, final String messageTemplate, final Object... messageArgs) {
+ final T string,
+ final @NonNull String messageTemplate,
+ final Object... messageArgs) {
if (TextUtils.isEmpty(string)) {
throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
}
@@ -160,18 +169,22 @@
}
/**
- * Ensures the truth of an expression involving the state of the calling
- * instance, but not involving any parameters to the calling method.
+ * Ensures that an object reference passed as a parameter to the calling
+ * method is not null.
*
- * @param expression a boolean expression
- * @param message exception message
- * @throws IllegalStateException if {@code expression} is false
+ * @param messageTemplate a printf-style message template to use if the check fails; will
+ * be converted to a string using {@link String#format(String, Object...)}
+ * @param messageArgs arguments for {@code messageTemplate}
+ * @throws NullPointerException if {@code reference} is null
*/
- @UnsupportedAppUsage
- public static void checkState(final boolean expression, String message) {
- if (!expression) {
- throw new IllegalStateException(message);
+ public static @NonNull <T> T checkNotNull(
+ final T reference,
+ final @NonNull String messageTemplate,
+ final Object... messageArgs) {
+ if (reference == null) {
+ throw new NullPointerException(String.format(messageTemplate, messageArgs));
}
+ return reference;
}
/**
@@ -187,6 +200,41 @@
}
/**
+ * Ensures the truth of an expression involving the state of the calling
+ * instance, but not involving any parameters to the calling method.
+ *
+ * @param expression a boolean expression
+ * @param errorMessage the exception message to use if the check fails; will
+ * be converted to a string using {@link String#valueOf(Object)}
+ * @throws IllegalStateException if {@code expression} is false
+ */
+ @UnsupportedAppUsage
+ public static void checkState(final boolean expression, String errorMessage) {
+ if (!expression) {
+ throw new IllegalStateException(errorMessage);
+ }
+ }
+
+ /**
+ * Ensures the truth of an expression involving the state of the calling
+ * instance, but not involving any parameters to the calling method.
+ *
+ * @param expression a boolean expression
+ * @param messageTemplate a printf-style message template to use if the check fails; will
+ * be converted to a string using {@link String#format(String, Object...)}
+ * @param messageArgs arguments for {@code messageTemplate}
+ * @throws IllegalStateException if {@code expression} is false
+ */
+ public static void checkState(
+ final boolean expression,
+ final @NonNull String messageTemplate,
+ final Object... messageArgs) {
+ if (!expression) {
+ throw new IllegalStateException(String.format(messageTemplate, messageArgs));
+ }
+ }
+
+ /**
* Ensures the truth of an expression involving whether the calling identity is authorized to
* call the calling method.
*
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index a23fc4b..0bafb2f 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -279,6 +279,7 @@
final Runnable mScreenshotTimeout = () -> {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
+ Log.e(TAG, "Timed out before getting screenshot capture response");
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
mScreenshotService = null;
@@ -353,6 +354,7 @@
mScreenshotService = null;
// only log an error if we're still within the timeout period
if (handler.hasCallbacks(mScreenshotTimeout)) {
+ Log.e(TAG, "Screenshot service disconnected");
handler.removeCallbacks(mScreenshotTimeout);
notifyScreenshotError();
}
diff --git a/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
new file mode 100644
index 0000000..461e116
--- /dev/null
+++ b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view;
+
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+/**
+ * ScrollCapture for RecyclerView and <i>RecyclerView-like</i> ViewGroups.
+ * <p>
+ * Requirements for proper operation:
+ * <ul>
+ * <li>at least one visible child view</li>
+ * <li>scrolls by pixels in response to {@link View#scrollBy(int, int)}.
+ * <li>reports ability to scroll with {@link View#canScrollVertically(int)}
+ * <li>properly implements {@link ViewParent#requestChildRectangleOnScreen(View, Rect, boolean)}
+ * </ul>
+ *
+ * @see ScrollCaptureViewSupport
+ */
+public class RecyclerViewCaptureHelper implements ScrollCaptureViewHelper<ViewGroup> {
+
+ // Experiment
+ private static final boolean DISABLE_ANIMATORS = false;
+ // Experiment
+ private static final boolean STOP_RENDER_THREAD = false;
+
+ private static final String TAG = "RVCaptureHelper";
+ private int mScrollDelta;
+ private boolean mScrollBarWasEnabled;
+ private int mOverScrollMode;
+ private float mDurationScale;
+
+ @Override
+ public void onPrepareForStart(@NonNull ViewGroup view, Rect scrollBounds) {
+ mScrollDelta = 0;
+
+ mOverScrollMode = view.getOverScrollMode();
+ view.setOverScrollMode(View.OVER_SCROLL_NEVER);
+
+ mScrollBarWasEnabled = view.isVerticalScrollBarEnabled();
+ view.setVerticalScrollBarEnabled(false);
+ if (DISABLE_ANIMATORS) {
+ mDurationScale = ValueAnimator.getDurationScale();
+ ValueAnimator.setDurationScale(0);
+ }
+ if (STOP_RENDER_THREAD) {
+ view.getThreadedRenderer().stop();
+ }
+ }
+
+ @Override
+ public ScrollResult onScrollRequested(@NonNull ViewGroup recyclerView, Rect scrollBounds,
+ Rect requestRect) {
+ ScrollResult result = new ScrollResult();
+ result.requestedArea = new Rect(requestRect);
+ result.scrollDelta = mScrollDelta;
+ result.availableArea = new Rect(); // empty
+
+ Log.d(TAG, "current scrollDelta: " + mScrollDelta);
+ if (!recyclerView.isVisibleToUser() || recyclerView.getChildCount() == 0) {
+ Log.w(TAG, "recyclerView is empty or not visible, cannot continue");
+ return result; // result.availableArea == empty Rect
+ }
+
+ // move from scrollBounds-relative to parent-local coordinates
+ Rect requestedContainerBounds = new Rect(requestRect);
+ requestedContainerBounds.offset(0, -mScrollDelta);
+ requestedContainerBounds.offset(scrollBounds.left, scrollBounds.top);
+
+ // requestedContainerBounds is now in recyclerview-local coordinates
+ Log.d(TAG, "requestedContainerBounds: " + requestedContainerBounds);
+
+ // Save a copy for later
+ View anchor = findChildNearestTarget(recyclerView, requestedContainerBounds);
+ if (anchor == null) {
+ Log.d(TAG, "Failed to locate anchor view");
+ return result; // result.availableArea == null
+ }
+
+ Log.d(TAG, "Anchor view:" + anchor);
+ Rect requestedContentBounds = new Rect(requestedContainerBounds);
+ recyclerView.offsetRectIntoDescendantCoords(anchor, requestedContentBounds);
+
+ Log.d(TAG, "requestedContentBounds = " + requestedContentBounds);
+ int prevAnchorTop = anchor.getTop();
+ // Note: requestChildRectangleOnScreen may modify rectangle, must pass pass in a copy here
+ Rect input = new Rect(requestedContentBounds);
+ if (recyclerView.requestChildRectangleOnScreen(anchor, input, true)) {
+ int scrolled = prevAnchorTop - anchor.getTop(); // inverse of movement
+ Log.d(TAG, "RecyclerView scrolled by " + scrolled + " px");
+ mScrollDelta += scrolled; // view.top-- is equivalent to parent.scrollY++
+ result.scrollDelta = mScrollDelta;
+ Log.d(TAG, "requestedContentBounds, (post-request-rect) = " + requestedContentBounds);
+ }
+
+ requestedContainerBounds.set(requestedContentBounds);
+ recyclerView.offsetDescendantRectToMyCoords(anchor, requestedContainerBounds);
+ Log.d(TAG, "requestedContainerBounds, (post-scroll): " + requestedContainerBounds);
+
+ Rect recyclerLocalVisible = new Rect(scrollBounds);
+ recyclerView.getLocalVisibleRect(recyclerLocalVisible);
+ Log.d(TAG, "recyclerLocalVisible: " + recyclerLocalVisible);
+
+ if (!requestedContainerBounds.intersect(recyclerLocalVisible)) {
+ // Requested area is still not visible
+ Log.d(TAG, "requested bounds not visible!");
+ return result;
+ }
+ Rect available = new Rect(requestedContainerBounds);
+ available.offset(-scrollBounds.left, -scrollBounds.top);
+ available.offset(0, mScrollDelta);
+ result.availableArea = available;
+ Log.d(TAG, "availableArea: " + result.availableArea);
+ return result;
+ }
+
+ /**
+ * Find a view that is located "closest" to targetRect. Returns the first view to fully
+ * vertically overlap the target targetRect. If none found, returns the view with an edge
+ * nearest the target targetRect.
+ *
+ * @param parent the parent vertical layout
+ * @param targetRect a rectangle in local coordinates of <code>parent</code>
+ * @return a child view within parent matching the criteria or null
+ */
+ static View findChildNearestTarget(ViewGroup parent, Rect targetRect) {
+ View selected = null;
+ int minCenterDistance = Integer.MAX_VALUE;
+ int maxOverlap = 0;
+
+ // allowable center-center distance, relative to targetRect.
+ // if within this range, taller views are preferred
+ final float preferredRangeFromCenterPercent = 0.25f;
+ final int preferredDistance =
+ (int) (preferredRangeFromCenterPercent * targetRect.height());
+
+ Rect parentLocalVis = new Rect();
+ parent.getLocalVisibleRect(parentLocalVis);
+ Log.d(TAG, "findChildNearestTarget: parentVis=" + parentLocalVis
+ + " targetRect=" + targetRect);
+
+ Rect frame = new Rect();
+ for (int i = 0; i < parent.getChildCount(); i++) {
+ final View child = parent.getChildAt(i);
+ child.getHitRect(frame);
+ Log.d(TAG, "child #" + i + " hitRect=" + frame);
+
+ if (child.getVisibility() != View.VISIBLE) {
+ Log.d(TAG, "child #" + i + " is not visible");
+ continue;
+ }
+
+ int centerDistance = Math.abs(targetRect.centerY() - frame.centerY());
+ Log.d(TAG, "child #" + i + " : center to center: " + centerDistance + "px");
+
+ if (centerDistance < minCenterDistance) {
+ // closer to center
+ minCenterDistance = centerDistance;
+ selected = child;
+ } else if (frame.intersect(targetRect) && (frame.height() > preferredDistance)) {
+ // within X% pixels of center, but taller
+ selected = child;
+ }
+ }
+ return selected;
+ }
+
+
+ @Override
+ public void onPrepareForEnd(@NonNull ViewGroup view) {
+ // Restore original position and state
+ view.scrollBy(0, mScrollDelta);
+ view.setOverScrollMode(mOverScrollMode);
+ view.setVerticalScrollBarEnabled(mScrollBarWasEnabled);
+ if (DISABLE_ANIMATORS) {
+ ValueAnimator.setDurationScale(mDurationScale);
+ }
+ if (STOP_RENDER_THREAD) {
+ view.getThreadedRenderer().start();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/ScrollCaptureInternal.java b/core/java/com/android/internal/view/ScrollCaptureInternal.java
index c589afde..ae1a815 100644
--- a/core/java/com/android/internal/view/ScrollCaptureInternal.java
+++ b/core/java/com/android/internal/view/ScrollCaptureInternal.java
@@ -17,8 +17,11 @@
package com.android.internal.view;
import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
+import android.util.Log;
import android.view.ScrollCaptureCallback;
import android.view.View;
import android.view.ViewGroup;
@@ -29,6 +32,12 @@
public class ScrollCaptureInternal {
private static final String TAG = "ScrollCaptureInternal";
+ // Log found scrolling views
+ private static final boolean DEBUG = true;
+
+ // Log all investigated views, as well as heuristic checks
+ private static final boolean DEBUG_VERBOSE = false;
+
private static final int UP = -1;
private static final int DOWN = 1;
@@ -57,38 +66,72 @@
* This needs to be fast and not alloc memory. It's called on everything in the tree not marked
* as excluded during scroll capture search.
*/
- public static int detectScrollingType(View view) {
+ private static int detectScrollingType(View view) {
// Must be a ViewGroup
if (!(view instanceof ViewGroup)) {
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "hint: not a subclass of ViewGroup");
+ }
return TYPE_FIXED;
}
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "hint: is a subclass of ViewGroup");
+ }
// Confirm that it can scroll.
if (!(view.canScrollVertically(DOWN) || view.canScrollVertically(UP))) {
// Nothing to scroll here, move along.
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "hint: cannot be scrolled");
+ }
return TYPE_FIXED;
}
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "hint: can be scrolled up or down");
+ }
// ScrollViews accept only a single child.
if (((ViewGroup) view).getChildCount() > 1) {
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "hint: scrollable with multiple children");
+ }
return TYPE_RECYCLING;
}
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "hint: less than two child views");
+ }
//Because recycling containers don't use scrollY, a non-zero value means Scroll view.
if (view.getScrollY() != 0) {
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "hint: scrollY != 0");
+ }
return TYPE_SCROLLING;
}
+ Log.v(TAG, "hint: scrollY == 0");
// Since scrollY cannot be negative, this means a Recycling view.
if (view.canScrollVertically(UP)) {
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "hint: able to scroll up");
+ }
return TYPE_RECYCLING;
}
- // canScrollVertically(UP) == false, getScrollY() == 0, getChildCount() == 1.
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "hint: cannot be scrolled up");
+ }
+ // canScrollVertically(UP) == false, getScrollY() == 0, getChildCount() == 1.
// For Recycling containers, this should be a no-op (RecyclerView logs a warning)
view.scrollTo(view.getScrollX(), 1);
// A scrolling container would have moved by 1px.
if (view.getScrollY() == 1) {
view.scrollTo(view.getScrollX(), 0);
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "hint: scrollTo caused scrollY to change");
+ }
return TYPE_SCROLLING;
}
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "hint: scrollTo did not cause scrollY to change");
+ }
return TYPE_RECYCLING;
}
@@ -99,19 +142,61 @@
* @param localVisibleRect the visible area of the given view in local coordinates, as supplied
* by the view parent
* @param positionInWindow the offset of localVisibleRect within the window
- *
* @return a new callback or null if the View isn't supported
*/
@Nullable
public ScrollCaptureCallback requestCallback(View view, Rect localVisibleRect,
Point positionInWindow) {
// Nothing to see here yet.
+ if (DEBUG_VERBOSE) {
+ Log.v(TAG, "scroll capture: checking " + view.getClass().getName()
+ + "[" + resolveId(view.getContext(), view.getId()) + "]");
+ }
int i = detectScrollingType(view);
switch (i) {
case TYPE_SCROLLING:
+ if (DEBUG) {
+ Log.d(TAG, "scroll capture: FOUND " + view.getClass().getName()
+ + "[" + resolveId(view.getContext(), view.getId()) + "]"
+ + " -> TYPE_SCROLLING");
+ }
return new ScrollCaptureViewSupport<>((ViewGroup) view,
new ScrollViewCaptureHelper());
+ case TYPE_RECYCLING:
+ if (DEBUG) {
+ Log.d(TAG, "scroll capture: FOUND " + view.getClass().getName()
+ + "[" + resolveId(view.getContext(), view.getId()) + "]"
+ + " -> TYPE_RECYCLING");
+ }
+ return new ScrollCaptureViewSupport<>((ViewGroup) view,
+ new RecyclerViewCaptureHelper());
+ case TYPE_FIXED:
+ // ignore
+ break;
+
}
return null;
}
+
+ // Lifted from ViewDebug (package protected)
+
+ private static String formatIntToHexString(int value) {
+ return "0x" + Integer.toHexString(value).toUpperCase();
+ }
+
+ static String resolveId(Context context, int id) {
+ String fieldValue;
+ final Resources resources = context.getResources();
+ if (id >= 0) {
+ try {
+ fieldValue = resources.getResourceTypeName(id) + '/'
+ + resources.getResourceEntryName(id);
+ } catch (Resources.NotFoundException e) {
+ fieldValue = "id/" + formatIntToHexString(id);
+ }
+ } else {
+ fieldValue = "NO_ID";
+ }
+ return fieldValue;
+ }
}
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
index a92e978..9829d0b 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
@@ -17,7 +17,6 @@
package com.android.internal.view;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.graphics.Rect;
import android.view.View;
@@ -62,8 +61,8 @@
* @param view the view being captured
* @return true if the callback should respond to a request with scroll bounds
*/
- default boolean onAcceptSession(@Nullable V view) {
- return view != null && view.isVisibleToUser()
+ default boolean onAcceptSession(@NonNull V view) {
+ return view.isVisibleToUser()
&& (view.canScrollVertically(UP) || view.canScrollVertically(DOWN));
}
@@ -73,7 +72,7 @@
*
* @param view the view being captured
*/
- default Rect onComputeScrollBounds(@Nullable V view) {
+ @NonNull default Rect onComputeScrollBounds(@NonNull V view) {
return new Rect(view.getPaddingLeft(), view.getPaddingTop(),
view.getWidth() - view.getPaddingRight(),
view.getHeight() - view.getPaddingBottom());
@@ -88,7 +87,7 @@
* @param view the view being captured
* @param scrollBounds the bounds within {@code view} where content scrolls
*/
- void onPrepareForStart(@NonNull V view, Rect scrollBounds);
+ void onPrepareForStart(@NonNull V view, @NonNull Rect scrollBounds);
/**
* Map the request onto the screen.
@@ -105,7 +104,9 @@
* content to capture for the request
* @return the result of the request as a {@link ScrollResult}
*/
- ScrollResult onScrollRequested(@NonNull V view, Rect scrollBounds, Rect requestRect);
+ @NonNull
+ ScrollResult onScrollRequested(@NonNull V view, @NonNull Rect scrollBounds,
+ @NonNull Rect requestRect);
/**
* Restore the target after capture.
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
index 7b4f73f..85fa791 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
@@ -23,8 +23,8 @@
import android.graphics.RectF;
import android.graphics.RenderNode;
import android.os.Handler;
-import android.os.SystemClock;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.ScrollCaptureCallback;
import android.view.ScrollCaptureSession;
import android.view.Surface;
@@ -46,8 +46,12 @@
*/
public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCallback {
+ public static final long NO_FRAME_PRODUCED = -1;
+
private static final String TAG = "ScrollCaptureViewSupport";
+ private static final boolean WAIT_FOR_ANIMATION = true;
+
private final WeakReference<V> mWeakView;
private final ScrollCaptureViewHelper<V> mViewHelper;
private ViewRenderer mRenderer;
@@ -99,12 +103,16 @@
V view = mWeakView.get();
if (view == null || !view.isVisibleToUser()) {
// Signal to the controller that we have a problem and can't continue.
- session.notifyBufferSent(0, null);
+ session.notifyBufferSent(NO_FRAME_PRODUCED, 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()) {
+ session.notifyBufferSent(NO_FRAME_PRODUCED, 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
@@ -112,8 +120,19 @@
Rect viewCaptureArea = new Rect(scrollResult.availableArea);
viewCaptureArea.offset(0, -scrollResult.scrollDelta);
- mRenderer.renderView(view, viewCaptureArea, mUiHandler,
- (frameNumber) -> session.notifyBufferSent(frameNumber, scrollResult.availableArea));
+ if (WAIT_FOR_ANIMATION) {
+ Log.d(TAG, "render: delaying until animation");
+ view.postOnAnimation(() -> {
+ Log.d(TAG, "postOnAnimation(): rendering now");
+ long resultFrame = mRenderer.renderView(view, viewCaptureArea);
+ Log.d(TAG, "notifyBufferSent: " + scrollResult.availableArea);
+
+ session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea));
+ });
+ } else {
+ long resultFrame = mRenderer.renderView(view, viewCaptureArea);
+ session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea));
+ }
}
@Override
@@ -132,8 +151,7 @@
/**
* Internal helper class which assists in rendering sections of the view hierarchy relative to a
- * given view. Used by framework implementations of ScrollCaptureHandler to render and dispatch
- * image requests.
+ * given view.
*/
static final class ViewRenderer {
// alpha, "reasonable default" from Javadoc
@@ -157,14 +175,11 @@
private final Matrix mTempMatrix = new Matrix();
private final int[] mTempLocation = new int[2];
private long mLastRenderedSourceDrawingId = -1;
-
-
- public interface FrameCompleteListener {
- void onFrameComplete(long frameNumber);
- }
+ private Surface mSurface;
ViewRenderer() {
mRenderer = new HardwareRenderer();
+ mRenderer.setName("ScrollCapture");
mCaptureRenderNode = new RenderNode("ScrollCaptureRoot");
mRenderer.setContentRoot(mCaptureRenderNode);
@@ -173,6 +188,7 @@
}
public void setSurface(Surface surface) {
+ mSurface = surface;
mRenderer.setSurface(surface);
}
@@ -223,20 +239,38 @@
mCaptureRenderNode.endRecording();
}
- public void renderView(View view, Rect sourceRect, Handler handler,
- FrameCompleteListener frameListener) {
+ public long renderView(View view, Rect sourceRect) {
if (updateForView(view)) {
setupLighting(view);
}
view.invalidate();
updateRootNode(view, sourceRect);
HardwareRenderer.FrameRenderRequest request = mRenderer.createRenderRequest();
- request.setVsyncTime(SystemClock.elapsedRealtimeNanos());
- // private API b/c request.setFrameCommitCallback does not provide access to frameNumber
- mRenderer.setFrameCompleteCallback(
- frameNr -> handler.post(() -> frameListener.onFrameComplete(frameNr)));
+ long timestamp = System.nanoTime();
+ request.setVsyncTime(timestamp);
+
+ // Would be nice to access nextFrameNumber from HwR without having to hold on to Surface
+ final long frameNumber = mSurface.getNextFrameNumber();
+
+ // Block until a frame is presented to the Surface
request.setWaitForPresent(true);
- request.syncAndDraw();
+
+ switch (request.syncAndDraw()) {
+ case HardwareRenderer.SYNC_OK:
+ case HardwareRenderer.SYNC_REDRAW_REQUESTED:
+ return frameNumber;
+
+ case HardwareRenderer.SYNC_FRAME_DROPPED:
+ Log.e(TAG, "syncAndDraw(): SYNC_FRAME_DROPPED !");
+ break;
+ case HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND:
+ Log.e(TAG, "syncAndDraw(): SYNC_LOST_SURFACE !");
+ break;
+ case HardwareRenderer.SYNC_CONTEXT_IS_STOPPED:
+ Log.e(TAG, "syncAndDraw(): SYNC_CONTEXT_IS_STOPPED !");
+ break;
+ }
+ return NO_FRAME_PRODUCED;
}
public void trimMemory() {
@@ -244,6 +278,7 @@
}
public void destroy() {
+ mSurface = null;
mRenderer.destroy();
}
@@ -254,6 +289,5 @@
mTempMatrix.mapRect(mTempRectF);
mTempRectF.round(outRect);
}
-
}
}
diff --git a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
index 1514b9a..a1d202e 100644
--- a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
+++ b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
@@ -57,10 +57,6 @@
public ScrollResult onScrollRequested(@NonNull ViewGroup view, Rect scrollBounds,
Rect requestRect) {
- final View contentView = view.getChildAt(0); // returns null, does not throw IOOBE
- if (contentView == null) {
- return null;
- }
/*
+---------+ <----+ Content [25,25 - 275,1025] (w=250,h=1000)
| |
@@ -88,9 +84,6 @@
\__ Requested Bounds[0,300 - 200,400] (200x100)
*/
- ScrollResult result = new ScrollResult();
- result.requestedArea = new Rect(requestRect);
-
// 0) adjust the requestRect to account for scroll change since start
//
// Scroll Bounds[50,50 - 250,250] (w=200,h=200)
@@ -99,6 +92,17 @@
// (y-100) (scrollY - mStartScrollY)
int scrollDelta = view.getScrollY() - mStartScrollY;
+ final ScrollResult result = new ScrollResult();
+ result.requestedArea = new Rect(requestRect);
+ result.scrollDelta = scrollDelta;
+ result.availableArea = new Rect();
+
+ final View contentView = view.getChildAt(0); // returns null, does not throw IOOBE
+ if (contentView == null) {
+ // No child view? Cannot continue.
+ return result;
+ }
+
// 1) Translate request rect to make it relative to container view
//
// Container View [0,0 - 300,300] (scrollY=200)
@@ -133,7 +137,7 @@
// TODO: crop capture area to avoid occlusions/minimize scroll changes
Point offset = new Point();
- final Rect available = new Rect(requestedContentBounds); // empty
+ final Rect available = new Rect(requestedContentBounds);
if (!view.getChildVisibleRect(contentView, available, offset)) {
available.setEmpty();
result.availableArea = available;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index adb0fad..4f97975 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -184,6 +184,7 @@
"com_android_internal_os_ClassLoaderFactory.cpp",
"com_android_internal_os_FuseAppLoop.cpp",
"com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
+ "com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp",
"com_android_internal_os_KernelSingleUidTimeReader.cpp",
"com_android_internal_os_Zygote.cpp",
"com_android_internal_os_ZygoteInit.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 5b1196d..27b23bd 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -189,6 +189,7 @@
extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env);
+extern int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv* env);
extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env);
extern int register_com_android_internal_os_Zygote(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
@@ -1581,6 +1582,7 @@
REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
REG_JNI(register_com_android_internal_os_FuseAppLoop),
REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader),
+ REG_JNI(register_com_android_internal_os_KernelSingleProcessCpuThreadReader),
REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader),
};
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index a30c37b..b07c293 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -69,13 +69,19 @@
queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height);
}
+static void nativeFlushShadowQueue(JNIEnv* env, jclass clazz, jlong ptr) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ queue->flushShadowQueue();
+}
+
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{"nativeCreate", "(Ljava/lang/String;JJJZ)J", (void*)nativeCreate},
{"nativeGetSurface", "(J)Landroid/view/Surface;", (void*)nativeGetSurface},
{"nativeDestroy", "(J)V", (void*)nativeDestroy},
{"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
- {"nativeUpdate", "(JJJJ)V", (void*)nativeUpdate}};
+ {"nativeUpdate", "(JJJJ)V", (void*)nativeUpdate},
+ {"nativeFlushShadowQueue", "(J)V", (void*)nativeFlushShadowQueue}};
int register_android_graphics_BLASTBufferQueue(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "android/graphics/BLASTBufferQueue",
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 5c4c509..1ca45fe 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -338,7 +338,7 @@
return (jint)AUDIO_JAVA_BAD_VALUE;
}
const char *address = env->GetStringUTFChars((jstring)addrJobj, NULL);
- AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address);
+ AudioDeviceTypeAddr dev = AudioDeviceTypeAddr((audio_devices_t)typesPtr[i], address);
audioDeviceTypeAddrVector.push_back(dev);
env->ReleaseStringUTFChars((jstring)addrJobj, address);
}
@@ -820,7 +820,8 @@
bool useInMask)
{
nAudioGainConfig->index = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mIndex);
- nAudioGainConfig->mode = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode);
+ nAudioGainConfig->mode =
+ (audio_gain_mode_t)env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode);
ALOGV("convertAudioGainConfigToNative got gain index %d", nAudioGainConfig->index);
jint jMask = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mChannelMask);
audio_channel_mask_t nMask;
@@ -940,8 +941,8 @@
jobject jAudioDevicePort = env->GetObjectField(jAudioPortConfig,
gAudioPortConfigFields.mPort);
- nAudioPortConfig->ext.device.type = env->GetIntField(jAudioDevicePort,
- gAudioPortFields.mType);
+ nAudioPortConfig->ext.device.type =
+ (audio_devices_t)env->GetIntField(jAudioDevicePort, gAudioPortFields.mType);
jstring jDeviceAddress = (jstring)env->GetObjectField(jAudioDevicePort,
gAudioPortFields.mAddress);
const char *nDeviceAddress = env->GetStringUTFChars(jDeviceAddress, NULL);
@@ -2334,7 +2335,7 @@
static jint
android_media_AudioSystem_setAllowedCapturePolicy(JNIEnv *env, jobject thiz, jint uid, jint flags) {
- return AudioSystem::setAllowedCapturePolicy(uid, flags);
+ return AudioSystem::setAllowedCapturePolicy(uid, static_cast<audio_flags_mask_t>(flags));
}
static jint
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 3acd15a..e7e9c31 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -60,7 +60,7 @@
sp<MessageQueue> mMessageQueue;
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
- int64_t sharedTimelineFrameCount) override;
+ VsyncEventData vsyncEventData) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
int32_t configId, nsecs_t vsyncPeriod) override;
@@ -91,14 +91,15 @@
}
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
- uint32_t count, int64_t frameTimelineVsyncId) {
+ uint32_t count, VsyncEventData vsyncEventData) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
if (receiverObj.get()) {
ALOGV("receiver %p ~ Invoking vsync handler.", this);
env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
- timestamp, displayId.value, count, frameTimelineVsyncId);
+ timestamp, displayId.value, count, vsyncEventData.id,
+ vsyncEventData.deadlineTimestamp);
ALOGV("receiver %p ~ Returned from vsync handler.", this);
}
@@ -198,7 +199,8 @@
gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
gDisplayEventReceiverClassInfo.dispatchVsync =
- GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJIJ)V");
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync",
+ "(JJIJJ)V");
gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env,
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 9ed71ac0..1ea918a 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -343,7 +343,7 @@
jboolean(focusEvent->getHasFocus()),
jboolean(focusEvent->getInTouchMode()));
finishInputEvent(seq, true /* handled */);
- return OK;
+ continue;
}
default:
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 1419855..a61903d 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1360,15 +1360,6 @@
transaction->detachChildren(ctrl);
}
-static void nativeSetOverrideScalingMode(JNIEnv* env, jclass clazz, jlong transactionObj,
- jlong nativeObject,
- jint scalingMode) {
- auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
-
- auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- transaction->setOverrideScalingMode(ctrl, scalingMode);
-}
-
static jobject nativeGetHdrCapabilities(JNIEnv* env, jclass clazz, jobject tokenObject) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
if (token == NULL) return NULL;
@@ -1694,8 +1685,6 @@
(void*)nativeReparent },
{"nativeSeverChildren", "(JJ)V",
(void*)nativeSeverChildren } ,
- {"nativeSetOverrideScalingMode", "(JJI)V",
- (void*)nativeSetOverrideScalingMode },
{"nativeCaptureDisplay",
"(Landroid/view/SurfaceControl$DisplayCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I",
(void*)nativeCaptureDisplay },
diff --git a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
new file mode 100644
index 0000000..52bed6b
--- /dev/null
+++ b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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.
+ */
+
+#include "core_jni_helpers.h"
+
+#include <cputimeinstate.h>
+#include <dirent.h>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android_runtime/Log.h>
+
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+namespace android {
+
+// Number of milliseconds in a jiffy - the unit of time measurement for processes and threads
+static const uint32_t gJiffyMillis = (uint32_t)(1000 / sysconf(_SC_CLK_TCK));
+
+// Given a PID, returns a vector of all TIDs for the process' tasks. Thread IDs are
+// file names in the /proc/<pid>/task directory.
+static bool getThreadIds(const std::string &procPath, const pid_t pid,
+ std::vector<pid_t> &outThreadIds) {
+ std::string taskPath = android::base::StringPrintf("%s/%u/task", procPath.c_str(), pid);
+
+ struct dirent **dirlist;
+ int threadCount = scandir(taskPath.c_str(), &dirlist, NULL, NULL);
+ if (threadCount == -1) {
+ ALOGE("Cannot read directory %s", taskPath.c_str());
+ return false;
+ }
+
+ outThreadIds.reserve(threadCount);
+
+ for (int i = 0; i < threadCount; i++) {
+ pid_t tid;
+ if (android::base::ParseInt<pid_t>(dirlist[i]->d_name, &tid)) {
+ outThreadIds.push_back(tid);
+ }
+ free(dirlist[i]);
+ }
+ free(dirlist);
+
+ return true;
+}
+
+// Reads contents of a time_in_state file and returns times as a vector of times per frequency
+// A time_in_state file contains pairs of frequency - time (in jiffies):
+//
+// cpu0
+// 300000 30
+// 403200 0
+// cpu4
+// 710400 10
+// 825600 20
+// 940800 30
+//
+static bool getThreadTimeInState(const std::string &procPath, const pid_t pid, const pid_t tid,
+ const size_t frequencyCount,
+ std::vector<uint64_t> &outThreadTimeInState) {
+ std::string timeInStateFilePath =
+ android::base::StringPrintf("%s/%u/task/%u/time_in_state", procPath.c_str(), pid, tid);
+ std::string data;
+
+ if (!android::base::ReadFileToString(timeInStateFilePath, &data)) {
+ ALOGE("Cannot read file: %s", timeInStateFilePath.c_str());
+ return false;
+ }
+
+ auto lines = android::base::Split(data, "\n");
+ size_t index = 0;
+ for (const auto &line : lines) {
+ if (line.empty()) {
+ continue;
+ }
+
+ auto numbers = android::base::Split(line, " ");
+ if (numbers.size() != 2) {
+ continue;
+ }
+ uint64_t timeInState;
+ if (!android::base::ParseUint<uint64_t>(numbers[1], &timeInState)) {
+ ALOGE("Invalid time_in_state file format: %s", timeInStateFilePath.c_str());
+ return false;
+ }
+ if (index < frequencyCount) {
+ outThreadTimeInState[index] = timeInState;
+ }
+ index++;
+ }
+
+ if (index != frequencyCount) {
+ ALOGE("Incorrect number of frequencies %u in %s. Expected %u",
+ (uint32_t)outThreadTimeInState.size(), timeInStateFilePath.c_str(),
+ (uint32_t)frequencyCount);
+ return false;
+ }
+
+ return true;
+}
+
+static int pidCompare(const void *a, const void *b) {
+ return (*(pid_t *)a - *(pid_t *)b);
+}
+
+static inline bool isSelectedThread(const pid_t tid, const pid_t *selectedThreadIds,
+ const size_t selectedThreadCount) {
+ return bsearch(&tid, selectedThreadIds, selectedThreadCount, sizeof(pid_t), pidCompare) != NULL;
+}
+
+// Reads all /proc/<pid>/task/*/time_in_state files and aggregates per-frequency
+// time in state data for all threads. Also, separately aggregates time in state for
+// selected threads whose TIDs are passes as selectedThreadIds.
+static void aggregateThreadCpuTimes(const std::string &procPath, const pid_t pid,
+ const std::vector<pid_t> &threadIds,
+ const size_t frequencyCount, const pid_t *selectedThreadIds,
+ const size_t selectedThreadCount,
+ uint64_t *threadCpuTimesMillis,
+ uint64_t *selectedThreadCpuTimesMillis) {
+ for (size_t j = 0; j < frequencyCount; j++) {
+ threadCpuTimesMillis[j] = 0;
+ selectedThreadCpuTimesMillis[j] = 0;
+ }
+
+ for (size_t i = 0; i < threadIds.size(); i++) {
+ pid_t tid = threadIds[i];
+ std::vector<uint64_t> timeInState(frequencyCount);
+ if (!getThreadTimeInState(procPath, pid, tid, frequencyCount, timeInState)) {
+ continue;
+ }
+
+ bool selectedThread = isSelectedThread(tid, selectedThreadIds, selectedThreadCount);
+ for (size_t j = 0; j < frequencyCount; j++) {
+ threadCpuTimesMillis[j] += timeInState[j];
+ if (selectedThread) {
+ selectedThreadCpuTimesMillis[j] += timeInState[j];
+ }
+ }
+ }
+ for (size_t i = 0; i < frequencyCount; i++) {
+ threadCpuTimesMillis[i] *= gJiffyMillis;
+ selectedThreadCpuTimesMillis[i] *= gJiffyMillis;
+ }
+}
+
+// Reads process utime and stime from the /proc/<pid>/stat file.
+// Format of this file is described in https://man7.org/linux/man-pages/man5/proc.5.html.
+static bool getProcessCpuTime(const std::string &procPath, const pid_t pid,
+ uint64_t &outTimeMillis) {
+ std::string statFilePath = android::base::StringPrintf("%s/%u/stat", procPath.c_str(), pid);
+ std::string data;
+ if (!android::base::ReadFileToString(statFilePath, &data)) {
+ return false;
+ }
+
+ auto fields = android::base::Split(data, " ");
+ uint64_t utime, stime;
+
+ // Field 14 (counting from 1) is utime - process time in user space, in jiffies
+ // Field 15 (counting from 1) is stime - process time in system space, in jiffies
+ if (fields.size() < 15 || !android::base::ParseUint(fields[13], &utime) ||
+ !android::base::ParseUint(fields[14], &stime)) {
+ ALOGE("Invalid file format %s", statFilePath.c_str());
+ return false;
+ }
+
+ outTimeMillis = (utime + stime) * gJiffyMillis;
+ return true;
+}
+
+// Estimates per cluster per frequency CPU time for the entire process
+// by distributing the total process CPU time proportionately to how much
+// CPU time its threads took on those clusters/frequencies. This algorithm
+// works more accurately when when we have equally distributed concurrency.
+// TODO(b/169279846): obtain actual process CPU times from the kernel
+static void estimateProcessTimeInState(const uint64_t processCpuTimeMillis,
+ const uint64_t *threadCpuTimesMillis,
+ const size_t frequencyCount,
+ uint64_t *processCpuTimesMillis) {
+ uint64_t totalCpuTimeAllThreads = 0;
+ for (size_t i = 0; i < frequencyCount; i++) {
+ totalCpuTimeAllThreads += threadCpuTimesMillis[i];
+ }
+
+ if (totalCpuTimeAllThreads != 0) {
+ for (size_t i = 0; i < frequencyCount; i++) {
+ processCpuTimesMillis[i] =
+ processCpuTimeMillis * threadCpuTimesMillis[i] / totalCpuTimeAllThreads;
+ }
+ } else {
+ for (size_t i = 0; i < frequencyCount; i++) {
+ processCpuTimesMillis[i] = 0;
+ }
+ }
+}
+
+static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jstring procPath, jint pid,
+ jintArray selectedThreadIdArray,
+ jlongArray processCpuTimesMillisArray,
+ jlongArray threadCpuTimesMillisArray,
+ jlongArray selectedThreadCpuTimesMillisArray) {
+ ScopedUtfChars procPathChars(env, procPath);
+ ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray);
+ ScopedLongArrayRW processCpuTimesMillis(env, processCpuTimesMillisArray);
+ ScopedLongArrayRW threadCpuTimesMillis(env, threadCpuTimesMillisArray);
+ ScopedLongArrayRW selectedThreadCpuTimesMillis(env, selectedThreadCpuTimesMillisArray);
+
+ std::string procPathStr(procPathChars.c_str());
+
+ // Get all thread IDs for the process.
+ std::vector<pid_t> threadIds;
+ if (!getThreadIds(procPathStr, pid, threadIds)) {
+ ALOGE("Could not obtain thread IDs from: %s", procPathStr.c_str());
+ return false;
+ }
+
+ size_t frequencyCount = processCpuTimesMillis.size();
+
+ if (threadCpuTimesMillis.size() != frequencyCount) {
+ ALOGE("Invalid array length: threadCpuTimesMillis");
+ return false;
+ }
+ if (selectedThreadCpuTimesMillis.size() != frequencyCount) {
+ ALOGE("Invalid array length: selectedThreadCpuTimesMillisArray");
+ return false;
+ }
+
+ aggregateThreadCpuTimes(procPathStr, pid, threadIds, frequencyCount, selectedThreadIds.get(),
+ selectedThreadIds.size(),
+ reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()),
+ reinterpret_cast<uint64_t *>(selectedThreadCpuTimesMillis.get()));
+
+ uint64_t processCpuTime;
+ bool ret = getProcessCpuTime(procPathStr, pid, processCpuTime);
+ if (ret) {
+ estimateProcessTimeInState(processCpuTime,
+ reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()),
+ frequencyCount,
+ reinterpret_cast<uint64_t *>(processCpuTimesMillis.get()));
+ }
+ return ret;
+}
+
+static const JNINativeMethod g_single_methods[] = {
+ {"readProcessCpuUsage", "(Ljava/lang/String;I[I[J[J[J)Z", (void *)readProcessCpuUsage},
+};
+
+int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv *env) {
+ return RegisterMethodsOrDie(env, "com/android/internal/os/KernelSingleProcessCpuThreadReader",
+ g_single_methods, NELEM(g_single_methods));
+}
+
+} // namespace android
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 42aab6a..f791cb1 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -80,6 +80,7 @@
#include <bionic/mte.h>
#include <bionic/mte_kernel.h>
#include <cutils/fs.h>
+#include <cutils/memory.h>
#include <cutils/multiuser.h>
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
@@ -630,6 +631,13 @@
// Set the jemalloc decay time to 1.
mallopt(M_DECAY_TIME, 1);
+
+ // Avoid potentially expensive memory mitigations, mostly meant for system
+ // processes, in apps. These may cause app compat problems, use more memory,
+ // or reduce performance. While it would be nice to have them for apps,
+ // we will have to wait until they are proven out, have more efficient
+ // hardware, and/or apply them only to new applications.
+ process_disable_memory_mitigations();
}
static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 7fac615..99fe1af 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2751,4 +2751,14 @@
// OS: R QPR2
BLUETOOTH_PAIRING_RECEIVER = 1851;
+
+ // OPEN: Settings > Display > Screen timeout
+ // CATEGORY: SETTINGS
+ // OS: S
+ SCREEN_TIMEOUT = 1852;
+
+ // OPEN: Settings > Accessibility > Reduce Bright Colors
+ // CATEGORY: SETTINGS
+ // OS: S
+ REDUCE_BRIGHT_COLORS_SETTINGS = 1853;
}
diff --git a/core/proto/android/server/vibratorservice.proto b/core/proto/android/server/vibratorservice.proto
index 281a25e..9e42e9e 100644
--- a/core/proto/android/server/vibratorservice.proto
+++ b/core/proto/android/server/vibratorservice.proto
@@ -21,6 +21,12 @@
import "frameworks/base/core/proto/android/privacy.proto";
+message OneShotProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ repeated int32 duration = 1;
+ repeated int32 amplitude = 2;
+}
+
message WaveformProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
repeated int32 timings = 1;
@@ -35,20 +41,41 @@
optional int32 fallback = 3;
}
+message ComposedProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ repeated int32 effect_ids = 1;
+ repeated float effect_scales = 2;
+ repeated int32 delays = 3;
+}
+
// A com.android.os.VibrationEffect object.
message VibrationEffectProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional OneShotProto oneshot = 3;
optional WaveformProto waveform = 1;
optional PrebakedProto prebaked = 2;
+ optional ComposedProto composed = 4;
}
+message VibrationAttributesProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional int32 usage = 1;
+ optional int32 audio_usage = 2;
+ optional int32 flags = 3;
+}
+
+// Next id: 7
message VibrationProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int64 start_time = 1;
+ optional int64 end_time = 4;
optional VibrationEffectProto effect = 2;
- optional VibrationEffectProto origin_effect = 3;
+ optional VibrationEffectProto original_effect = 3;
+ optional VibrationAttributesProto attributes = 5;
+ optional int32 status = 6;
}
+// Next id: 17
message VibratorServiceDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional VibrationProto current_vibration = 1;
@@ -57,10 +84,14 @@
optional bool vibrator_under_external_control = 4;
optional bool low_power_mode = 5;
optional int32 haptic_feedback_intensity = 6;
+ optional int32 haptic_feedback_default_intensity = 14;
optional int32 notification_intensity = 7;
+ optional int32 notification_default_intensity = 15;
optional int32 ring_intensity = 8;
+ optional int32 ring_default_intensity = 16;
repeated VibrationProto previous_ring_vibrations = 9;
repeated VibrationProto previous_notification_vibrations = 10;
repeated VibrationProto previous_alarm_vibrations = 11;
repeated VibrationProto previous_vibrations = 12;
+ repeated VibrationProto previous_external_vibrations = 13;
}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2ac2a1f..0a1b8e0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4518,6 +4518,12 @@
<permission android:name="android.permission.MANAGE_NOTIFICATIONS"
android:protectionLevel="signature" />
+ <!-- @SystemApi @TestApi Allows adding/removing enabled notification listener components.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS"
+ android:protectionLevel="signature|installer" />
+ <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
+
<!-- Allows notifications to be colorized
<p>Not for use by third-party applications. @hide -->
<permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS"
@@ -4541,6 +4547,12 @@
<permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
android:protectionLevel="signature" />
+ <!-- Allows access to TestApis for various components in the biometric stack, including
+ FingerprintService, FaceService, BiometricService. Used by com.android.server.biometrics
+ CTS tests. @hide @TestApi -->
+ <permission android:name="android.permission.TEST_BIOMETRIC"
+ android:protectionLevel="signature" />
+
<!-- Allows direct access to the <Biometric>Service interfaces. Reserved for the system. @hide -->
<permission android:name="android.permission.MANAGE_BIOMETRIC"
android:protectionLevel="signature" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index bd81519..20ebbd8 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gebruik kortpad"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Kleuromkering"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Kleurkorreksie"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aangeskakel."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is afgeskakel"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Druk en hou albei volumesleutels drie sekondes lank om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> te gebruik"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 0bbe232..d534758 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"አቋራጭ ይጠቀሙ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ተቃራኒ ቀለም"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"የቀለም ማስተካከያ"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 8868fb6..86ed737 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1742,6 +1742,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استخدام الاختصار"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"قلب الألوان"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"تصحيح الألوان"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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">"اضغط مع الاستمرار على مفتاحي مستوى الصوت لمدة 3 ثوانٍ لاستخدام <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 619666b..da99576 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"শ্বৰ্টকাট ব্যৱহাৰ কৰক"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ৰং বিপৰীতকৰণ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ৰং শুধৰণী"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index ed708cc..bc3f146 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Qısayol İstifadə edin"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Rəng İnversiyası"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Rəng korreksiyası"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Səs səviyyəsi düymələrinə basıb saxlayın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktiv edildi."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Səs səviyyəsi düymələrinə basılaraq saxlanıb. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> deaktiv edilib."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> istifadə etmək üçün hər iki səs düyməsini üç saniyə basıb saxlayın"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 0b21545..f6dfe6e 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1676,6 +1676,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Koristi prečicu"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcija boja"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite i zadržite oba tastera za jačinu zvuka tri sekunde da biste koristili <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 6a0ad98..f761cba 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1698,6 +1698,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Выкарыстоўваць камбінацыю хуткага доступу"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія колеру"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Карэкцыя колеру"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index cdd0ec0..e944cd3 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Използване на пряк път"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инвертиране на цветовете"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Коригиране на цветовете"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 779fd3e..ce0838c 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"শর্টকাট ব্যবহার করুন"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"রঙ উল্টানো"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"রঙ সংশোধন"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index fab6c91..95e6882 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1676,6 +1676,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Koristi prečicu"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Ispravka boja"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite obje tipke za podešavanje jačine zvuka i držite ih pritisnutim tri sekunde da koristite uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 96c6b6a..d115bde 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilitza la drecera"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversió de colors"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correcció de color"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S\'han mantingut premudes les tecles de volum. S\'ha activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S\'han mantingut premudes les tecles de volum. S\'ha desactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén premudes les dues tecles de volum durant 3 segons per fer servir <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 9a5f069..60fe94c 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1698,6 +1698,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Použít zkratku"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Převrácení barev"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Oprava barev"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> byla vypnuta."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Chcete-li používat službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, tři sekundy podržte stisknutá obě tlačítka hlasitosti"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 480e87c..a8641d3 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Brug genvej"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ombytning af farver"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korriger farve"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er aktiveret."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er deaktiveret."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Hold begge lydstyrkeknapper nede i tre sekunder for at bruge <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ff8e6ad..68749c6 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Verknüpfung verwenden"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Farbumkehr"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Farbkorrektur"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist aktiviert."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist deaktiviert."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Halten Sie beide Lautstärketasten drei Sekunden lang gedrückt, um <xliff:g id="SERVICE_NAME">%1$s</xliff:g> zu verwenden"</string>
@@ -1796,8 +1798,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Von deinem Administrator aktualisiert"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Von deinem Administrator gelöscht"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
- <string name="battery_saver_description" msgid="6794188153647295212">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Der Datensparmodus verhindert zum einen, dass manche Apps im Hintergrund Daten senden oder empfangen, sodass weniger Daten verbraucht werden. Zum anderen werden die Datenzugriffe der gerade aktiven App eingeschränkt, was z. B. dazu führen kann, dass Bilder erst angetippt werden müssen, bevor sie sichtbar werden."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Datensparmodus aktivieren?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivieren"</string>
@@ -2005,9 +2007,9 @@
<string name="notification_feedback_indicator" msgid="663476517711323016">"Feedback geben"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Infomitteilung zum Ablaufmodus"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Dein Akku könnte vor der gewöhnlichen Ladezeit leer sein"</string>
- <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Stromsparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
- <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Stromsparmodus"</string>
- <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Stromsparmodus deaktiviert"</string>
+ <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Energiesparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
+ <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Energiesparmodus"</string>
+ <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Energiesparmodus deaktiviert"</string>
<string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Das Smartphone ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
<string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Das Tablet ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
<string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Das Gerät ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 6f03f80..88edddf 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Χρήση συντόμευσης"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Αντιστροφή χρωμάτων"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Διόρθωση χρωμάτων"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index fac2f22..869a3bd 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 8497658..7738afb 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 12881e7..51b3a2c 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index cb2eb7a..0476351 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Color correction"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 5750e50..9c3bd6e 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Color Inversion"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Color Correction"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 8e7d1a33..85e5b96 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar acceso directo"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de color"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de color"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Como mantuviste presionadas las teclas de volumen, se activó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se presionaron las teclas de volumen. Se desactivó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén presionadas ambas teclas de volumen durante tres segundos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index ffc5ff7..39e04bd 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -327,7 +327,7 @@
<string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controla el posicionamiento y el nivel de zoom de la pantalla."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Realizar gestos"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Puedes tocar y pellizcar la pantalla, deslizar el dedo y hacer otros gestos."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestos de huellas digitales"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestos de huella digital"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Puede capturar los gestos realizados en el sensor de huellas digitales del dispositivo."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Hacer captura"</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Puede hacer capturas de la pantalla."</string>
@@ -1654,6 +1654,8 @@
<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>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de color"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Al mantener pulsadas las teclas de volumen, se ha activado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se han mantenido pulsadas las teclas de volumen. Se ha desactivado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Para utilizar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, mantén pulsadas ambas teclas de volumen durante 3 segundos"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index d825429..7883db3 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kasuta otseteed"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Värvide ümberpööramine"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Värvide korrigeerimine"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati sisse."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati välja."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kasutamiseks hoidke kolm sekundit all mõlemat helitugevuse klahvi"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 05d4050..4b4414b 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Erabili lasterbidea"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Koloreen alderantzikatzea"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Koloreen zuzenketa"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktibatu egin da."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desaktibatu egin da."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> erabiltzeko, eduki sakatuta bi bolumen-botoiak hiru segundoz"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index a99d4d6..4cba3c9 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1654,15 +1654,17 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استفاده از میانبر"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"وارونگی رنگ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"تصحیح رنگ"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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_gesture_prompt_text" msgid="8742535972130563952">"ویژگی را برای استفاده با اشاره دسترسپذیری انتخاب کنید (با دو انگشت صفحه را از پایین تند به بالا بکشید):"</string>
- <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ویزگی را برای استفاده با اشاره دسترسپذیری انتخاب کنید (با سه انگشت صفحه را از پایین تند به بالا بکشید):"</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_gesture_instructional_text" msgid="9196230728837090497">"برای جابهجایی بین ویژگیها، با دو انگشت صفحه را تند به بالا بکشید و نگه دارید."</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"برای جابهجایی بین ویژگیها، با سه انگشت صفحه را تند به بالا بکشید و نگه دارید."</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="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>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index e7bcf48..dc40a02 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Käytä pikanäppäintä"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Käänteiset värit"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Värinkorjaus"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin päälle."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin pois päältä."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Voit käyttää palvelua <xliff:g id="SERVICE_NAME">%1$s</xliff:g> painamalla molempia äänenvoimakkuuspainikkeita kolmen sekunnin ajan"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index aad5749..582c966 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utiliser le raccourci"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversion des couleurs"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correction des couleurs"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume maintenues enfoncées. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume maintenues enfoncées. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Maintenez enfoncées les deux touches de volume pendant trois secondes pour utiliser <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 59d4d77..167249f 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -327,7 +327,7 @@
<string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Contrôle le niveau de zoom et le positionnement de l\'écran."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Effectuer des gestes"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Permet d\'appuyer sur l\'écran, de le balayer, de le pincer et d\'effectuer d\'autres gestes."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestes avec l\'empreinte digitale"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestes d\'empreinte digitale"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Peut enregistrer des gestes effectués sur le lecteur d\'empreinte digitale de l\'appareil."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Prendre une capture d\'écran"</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Peut prendre des captures d\'écran."</string>
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utiliser le raccourci"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversion des couleurs"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correction des couleurs"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Appuyez de manière prolongée sur les deux touches de volume pendant trois secondes pour utiliser <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 79380d5..e78a0c8 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar atallo"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de cor"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de cor"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume premidas. Activouse o servizo <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Desactivouse <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén premidas as teclas do volume durante tres segudos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index bf4d6c0..e38096c 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"શૉર્ટકટનો ઉપયોગ કરો"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"વિપરીત રંગમાં બદલવું"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"રંગ સુધારણા"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index b867e9d..59c2b2d 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"शॉर्टकट का उपयोग करें"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"रंग बदलने की सुविधा"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"रंग में सुधार करने की सुविधा"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index dd2bdbe..c3edbf5 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1676,6 +1676,8 @@
<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>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite i zadržite tipke za glasnoću na tri sekunde da biste koristili uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 9d1c2f4..47cf41b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Billentyűparancs használata"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Színek invertálása"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Színkorrekció"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> bekapcsolva."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kikapcsolva."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"A(z) <xliff:g id="SERVICE_NAME">%1$s</xliff:g> használatához tartsa lenyomva három másodpercig a két hangerőgombot"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 4e2bbe2..ad6df06 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Օգտագործել դյուրանցումը"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Գունաշրջում"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Գունաշտկում"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 202664f..8a09de4 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gunakan Pintasan"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversi Warna"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Koreksi Warna"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> diaktifkan."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dinonaktifkan."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tekan dan tahan kedua tombol volume selama tiga detik untuk menggunakan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index efb23d4..2c7ee6b 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Nota flýtileið"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Umsnúningur lita"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Litaleiðrétting"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Hljóðstyrkstökkum haldið inni. Kveikt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Hljóðstyrkstökkum haldið inni. Slökkt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Haltu báðum hljóðstyrkstökkunum inni í þrjár sekúndur til að nota <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index ec446d7..e32609a 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usa scorciatoia"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversione dei colori"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correzione del colore"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> attivato."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> disattivato."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tieni premuti entrambi i tasti del volume per tre secondi per utilizzare <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 874d577..2edfa4d 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1698,6 +1698,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"השתמש בקיצור הדרך"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"היפוך צבעים"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"תיקון צבעים"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 32a4ded..c9fc1c2 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ショートカットを使用"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"色反転"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"色補正"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が ON になりました。"</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が OFF になりました。"</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> を使用するには、音量大と音量小の両方のボタンを 3 秒間長押ししてください"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 349bd16..1778f5e 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"მალსახმობის გამოყენება"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ფერთა ინვერსია"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ფერთა კორექცია"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 4d4eef0..d80572f 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Төте жолды пайдалану"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Түстер инверсиясы"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Түсті түзету"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 3ebd736..2fd7257 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ប្រើប្រាស់ផ្លូវកាត់"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"បញ្ច្រាសពណ៌"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ការកែពណ៌"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 0cf6f49..20b2ccf 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ಶಾರ್ಟ್ಕಟ್ ಬಳಸಿ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ಬಣ್ಣ ವಿಲೋಮ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 3dfdf3d..79569e2 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"단축키 사용"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"색상 반전"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"색상 보정"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 7057b844..4fee44a 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1206,7 +1206,7 @@
<string name="android_upgrading_apk" msgid="1339564803894466737">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ичинен <xliff:g id="NUMBER_0">%1$d</xliff:g> колдонмо ыңгайлаштырылууда."</string>
<string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> даярдалууда."</string>
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Колдонмолорду иштетип баштоо"</string>
- <string name="android_upgrading_complete" msgid="409800058018374746">"Жүктөө аякталууда."</string>
+ <string name="android_upgrading_complete" msgid="409800058018374746">"Жүктөлүүдө"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> иштеп жатат"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Оюнга кайтуу үчүн таптаңыз"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Оюн тандоо"</string>
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Кыска жолду колдонуу"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Түстү инверсиялоо"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Түсүн тууралоо"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 315ee32..b0e14a8 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ໃຊ້ປຸ່ມລັດ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ການປີ້ນສີ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ການແກ້ໄຂຄ່າສີ"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 1d6b2c7..cedde11 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1698,6 +1698,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Naudoti spartųjį klavišą"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Spalvų inversija"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Spalvų taisymas"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ įjungta."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ išjungta."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Jei norite naudoti „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“, paspauskite abu garsumo klavišus ir palaikykite tris sekundes"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index ba790a0..e34bb95 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1676,6 +1676,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Izmantot saīsni"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Krāsu inversija"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Krāsu korekcija"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika ieslēgts."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika izslēgts."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Lai izmantotu pakalpojumu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, nospiediet abus skaļuma taustiņus un turiet tos trīs sekundes."</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index c882cb1..c061283 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи кратенка"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија на бои"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Корекција на бои"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index fcf64c1..18dbf0e 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -327,7 +327,7 @@
<string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"ഡിസ്പ്ലേയുടെ സൂം നിലയും പൊസിഷനിംഗും നിയന്ത്രിക്കുക."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"ജെസ്റ്ററുകൾ നിർവഹിക്കുക"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"ടാപ്പുചെയ്യാനോ സ്വൈപ്പുചെയ്യാനോ പിഞ്ചുചെയ്യാനോ മറ്റ് ജെസ്റ്ററുകൾ നിർവഹിക്കാനോ കഴിയും."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"ഫിംഗർപ്രിന്റ് ജെസ്റ്ററുകൾ"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"ഫിംഗർപ്രിന്റ് ജെസ്ച്ചറുകൾ"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ഉപകരണത്തിന്റെ ഫിംഗർപ്രിന്റ് സെൻസറിൽ ചെയ്ത ജെസ്റ്ററുകൾ ക്യാപ്ചർ ചെയ്യാനാകും."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"സ്ക്രീന്ഷോട്ട് എടുക്കുക"</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ഡിസ്പ്ലേയുടെ സ്ക്രീൻഷോട്ട് എടുക്കാൻ കഴിയും."</string>
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"കുറുക്കുവഴി ഉപയോഗിക്കുക"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"വർണ്ണ വിപര്യയം"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"നിറം ക്രമീകരിക്കൽ"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 1418248..a7a08ce 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -957,18 +957,18 @@
<string name="autofill_parish" msgid="6847960518334530198">"Мөргөлч"</string>
<string name="autofill_area" msgid="8289022370678448983">"Хэсэг"</string>
<string name="autofill_emirate" msgid="2544082046790551168">"Эмират"</string>
- <string name="permlab_readHistoryBookmarks" msgid="9102293913842539697">"өөрийн Вэб хавчуурга болон түүхийг унших"</string>
+ <string name="permlab_readHistoryBookmarks" msgid="9102293913842539697">"өөрийн Веб хавчуурга болон түүхийг унших"</string>
<string name="permdesc_readHistoryBookmarks" msgid="2323799501008967852">"Апп нь Хөтчийн зочилж байсан бүх URL-н түүх болон Хөтчийн бүх хавчуургыг унших боломжтой. Анхаар: Энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вебээр хөтөчлөх чадавхтай аппликейшнүүдэд ашиглагдахгүй байх боломжтой."</string>
- <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"вэб хавчуурга болон түүхийг бичих"</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"веб хавчуурга болон түүхийг бичих"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="573341025292489065">"Апп нь таны таблет дээр хадгалагдсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөх боломжтой. Энэ нь апп-д Хөтчийн датаг арилгах эсвэл өөрчлөх боломжийг олгоно. Анхаар: Энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вебээр хөтөчлөх чадвартай аппликейшнд ажиллахгүй байх боломжтой."</string>
- <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"Аппад таны Android TV төхөөрөмжид хадгалсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөхийг зөвшөөрнө. Энэ нь аппад Хөтчийн өгөгдлийг устгах эсвэл өөрчлөхийг зөвшөөрч болзошгүй. Санамж: энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вэб хөтчийн чадамжтай бусад аппад хэрэгжихгүй байж болзошгүй."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"Аппад таны Android TV төхөөрөмжид хадгалсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөхийг зөвшөөрнө. Энэ нь аппад Хөтчийн өгөгдлийг устгах эсвэл өөрчлөхийг зөвшөөрч болзошгүй. Санамж: энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл веб хөтчийн чадамжтай бусад аппад хэрэгжихгүй байж болзошгүй."</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"Апп нь таны утсан дээр хадгалагдсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөх боломжтой. Энэ нь апп-д Хөтчийн датаг арилгах эсвэл өөрчлөх боломжийг олгоно. Анхаар: Энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вебээр хөтөчлөх чадвартай аппликейшнд ажиллахгүй байх боломжтой."</string>
<string name="permlab_setAlarm" msgid="1158001610254173567">"сэрүүлэг тохируулах"</string>
<string name="permdesc_setAlarm" msgid="2185033720060109640">"Апп нь суулгагдсан сэрүүлэгний апп дээр сэрүүлэг тохируулах боломжтой. Зарим сэрүүлэгний апп нь энэ функцийг дэмжихгүй байж болзошгүй."</string>
<string name="permlab_addVoicemail" msgid="4770245808840814471">"дуут шуудан нэмэх"</string>
<string name="permdesc_addVoicemail" msgid="5470312139820074324">"Таны дуут шуудангийн ирсэн мэйлд зурвас нэмэхийг апп-д зөвшөөрөх."</string>
<string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"Хөтчийн геобайршлын зөвшөөрлийг өөрчлөх"</string>
- <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"Апп нь Хөтчийн гео байршлын зөвшөөрлийг өөрчлөх боломжтой. Хортой апп нь энийг ашиглан дурын вэб хуудасруу байршлын мэдээллийг илгээх боломжтой."</string>
+ <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"Апп нь Хөтчийн гео байршлын зөвшөөрлийг өөрчлөх боломжтой. Хортой апп нь энийг ашиглан дурын веб хуудасруу байршлын мэдээллийг илгээх боломжтой."</string>
<string name="save_password_message" msgid="2146409467245462965">"Та хөтчид энэ нууц үгийг сануулах уу?"</string>
<string name="save_password_notnow" msgid="2878327088951240061">"Одоо биш"</string>
<string name="save_password_remember" msgid="6490888932657708341">"Санах"</string>
@@ -1459,7 +1459,7 @@
<string name="progress_erasing" msgid="6891435992721028004">"Хуваалцсан хадгалах санг устгаж байна…"</string>
<string name="share" msgid="4157615043345227321">"Хуваалцах"</string>
<string name="find" msgid="5015737188624767706">"Олох"</string>
- <string name="websearch" msgid="5624340204512793290">"Вэб хайлт"</string>
+ <string name="websearch" msgid="5624340204512793290">"Веб хайлт"</string>
<string name="find_next" msgid="5341217051549648153">"Дараагийнхыг хайх"</string>
<string name="find_previous" msgid="4405898398141275532">"Өмнөхөөс олох"</string>
<string name="gpsNotifTicker" msgid="3207361857637620780">"<xliff:g id="NAME">%s</xliff:g>-н байршлын хүсэлт"</string>
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Товчлол ашиглах"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Өнгө хувиргалт"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Өнгөний засвар"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 3d2fbcb..b05a5a4 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"शॉर्टकट वापरा"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"रंगांची उलटापालट"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"रंग सुधारणा"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 5c35ba1..d0b9f3a 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gunakan Pintasan"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Penyongsangan Warna"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Pembetulan Warna"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dihidupkan."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dimatikan."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tekan dan tahan kedua-dua kekunci kelantangan selama tiga saat untuk menggunakan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 38f1aa8..0729f53 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ဖြတ်လမ်းလင့်ခ်ကို သုံးရန်"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"အရောင် ပြောင်းပြန်လှန်ခြင်း"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"အရောင်ပြင်ဆင်ခြင်း"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 5b0a4ad..1839426 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Bruk snarveien"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Fargeinvertering"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Fargekorrigering"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått på."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått av."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Trykk og hold inne begge volumtastene i tre sekunder for å bruke <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 01ee79a9..b75a05d 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -78,18 +78,18 @@
<string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवाको व्यवस्था छैन।"</string>
<string name="CLIRPermanent" msgid="166443681876381118">"तपाईं कलर ID सेटिङ परिवर्तन गर्न सक्नुहुन्न।"</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"कुनै पनि मोबाइल डेटा सेवा उपलब्ध छैन"</string>
- <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आपतकालीन कल सेवा उपलब्ध छैन"</string>
+ <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आपत्कालीन कल सेवा उपलब्ध छैन"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"कुनै पनि भ्वाइस सेवा उपलब्ध छैन"</string>
<string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"कुनै पनि भ्वाइस वा आपातकालीन कल सेवा उपलब्ध छैन"</string>
<string name="RestrictedStateContent" msgid="7693575344608618926">"तपाईंको सेवा प्रदायकले अस्थायी रूपमा निष्क्रिय पार्नुभएको"</string>
<string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> का लागि तपाईंको सेवा प्रदायकले अस्थायी रूपमा निष्क्रिय पार्नुभएको"</string>
<string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"मोबाइल नेटवर्कमाथि पहुँच राख्न सकिएन"</string>
<string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"रुचाइएको नेटवर्क परिवर्तन गरी हेर्नुहोस्। परिवर्तन गर्न ट्याप गर्नुहोस्।"</string>
- <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"आपतकालीन कल सेवा अनुपलब्ध छ"</string>
- <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Wi‑Fi मार्फत आपतकालीन कल गर्न सकिँदैन"</string>
+ <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"आपत्कालीन कल सेवा अनुपलब्ध छ"</string>
+ <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Wi‑Fi मार्फत आपत्कालीन कल गर्न सकिँदैन"</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_emergency_callback" msgid="54074839059123159">"आपत्कालीन कलब्याक मोड"</string>
<string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"मोबाइल डेटाको स्थिति"</string>
<string name="notification_channel_sms" msgid="1243384981025535724">"SMS सन्देशहरू"</string>
<string name="notification_channel_voice_mail" msgid="8457433203106654172">"भ्वाइस मेल सन्देशहरू"</string>
@@ -241,7 +241,7 @@
<string name="global_action_power_off" msgid="4404936470711393203">"बन्द गर्नुहोस्"</string>
<string name="global_action_power_options" msgid="1185286119330160073">"पावर"</string>
<string name="global_action_restart" msgid="4678451019561687074">"पुनः सुरु गर्नुहोस्"</string>
- <string name="global_action_emergency" msgid="1387617624177105088">"आपतकालीन"</string>
+ <string name="global_action_emergency" msgid="1387617624177105088">"आपत्कालीन"</string>
<string name="global_action_bug_report" msgid="5127867163044170003">"बग रिपोर्ट"</string>
<string name="global_action_logout" msgid="6093581310002476511">"सत्रको अन्त्य गर्नुहोस्"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"स्क्रिनसट"</string>
@@ -345,24 +345,24 @@
<string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"एपलाई अन्य नम्बरमा कल पुर्ननिर्देश वा समग्र कल परित्याग विकल्प सहित बहिर्गमन कल समयमा डायल गर्दाको नम्बर हेर्न अनुमति दिन्छ।"</string>
<string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"फोन कलहरूको जवाफ दिनुहोस्"</string>
<string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"एपलाई आगमन फोन कलको जवाफ दिन अनुमति दिन्छ।"</string>
- <string name="permlab_receiveSms" msgid="505961632050451881">"पाठ सन्देशहरू (SMS) प्राप्त गर्नुहोस्"</string>
+ <string name="permlab_receiveSms" msgid="505961632050451881">"टेक्स्ट म्यासेजहरू (SMS) प्राप्त गर्नुहोस्"</string>
<string name="permdesc_receiveSms" msgid="1797345626687832285">"एपलाई SMS सन्देशहरू प्राप्त गर्न र प्रक्रिया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको सन्देशहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string>
<string name="permlab_receiveMms" msgid="4000650116674380275">"पाठ सन्देश (MMS) प्राप्त गर्नुहोस्"</string>
<string name="permdesc_receiveMms" msgid="958102423732219710">"एपलाई MMS सन्देशहरू प्राप्त गर्न र प्रकृया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको सन्देशहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string>
<string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू फर्वार्ड गर्नुहोस्"</string>
- <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू प्राप्त हुनासाथै तिनीहरूलाई फर्वार्ड गर्नका लागि यसले एपलाई मोबाइल प्रसारण मोड्युलमा जोडिने अनुमति दिन्छ। तपाईंलाई कतिपय स्थानमा आपत्कालीन अवस्थाका बारेमा जानकारी दिनका लागि मोबाइल प्रसारणसम्बन्धी अलर्टहरू पठाइन्छ। हानिकारक एपहरूले आपत्कालीन मोबाइल प्रसारण प्राप्त हुँदा तपाईंको यन्त्रलाई कार्य सम्पादन गर्ने वा सञ्चालित हुने क्रममा हस्तक्षेप गर्न सक्छन्।"</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू प्राप्त हुनासाथै तिनीहरूलाई फर्वार्ड गर्नका लागि यसले एपलाई मोबाइल प्रसारण मोड्युलमा जोडिने अनुमति दिन्छ। तपाईंलाई कतिपय स्थानमा आपत्कालीन अवस्थाका बारेमा जानकारी दिनका लागि मोबाइल प्रसारणसम्बन्धी अलर्टहरू पठाइन्छ। हानिकारक एपहरूले आपत्कालीन मोबाइल प्रसारण प्राप्त हुँदा तपाईंको यन्त्रलाई कार्य सम्पादन गर्ने वा सञ्चालित हुने क्रममा हस्तक्षेप गर्न सक्छन्।"</string>
<string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"सेल प्रसारित सन्देशहरू पढ्नुहोस्"</string>
- <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"तपाईंको उपकरणद्वारा प्राप्त सेल प्रसारण सन्देशहरू एपलाई पढ्न अनुमति दिन्छ। सेल प्रसारण चेतावनीहरू केही स्थानहरूमा तपाईंलाई आपतकालीन गतिविधिहरूको बारेमा सचेत गराउन गरिएका छन्। खराब एपहरूले एउटा आपतकालीन सेल प्रसारण प्राप्त गर्दछ जब तपाईंको उपकरणको प्रदर्शन वा अपरेशनको साथ हस्तक्षेप गर्न सक्दछन्।"</string>
+ <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"तपाईंको उपकरणद्वारा प्राप्त सेल प्रसारण सन्देशहरू एपलाई पढ्न अनुमति दिन्छ। सेल प्रसारण चेतावनीहरू केही स्थानहरूमा तपाईंलाई आपत्कालीन गतिविधिहरूको बारेमा सचेत गराउन गरिएका छन्। खराब एपहरूले एउटा आपत्कालीन सेल प्रसारण प्राप्त गर्दछ जब तपाईंको उपकरणको प्रदर्शन वा अपरेशनको साथ हस्तक्षेप गर्न सक्दछन्।"</string>
<string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"सदस्य बनाइका फिडहरू पढ्नुहोस्"</string>
<string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"एपलाई अहिलेको समीकरण गरिएका सूचकहरू बारे विवरणहरू लिने अनुमति दिन्छ।"</string>
<string name="permlab_sendSms" msgid="7757368721742014252">"SMS सन्देशहरू पठाउनुहोस् र हेर्नुहोस्"</string>
<string name="permdesc_sendSms" msgid="6757089798435130769">"एपलाई SMS सन्देशहरू पठाउन अनुमति दिन्छ। यसले अप्रत्यासित चार्जहरूको परिणाम दिन सक्दछ। खराब एपहरूले तपाईंको पुष्टि बिना सन्देशहरू पठाएर तपाईंको पैसा खर्च गराउन सक्दछ।"</string>
- <string name="permlab_readSms" msgid="5164176626258800297">"तपाईंका पाठ सन्देशहरू (SMS वा MMS) पढ्नुहोस्"</string>
+ <string name="permlab_readSms" msgid="5164176626258800297">"तपाईंका टेक्स्ट म्यासेजहरू (SMS वा MMS) पढ्नुहोस्"</string>
<string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"यस अनुप्रयोगले तपाईंको ट्याब्लेटमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।"</string>
<string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"यस अनुप्रयोगले तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका सबै SMS.(पाठ) सन्देशहरू पढ्न सक्छ।"</string>
<string name="permdesc_readSms" product="default" msgid="774753371111699782">"यस अनुप्रयोगले तपाईंको फोनमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।"</string>
- <string name="permlab_receiveWapPush" msgid="4223747702856929056">"पाठ सन्देशहरू (WAP) प्राप्त गर्नुहोस्"</string>
- <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP सन्देशहरू प्राप्त गर्न र प्रशोधन गर्न एपलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका सन्देशहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"</string>
+ <string name="permlab_receiveWapPush" msgid="4223747702856929056">"टेक्स्ट म्यासेजहरू (WAP) प्राप्त गर्नुहोस्"</string>
+ <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP सन्देशहरू प्राप्त गर्न र प्रशोधन गर्न एपलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका म्यासेजहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"</string>
<string name="permlab_getTasks" msgid="7460048811831750262">"चलिरहेका एपहरू पुनःबहाली गर्नुहोस्"</string>
<string name="permdesc_getTasks" msgid="7388138607018233726">"वर्तमानमा र भरखरै चलिरहेका कार्यहरू बारेको सूचना पुनःबहाली गर्न एपलाई अनुमित दिन्छ। यसले उपकरणमा प्रयोग भएका अनुप्रयोगहरूको बारेमा सूचना पत्ता लगाउन एपलाई अनुमति दिन सक्छ।"</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"प्रोफाइल र यन्त्र मालिकहरूको व्यवस्थापन गराउनुहोस्"</string>
@@ -451,7 +451,7 @@
<string name="permdesc_vibrate" msgid="8733343234582083721">"एपलाई भाइब्रेटर नियन्त्रण गर्न अनुमति दिन्छ।"</string>
<string name="permdesc_vibrator_state" msgid="7050024956594170724">"यो एपलाई कम्पनको स्थितिमाथि पहुँच राख्न दिनुहोस्।"</string>
<string name="permlab_callPhone" msgid="1798582257194643320">"फोन नम्बरहरूमा सीधै कल गर्नुहोस्"</string>
- <string name="permdesc_callPhone" msgid="5439809516131609109">"तपाईँको हस्तक्षेप बेगरै फोन नम्बर कल गर्न एपलाई अनुमति दिन्छ। यसले अनपेक्षित शुल्क वा कलहरू गराउन सक्छ। यसले एपलाई आपतकालीन नम्बरहरू कल गर्न अनुमति दिँदैन विचार गर्नुहोस्। खराब एपहरूले तपाईँको स्वीकार बिना कलहरू गरेर तपाईँलाई बढी पैसा तिराउन सक्छ।"</string>
+ <string name="permdesc_callPhone" msgid="5439809516131609109">"तपाईँको हस्तक्षेप बेगरै फोन नम्बर कल गर्न एपलाई अनुमति दिन्छ। यसले अनपेक्षित शुल्क वा कलहरू गराउन सक्छ। यसले एपलाई आपत्कालीन नम्बरहरू कल गर्न अनुमति दिँदैन विचार गर्नुहोस्। खराब एपहरूले तपाईँको स्वीकार बिना कलहरू गरेर तपाईँलाई बढी पैसा तिराउन सक्छ।"</string>
<string name="permlab_accessImsCallService" msgid="442192920714863782">"IMS कल सेवा पहुँच गर्नुहोस्"</string>
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"तपाईँको हस्तक्षेप बिना नै कल गर्न IMS सेवा प्रयोग गर्न एपलाई अनुमति दिन्छ।"</string>
<string name="permlab_readPhoneState" msgid="8138526903259297969">"फोन स्थिति र पहिचान पढ्नुहोस्"</string>
@@ -830,13 +830,13 @@
<string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"अनलक गर्न PIN कोड टाइप गर्नुहोस्"</string>
<string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"गलत PIN कोड।"</string>
<string name="keyguard_label_text" msgid="3841953694564168384">"अनलक गर्न मेनु थिच्नुहोस् र त्यसपछि ० थिच्नुहोस्।"</string>
- <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"आपतकालीन नम्बर"</string>
+ <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"आपत्कालीन नम्बर"</string>
<string name="lockscreen_carrier_default" msgid="6192313772955399160">"कुनै सेवा छैन"</string>
<string name="lockscreen_screen_locked" msgid="7364905540516041817">"स्क्रिन लक गरिएको।"</string>
- <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलक वा आपतकालीन कल गर्न मेनु थिच्नुहोस्।"</string>
+ <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलक वा आपत्कालीन कल गर्न मेनु थिच्नुहोस्।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"अनलक गर्न मेनु थिच्नुहोस्।"</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलक गर्नु ढाँचा खिच्नुहोस्"</string>
- <string name="lockscreen_emergency_call" msgid="7549683825868928636">"आपतकालीन कल"</string>
+ <string name="lockscreen_emergency_call" msgid="7549683825868928636">"आपत्कालीन कल"</string>
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"कलमा फर्किनुहोस्"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"सही!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फेरि प्रयास गर्नुहोस्"</string>
@@ -858,7 +858,7 @@
<string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"रोक्नुहोस्"</string>
<string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"दोहोर्याउनुहोस्"</string>
<string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"फास्ट फर्वार्ड"</string>
- <string name="emergency_calls_only" msgid="3057351206678279851">"आपतकालीन कलहरू मात्र"</string>
+ <string name="emergency_calls_only" msgid="3057351206678279851">"आपत्कालीन कलहरू मात्र"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"नेटवर्क लक छ"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"SIM कार्ड PUK-लक गरिएको छ।"</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"प्रयोगकर्ता निर्देशक वा ग्राहक सेवा सम्पर्क हर्नुहोस्।"</string>
@@ -1389,7 +1389,7 @@
<string name="ext_media_status_unmountable" msgid="7043574843541087748">"बिग्रेको"</string>
<string name="ext_media_status_unsupported" msgid="5460509911660539317">"असमर्थित"</string>
<string name="ext_media_status_ejecting" msgid="7532403368044013797">"निकाल्दै..."</string>
- <string name="ext_media_status_formatting" msgid="774148701503179906">"फरम्याट गर्दै…"</string>
+ <string name="ext_media_status_formatting" msgid="774148701503179906">"फर्म्याट गर्दै…"</string>
<string name="ext_media_status_missing" msgid="6520746443048867314">"सम्मिलित छैन"</string>
<string name="activity_list_empty" msgid="4219430010716034252">"कुनै मिल्ने गतिविधि पाइएन।"</string>
<string name="permlab_route_media_output" msgid="8048124531439513118">"मिडिया निकास दिशानिर्देश गराउनुहोस्"</string>
@@ -1477,7 +1477,7 @@
<string name="add_account_button_label" msgid="322390749416414097">"खाता थप्नुहोस्"</string>
<string name="number_picker_increment_button" msgid="7621013714795186298">"बढाउनुहोस्"</string>
<string name="number_picker_decrement_button" msgid="5116948444762708204">"घटाउनुहोस्"</string>
- <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> छोइराख्नुहोस्।"</string>
+ <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> टच एण्ड होल्ड गर्नुहोस्।"</string>
<string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"बढाउन माथि र घटाउन तल सार्नुहोस्।"</string>
<string name="time_picker_increment_minute_button" msgid="7195870222945784300">"मिनेट बढाउनुहोस्"</string>
<string name="time_picker_decrement_minute_button" msgid="230925389943411490">"मिनेट घटाउनुहोस्"</string>
@@ -1654,15 +1654,17 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"सर्टकट प्रयोग गर्नुहोस्"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"रङ्ग उल्टाउने सुविधा"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"रङ्ग सच्याउने सुविधा"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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_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_gesture_instructional_text" msgid="9196230728837090497">"एउटा सुविधाबाट अर्को सुविधामा जान दुईवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा छोइराख्नुहोस्।"</string>
- <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"एउटा सुविधाबाट अर्को सुविधामा जान तीनवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा छोइराख्नुहोस्।"</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>
<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>
@@ -1966,7 +1968,7 @@
<string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"शान्त रहनुहोस् र नजिकै आश्रयस्थल खोज्नुहोस्।"</string>
<string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"तटीय क्षेत्र र नदीछेउका ठाउँहरू छाडी उच्च सतहमा अवस्थित कुनै अझ सुरक्षित ठाउँमा जानुहोस्।"</string>
<string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"शान्त रहनुहोस् र नजिकै आश्रयस्थल खोज्नुहोस्।"</string>
- <string name="etws_primary_default_message_test" msgid="4583367373909549421">"आपतकालीन सन्देशहरूको परीक्षण"</string>
+ <string name="etws_primary_default_message_test" msgid="4583367373909549421">"आपत्कालीन सन्देशहरूको परीक्षण"</string>
<string name="notification_reply_button_accessibility" msgid="5235776156579456126">"जवाफ दिनु…"</string>
<string name="etws_primary_default_message_others" msgid="7958161706019130739"></string>
<string name="mmcc_authentication_reject" msgid="4891965994643876369">"SIM मार्फत भ्वाइस कल गर्न मिल्दैन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 17e988e4..d460f40 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sneltoets gebruiken"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Kleurinversie"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Kleurcorrectie"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is ingeschakeld."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> uitgeschakeld."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Houd beide volumetoetsen drie seconden ingedrukt om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> te gebruiken"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 0f10cde..129f4d3 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ଶର୍ଟକଟ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ରଙ୍ଗ ବଦଳାଇବାର ସୁବିଧା"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 6ea5f52..ccd2039 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -432,23 +432,17 @@
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"ਆਪਣੀਆਂ ਆਡੀਓ ਸੈਟਿੰਗਾਂ ਬਦਲੋ"</string>
<string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"ਐਪ ਨੂੰ ਗਲੋਬਲ ਆਡੀਓ ਸੈਟਿੰਗਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਅਵਾਜ਼ ਅਤੇ ਆਊਟਪੁਟ ਲਈ ਕਿਹੜਾ ਸਪੀਕਰ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ।"</string>
<string name="permlab_recordAudio" msgid="1208457423054219147">" ਆਡੀਓ ਰਿਕਾਰਡ ਕਰਨ"</string>
- <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
- <skip />
- <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
- <skip />
- <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
- <skip />
+ <string name="permdesc_recordAudio" msgid="5857246765327514062">"ਇਹ ਐਪ ਵਰਤੋਂ ਵਿੱਚ ਹੋਣ ਵੇਲੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਵਰਤ ਕੇ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
+ <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰੋ"</string>
+ <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"ਇਹ ਐਪ ਕਿਸੇ ਵੇਲੇ ਵੀ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਵਰਤ ਕੇ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
<string name="permlab_sim_communication" msgid="176788115994050692">"SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜੋ"</string>
<string name="permdesc_sim_communication" msgid="4179799296415957960">"ਐਪ ਨੂੰ SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਬਹੁਤ ਘਾਤਕ ਹੈ।"</string>
<string name="permlab_activityRecognition" msgid="1782303296053990884">"ਸਰੀਰਕ ਸਰਗਰਮੀ ਨੂੰ ਪਛਾਣਨਾ"</string>
<string name="permdesc_activityRecognition" msgid="8667484762991357519">"ਇਹ ਐਪ ਤੁਹਾਡੀ ਸਰੀਰਕ ਸਰਗਰਮੀ ਨੂੰ ਪਛਾਣ ਸਕਦੀ ਹੈ।"</string>
<string name="permlab_camera" msgid="6320282492904119413">"ਤਸਵੀਰਾਂ ਅਤੇ ਵੀਡੀਓ ਬਣਾਓ"</string>
- <!-- no translation found for permdesc_camera (5240801376168647151) -->
- <skip />
- <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
- <skip />
- <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
- <skip />
+ <string name="permdesc_camera" msgid="5240801376168647151">"ਇਹ ਐਪ ਵਰਤੋਂ ਵਿੱਚ ਹੋਣ ਵੇਲੇ ਕੈਮਰੇ ਨੂੰ ਵਰਤ ਕੇ ਤਸਵੀਰਾਂ ਖਿੱਚ ਸਕਦੀ ਹੈ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
+ <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਤਸਵੀਰਾਂ ਖਿੱਚੋ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰੋ"</string>
+ <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"ਇਹ ਐਪ ਕਿਸੇ ਵੇਲੇ ਵੀ ਕੈਮਰੇ ਨੂੰ ਵਰਤ ਕੇ ਤਸਵੀਰਾਂ ਖਿੱਚ ਸਕਦੀ ਹੈ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
<string name="permlab_systemCamera" msgid="3642917457796210580">"ਸਿਸਟਮ ਕੈਮਰੇ ਨੂੰ ਤਸਵੀਰਾਂ ਅਤੇ ਵੀਡੀਓ ਬਣਾਉਣ ਲਈ ਐਪਲੀਕੇਸ਼ਨ ਜਾਂ ਸੇਵਾ ਤੱਕ ਪਹੁੰਚ ਦਿਓ"</string>
<string name="permdesc_systemCamera" msgid="5938360914419175986">"ਇਹ ਵਿਸ਼ੇਸ਼ ਅਧਿਕ੍ਰਿਤ ਜਾਂ ਸਿਸਟਮ ਐਪ ਕਿਸੇ ਵੇਲੇ ਵੀ ਸਿਸਟਮ ਕੈਮਰੇ ਨੂੰ ਵਰਤ ਕੇ ਤਸਵੀਰਾਂ ਖਿੱਚ ਸਕਦੀ ਹੈ ਅਤੇ ਵੀਡੀਓ ਫ਼ਾਈਲਾਂ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਨੂੰ ਵੀ android.permission.CAMERA ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ"</string>
<string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ਐਪਲੀਕੇਸ਼ਨ ਜਾਂ ਸੇਵਾ ਨੂੰ ਕੈਮਰਾ ਡੀਵਾਈਸਾਂ ਦੇ ਚਾਲੂ ਜਾਂ ਬੰਦ ਕੀਤੇ ਜਾਣ ਬਾਰੇ ਕਾਲਬੈਕ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ।"</string>
@@ -1660,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ਸ਼ਾਰਟਕੱਟ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"ਰੰਗ ਪਲਟਨਾ"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"ਰੰਗ ਸੁਧਾਈ"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index d626513..a55ca4c 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1698,6 +1698,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Użyj skrótu"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Odwrócenie kolorów"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcja kolorów"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została włączona."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została wyłączona."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Naciśnij i przytrzymaj oba przyciski głośności przez trzy sekundy, by użyć usługi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 450fddb..b353085 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correção de cor"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 6862001..321c114 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar atalho"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correção da cor"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas do volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Prima sem soltar as teclas de volume durante três segundos para utilizar o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 450fddb..b353085 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Correção de cor"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index f9e608f..2914094 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1676,6 +1676,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizați comanda rapidă"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversarea culorilor"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corecția culorii"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S-au apăsat lung tastele de volum. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S-au apăsat lung tastele de volum. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Apăsați ambele butoane de volum timp de trei secunde pentru a folosi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index be24770..e241bb7 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1698,6 +1698,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Использовать быстрое включение"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инверсия цветов"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Коррекция цвета"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index c23e475..bb2da61b 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"කෙටිමඟ භාවිතා කරන්න"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"වර්ණ අපවර්තනය"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"වර්ණ නිවැරදි කිරීම"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 980a945..02f25f5 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1698,6 +1698,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Použiť skratku"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzia farieb"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Úprava farieb"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vypnutá."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Ak chcete používať službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, pridržte tri sekundy oba klávesy hlasitosti"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 573a082..7fd0243 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1698,6 +1698,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Uporabi bližnjico"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija barv"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Popravljanje barv"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vklopljena."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je izklopljena."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Za uporabo storitve <xliff:g id="SERVICE_NAME">%1$s</xliff:g> pritisnite obe tipki za glasnost in ju pridržite tri sekunde"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index f6f3594..13ad398 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1654,6 +1654,8 @@
<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_correction_feature_name" msgid="3655077237805422597">"Korrigjimi i ngjyrës"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tastet e volumit të mbajtura shtypur. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> i aktivizuar."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tastet e volumit të mbajtura shtypur. U çaktivizua \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Shtyp dhe mbaj shtypur të dy butonat e volumit për tre sekonda për të përdorur <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 366d29b..42dc695 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1676,6 +1676,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи пречицу"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија боја"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Корекција боја"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 988fb93..66ba07f 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Använd kortkommandot"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inverterade färger"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Färgkorrigering"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har aktiverats."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har inaktiverats."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tryck och håll båda volymknapparna i tre sekunder för att använda <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index fd390d8..5c0ab95 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Tumia Njia ya Mkato"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ugeuzaji rangi"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Usahihishaji wa rangi"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Vitufe vya sauti vilivyoshikiliwa. Umewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Vitufe vya sauti vimeshikiliwa. Umezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Bonyeza na ushikilie vitufe vyote viwili vya sauti kwa sekunde tatu ili utumie <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index c764a20..23b7211 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ஷார்ட்கட்டைப் பயன்படுத்து"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"கலர் இன்வெர்ஷன்"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"வண்ணத் திருத்தம்"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 23292a2..9b03dcb 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"సత్వరమార్గాన్ని ఉపయోగించు"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"కలర్ మార్పిడి"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"కలర్ సరిచేయడం"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index d5d21c6..fc33060 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ใช้ทางลัด"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"การกลับสี"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"การแก้สี"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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">"กดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 3 วินาทีเพื่อใช้ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index f53851d..bc67249 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gamitin ang Shortcut"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Pag-invert ng Kulay"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Pagwawasto ng Kulay"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pinindot nang matagal ang volume keys. Na-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pinindot nang matagal ang volume keys. Na-off ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pindutin nang matagal ang parehong volume key sa loob ng tatlong segundo para magamit ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 5aa336e..c870742 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kısayolu Kullan"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Rengi Ters Çevirme"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Renk Düzeltme"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> açıldı."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kapatıldı."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini kullanmak için her iki ses tuşunu basılı tutun"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 0c15f9e..3af955d 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1137,7 +1137,7 @@
<string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
<string name="selectAll" msgid="1532369154488982046">"Вибрати все"</string>
- <string name="cut" msgid="2561199725874745819">"Виріз."</string>
+ <string name="cut" msgid="2561199725874745819">"Вирізати"</string>
<string name="copy" msgid="5472512047143665218">"Копіювати"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Не вдалося скопіювати в буфер обміну"</string>
<string name="paste" msgid="461843306215520225">"Вставити"</string>
@@ -1698,6 +1698,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Використовувати ярлик"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія кольорів"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Корекція кольорів"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 94fad25..bbe3f33 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"شارٹ کٹ استعمال کریں"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"رنگوں کی تقلیب"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"رنگ کی تصحیح"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 61d29dc..d1d5115 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Tezkor ishga tushirishdan foydalanish"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ranglarni akslantirish"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Rangni tuzatish"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> yoqildi."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> faolsizlantirildi."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmatidan foydalanish uchun ikkala ovoz balandligi tugmalarini uzoq bosib turing"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index fd7205d..6be3ac8 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sử dụng phím tắt"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Đảo màu"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Chỉnh màu"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã bật."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã tắt."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Nhấn và giữ đồng thời cả hai phím âm lượng trong 3 giây để sử dụng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 16775d5..62b8937 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用快捷方式"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"颜色反转"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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">"同时按住两个音量键 3 秒钟即可使用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index bd13574..54f2a87 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用快速鍵"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"色彩反轉"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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">"㩒住兩個音量鍵 3 秒就可以用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 0637742..e71f794 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用捷徑"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"色彩反轉"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<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>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index ea97a6e..cc4a3d0 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1654,6 +1654,8 @@
<string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sebenzisa isinqamuleli"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Ukuguqulwa kombala"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Ukulungiswa kombala"</string>
+ <!-- no translation found for reduce_bright_colors_feature_name (6722364385073799185) -->
+ <skip />
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivuliwe."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivaliwe."</string>
<string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Cindezela uphinde ubambe bobabili okhiye bevolumu ngamasekhondi amathathu ukuze usebenzise i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 25c64a9..e3ddbd8 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3200,6 +3200,15 @@
then the system will set the same minimal height on all other activities in the task. It
will also ignore any other minimal height attributes of non-root activities. -->
<attr name="minHeight" />
+
+ <!-- Window layout affinity of this activity. Activities with the same window layout
+ affinity will share the same layout record. If an activity is launched in freeform window,
+ the activity will be launched to the latest position and size where any task, if the root
+ activity of that task shares the same window layout affinity with the activity being
+ launched. Window layout affinity is shared only among activities with the same UID.
+
+ <p>By default activity doesn't share any affinity with other activities. -->
+ <attr name="windowLayoutAffinity" format="string" />
</declare-styleable>
<!-- <code>restrict-update</code> tag restricts system apps from being updated unless the
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e434ac9..b4b634a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1686,6 +1686,21 @@
-->
</string-array>
+ <!-- Optional IPsec algorithms enabled by this device, defaulting to empty. OEMs can override
+ it by providing a list of algorithm names in an overlay config.xml file.
+
+ As Android releases new versions, more algorithms are becoming mandatory. Mandatory
+ algorithms will be automatically enabled on the device. Optional algorithms need
+ to be explicitly declared in this resource to be enabled.
+ * SDK level 28 makes the following algorithms mandatory : "cbc(aes)", "hmac(md5)",
+ "hmac(sha1)", "hmac(sha256)", "hmac(sha384)", "hmac(sha512)", "rfc4106(gcm(aes))"
+ * SDK level 30 makes the following algorithms mandatory : "rfc3686(ctr(aes))",
+ "xcbc(aes)", "rfc7539esp(chacha20,poly1305)"
+ -->
+ <string-array name="config_optionalIpSecAlgorithms" translatable="false">
+ <!-- Add algorithm here -->
+ </string-array>
+
<!-- Boolean indicating if current platform supports bluetooth SCO for off call
use cases -->
<bool name="config_bluetooth_sco_off_call">true</bool>
@@ -1803,6 +1818,9 @@
Note: This config is deprecated, please use config_defaultSms instead. -->
<string name="default_sms_application" translatable="false">com.android.messaging</string>
+ <!-- Flag indicating whether the current device supports "Ask every time" for sms-->
+ <bool name="config_sms_ask_every_time_support">true</bool>
+
<!-- Flag indicating whether the current device allows data.
If true, this means that the device supports data connectivity through
the telephony network.
@@ -4440,4 +4458,13 @@
<!-- WindowsManager JetPack display features -->
<string name="config_display_features" translatable="false" />
+
+ <!-- Physical Display IDs of the display-devices that are swapped when a folding device folds.
+ This list is expected to contain two elements: the first is the display to use
+ when the device is folded, the second is the display to use when unfolded. If the array
+ is empty or the display IDs are not recognized, this feature is turned off and the value
+ ignored.
+ TODO: b/170470621 - remove once we can have multiple Internal displays in DMS as
+ well as a notification from DisplayStateManager. -->
+ <string-array name="config_internalFoldedPhysicalDisplayIds" translatable="false" />
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index fe17eca..45bdff9 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3046,6 +3046,7 @@
<public-group type="attr" first-id="0x01010617">
<public name="rollbackDataPolicy" />
<public name="allowClickWhenDisabled" />
+ <public name="windowLayoutAffinity" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fc489b1..645bae7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4494,6 +4494,9 @@
shown in the warning dialog about the accessibility shortcut. -->
<string name="color_correction_feature_name">Color Correction</string>
+ <!-- Title of Reduce Bright Colors feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
+ <string name="reduce_bright_colors_feature_name">Reduce Bright Colors</string>
+
<!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility service. [CHAR LIMIT=none] -->
<string name="accessibility_shortcut_enabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned on.</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1249b17..4ee9123 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -313,6 +313,7 @@
<java-symbol type="bool" name="config_networkSamplingWakesDevice" />
<java-symbol type="bool" name="config_showMenuShortcutsWhenKeyboardPresent" />
<java-symbol type="bool" name="config_sip_wifi_only" />
+ <java-symbol type="bool" name="config_sms_ask_every_time_support" />
<java-symbol type="bool" name="config_sms_capable" />
<java-symbol type="bool" name="config_sms_utf8_support" />
<java-symbol type="bool" name="config_mobile_data_capable" />
@@ -3195,6 +3196,9 @@
<!-- Network Recommendation -->
<java-symbol type="string" name="config_defaultNetworkRecommendationProviderPackage" />
+ <!-- Optional IPsec algorithms -->
+ <java-symbol type="array" name="config_optionalIpSecAlgorithms" />
+
<!-- Whether allow 3rd party apps on internal storage. -->
<java-symbol type="bool" name="config_allow3rdPartyAppOnInternal" />
@@ -3249,6 +3253,7 @@
<java-symbol type="string" name="accessibility_shortcut_disabling_service" />
<java-symbol type="string" name="color_inversion_feature_name" />
<java-symbol type="string" name="color_correction_feature_name" />
+ <java-symbol type="string" name="reduce_bright_colors_feature_name" />
<java-symbol type="string" name="config_defaultAccessibilityService" />
<java-symbol type="string" name="accessibility_shortcut_spoken_feedback" />
@@ -4074,4 +4079,5 @@
<java-symbol type="array" name="config_keep_warming_services" />
<java-symbol type="string" name="config_display_features" />
+ <java-symbol type="array" name="config_internalFoldedPhysicalDisplayIds" />
</resources>
diff --git a/core/sysprop/Android.bp b/core/sysprop/Android.bp
index 7f20a0b..237ede2 100644
--- a/core/sysprop/Android.bp
+++ b/core/sysprop/Android.bp
@@ -19,3 +19,11 @@
api_packages: ["android.sysprop"],
vendor_available: false,
}
+
+sysprop_library {
+ name: "com.android.sysprop.watchdog",
+ srcs: ["WatchdogProperties.sysprop"],
+ property_owner: "Platform",
+ api_packages: ["android.sysprop"],
+ vendor_available: false,
+}
diff --git a/core/sysprop/WatchdogProperties.sysprop b/core/sysprop/WatchdogProperties.sysprop
new file mode 100644
index 0000000..1bcc773
--- /dev/null
+++ b/core/sysprop/WatchdogProperties.sysprop
@@ -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.
+
+module: "android.sysprop.WatchdogProperties"
+owner: Platform
+
+# To escape the watchdog timeout loop, fatal reboot the system when
+# watchdog timed out 'fatal_count' times in 'fatal_window_second'
+# seconds, if both values are not 0. Default value of both is 0.
+prop {
+ api_name: "fatal_count"
+ type: Integer
+ prop_name: "framework_watchdog.fatal_count"
+ scope: Internal
+ access: Readonly
+}
+
+prop {
+ api_name: "fatal_window_second"
+ type: Integer
+ prop_name: "framework_watchdog.fatal_window.second"
+ scope: Internal
+ access: Readonly
+}
+
+# The fatal counting can be disabled by setting property
+# 'is_fatal_ignore' to true.
+prop {
+ api_name: "is_fatal_ignore"
+ type: Boolean
+ prop_name: "persist.debug.framework_watchdog.fatal_ignore"
+ scope: Internal
+ access: Readonly
+}
diff --git a/core/sysprop/api/com.android.sysprop.localization-current.txt b/core/sysprop/api/com.android.sysprop.localization-current.txt
index fe4f457..e69de29 100644
--- a/core/sysprop/api/com.android.sysprop.localization-current.txt
+++ b/core/sysprop/api/com.android.sysprop.localization-current.txt
@@ -1,9 +0,0 @@
-props {
- module: "android.sysprop.LocalizationProperties"
- prop {
- api_name: "locale_filter"
- type: String
- scope: Internal
- prop_name: "ro.localization.locale_filter"
- }
-}
diff --git a/core/sysprop/api/com.android.sysprop.watchdog-current.txt b/core/sysprop/api/com.android.sysprop.watchdog-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/core/sysprop/api/com.android.sysprop.watchdog-current.txt
diff --git a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
new file mode 100644
index 0000000..d901aef
--- /dev/null
+++ b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
@@ -0,0 +1,20 @@
+props {
+ module: "android.sysprop.WatchdogProperties"
+ prop {
+ api_name: "fatal_count"
+ type: Integer
+ scope: Internal
+ prop_name: "framework_watchdog.fatal_count"
+ }
+ prop {
+ api_name: "fatal_window_second"
+ type: Integer
+ scope: Internal
+ prop_name: "framework_watchdog.fatal_window.second"
+ }
+ prop {
+ api_name: "is_fatal_ignore"
+ scope: Internal
+ prop_name: "persist.debug.framework_watchdog.fatal_ignore"
+ }
+}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 6f55b88..38dce15 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -164,6 +164,14 @@
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
</intent-filter>
</activity>
+ <activity android:name="android.view.ViewInputConnectionTestActivity"
+ android:label="View Input Connection Test"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
<activity android:name="StubTestBrowserActivity"
android:label="Stubbed Test Browser"
android:exported="true">
diff --git a/core/tests/coretests/res/layout/activity_view_ic_test.xml b/core/tests/coretests/res/layout/activity_view_ic_test.xml
new file mode 100644
index 0000000..fa26869
--- /dev/null
+++ b/core/tests/coretests/res/layout/activity_view_ic_test.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/root"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+</LinearLayout>
diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
index 7fa1613..3df0a68 100644
--- a/core/tests/coretests/src/android/app/NotificationHistoryTest.java
+++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
@@ -117,8 +117,8 @@
history.addNotificationToWrite(n);
assertThat(history.getNotificationsToWrite().size()).isEqualTo(2);
- assertThat(history.getNotificationsToWrite().get(0)).isSameAs(n2);
- assertThat(history.getNotificationsToWrite().get(1)).isSameAs(n);
+ assertThat(history.getNotificationsToWrite().get(0)).isSameInstanceAs(n2);
+ assertThat(history.getNotificationsToWrite().get(1)).isSameInstanceAs(n);
assertThat(history.getHistoryCount()).isEqualTo(2);
}
@@ -141,11 +141,11 @@
history.addNotificationsToWrite(secondHistory);
assertThat(history.getNotificationsToWrite().size()).isEqualTo(5);
- assertThat(history.getNotificationsToWrite().get(0)).isSameAs(n3);
- assertThat(history.getNotificationsToWrite().get(1)).isSameAs(n);
- assertThat(history.getNotificationsToWrite().get(2)).isSameAs(n4);
- assertThat(history.getNotificationsToWrite().get(3)).isSameAs(n2);
- assertThat(history.getNotificationsToWrite().get(4)).isSameAs(n5);
+ assertThat(history.getNotificationsToWrite().get(0)).isSameInstanceAs(n3);
+ assertThat(history.getNotificationsToWrite().get(1)).isSameInstanceAs(n);
+ assertThat(history.getNotificationsToWrite().get(2)).isSameInstanceAs(n4);
+ assertThat(history.getNotificationsToWrite().get(3)).isSameInstanceAs(n2);
+ assertThat(history.getNotificationsToWrite().get(4)).isSameInstanceAs(n5);
assertThat(history.getHistoryCount()).isEqualTo(5);
assertThat(history.getPooledStringsToWrite()).asList().contains(n2.getChannelName());
diff --git a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
index ba060fa..593e70e 100644
--- a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
@@ -45,7 +45,8 @@
CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
assertThat(compoundFormula.getConnector()).isEqualTo(CompoundFormula.AND);
- assertThat(compoundFormula.getFormulas()).containsAllOf(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2);
+ assertThat(compoundFormula.getFormulas())
+ .containsAtLeast(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2);
}
@Test
diff --git a/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt b/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt
index d45fee9..9ad63ad 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt
+++ b/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt
@@ -113,7 +113,7 @@
assertError(result)
assertThat(result.errorCode).isEqualTo(errorCode)
assertThat(result.errorMessage).isEqualTo(errorMessage)
- assertThat(result.exception).isSameAs(exception)
+ assertThat(result.exception).isSameInstanceAs(exception)
}
@Test
@@ -125,13 +125,13 @@
assertError(result)
assertThat(result.errorCode).isEqualTo(errorCode)
assertThat(result.errorMessage).isEqualTo(errorMessage)
- assertThat(result.exception).isSameAs(exception)
+ assertThat(result.exception).isSameInstanceAs(exception)
val carriedResult = input.error<Int>(result)
assertError(carriedResult)
assertThat(carriedResult.errorCode).isEqualTo(errorCode)
assertThat(carriedResult.errorMessage).isEqualTo(errorMessage)
- assertThat(carriedResult.exception).isSameAs(exception)
+ assertThat(carriedResult.exception).isSameInstanceAs(exception)
}
@Test
@@ -259,7 +259,7 @@
private fun assertSuccess(expected: Any? = null, result: ParseResult<*>) {
assertThat(result.isError).isFalse()
assertThat(result.isSuccess).isTrue()
- assertThat(result.result).isSameAs(expected)
+ assertThat(result.result).isSameInstanceAs(expected)
assertThat(result.errorCode).isEqualTo(PackageManager.INSTALL_SUCCEEDED)
assertThat(result.errorMessage).isNull()
assertThat(result.exception).isNull()
diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
index 0f6284d..01cf311 100644
--- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
+++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
@@ -362,14 +362,12 @@
private final class TestPresentation extends Presentation {
private final int mColor;
- private final int mWindowType;
private final int mWindowFlags;
public TestPresentation(Context context, Display display,
int color, int windowType, int windowFlags) {
- super(context, display);
+ super(context, display, 0 /* theme */, windowType);
mColor = color;
- mWindowType = windowType;
mWindowFlags = windowFlags;
}
@@ -378,7 +376,6 @@
super.onCreate(savedInstanceState);
setTitle(TAG);
- getWindow().setType(mWindowType);
getWindow().addFlags(mWindowFlags);
// Create a solid color image to use as the content of the presentation.
diff --git a/core/tests/coretests/src/android/text/TextShaperTest.java b/core/tests/coretests/src/android/text/TextShaperTest.java
new file mode 100644
index 0000000..8237cb0
--- /dev/null
+++ b/core/tests/coretests/src/android/text/TextShaperTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.text;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextShaperTest {
+
+ @Test
+ public void testFontWithPath() {
+ TextPaint p = new TextPaint();
+ p.setFontFeatureSettings("'wght' 900");
+ TextShaper.shapeText("a", 0, 1, TextDirectionHeuristics.LTR, p,
+ (start, end, glyphs, paint) -> {
+ // This test only passes if the font of the Latin font is variable font.
+ assertThat(glyphs.getFont(0).getFile()).isNotNull();
+ });
+ }
+}
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index 285ad9f..4e49118 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -16,6 +16,8 @@
package android.text;
+import static android.text.TextUtils.formatSimple;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -794,4 +796,53 @@
assertEquals("", TextUtils.trimToLengthWithEllipsis("", 3));
assertNull(TextUtils.trimToLengthWithEllipsis(null, 3));
}
+
+ @Test
+ public void testFormatSimple_Types() {
+ assertEquals("true", formatSimple("%b", true));
+ assertEquals("false", formatSimple("%b", false));
+ assertEquals("true", formatSimple("%b", this));
+ assertEquals("false", formatSimple("%b", new Object[] { null }));
+
+ assertEquals("!", formatSimple("%c", '!'));
+
+ assertEquals("42", formatSimple("%d", 42));
+ assertEquals("281474976710656", formatSimple("%d", 281474976710656L));
+
+ assertEquals("3.14159", formatSimple("%f", 3.14159));
+ assertEquals("NaN", formatSimple("%f", Float.NaN));
+
+ assertEquals("example", formatSimple("%s", "example"));
+ assertEquals("null", formatSimple("%s", new Object[] { null }));
+
+ assertEquals("2a", formatSimple("%x", 42));
+ assertEquals("1000000000000", formatSimple("%x", 281474976710656L));
+
+ assertEquals("%", formatSimple("%%"));
+ }
+
+ @Test
+ public void testFormatSimple_Empty() {
+ assertEquals("", formatSimple(""));
+ }
+
+ @Test
+ public void testFormatSimple_Typical() {
+ assertEquals("String foobar and %% number -42 together",
+ formatSimple("String %s%s and %%%% number %d%d together", "foo", "bar", -4, 2));
+ }
+
+ @Test
+ public void testFormatSimple_Mismatch() {
+ try {
+ formatSimple("%s");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ try {
+ formatSimple("%s", "foo", "bar");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java
index a3434e8..212cc44 100644
--- a/core/tests/coretests/src/android/text/format/DateFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java
@@ -21,13 +21,19 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.compat.testing.PlatformCompatChangeRule;
import android.icu.text.DateFormatSymbols;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import java.util.Arrays;
@@ -38,6 +44,9 @@
@RunWith(AndroidJUnit4.class)
public class DateFormatTest {
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
@Test
public void testHasDesignator() {
assertTrue(DateFormat.hasDesignator("hh:mm:ss", DateFormat.MINUTE));
@@ -135,4 +144,29 @@
private static String best(Locale l, String skeleton) {
return DateFormat.getBestDateTimePattern(l, skeleton);
}
+
+ @Test
+ @EnableCompatChanges({DateFormat.DISALLOW_DUPLICATE_FIELD_IN_SKELETON})
+ public void testGetBestDateTimePattern_disableDuplicateField() {
+ assertIllegalArgumentException(Locale.US, "jmma");
+ assertIllegalArgumentException(Locale.US, "ahmma");
+ }
+
+ @Test
+ @DisableCompatChanges({DateFormat.DISALLOW_DUPLICATE_FIELD_IN_SKELETON})
+ public void testGetBestDateTimePattern_enableDuplicateField() {
+ // en-US uses 12-hour format by default.
+ assertEquals("h:mm a", DateFormat.getBestDateTimePattern(Locale.US, "jmma"));
+ assertEquals("h:mm a", DateFormat.getBestDateTimePattern(Locale.US, "ahmma"));
+ }
+
+ private static void assertIllegalArgumentException(Locale l, String skeleton) {
+ try {
+ DateFormat.getBestDateTimePattern(l, skeleton);
+ fail("getBestDateTimePattern() does not fail with Locale: " + l
+ + " skeleton: " + skeleton);
+ } catch (IllegalArgumentException expected) {
+ // ignored
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/text/format/OWNERS b/core/tests/coretests/src/android/text/format/OWNERS
new file mode 100644
index 0000000..32adc12
--- /dev/null
+++ b/core/tests/coretests/src/android/text/format/OWNERS
@@ -0,0 +1,3 @@
+# Inherits OWNERS from parent directory, plus the following
+
+vichang@google.com
diff --git a/core/tests/coretests/src/android/view/ViewInputConnectionTest.java b/core/tests/coretests/src/android/view/ViewInputConnectionTest.java
new file mode 100644
index 0000000..d667af3
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewInputConnectionTest.java
@@ -0,0 +1,292 @@
+/*
+ * 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.view;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.Handler;
+import android.text.format.DateUtils;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.EditText;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for internal APIs/behaviors of {@link View} and {@link InputConnection}.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ViewInputConnectionTest {
+ @Rule
+ public ActivityTestRule<ViewInputConnectionTestActivity> mActivityRule =
+ new ActivityTestRule<>(ViewInputConnectionTestActivity.class);
+
+ private Instrumentation mInstrumentation;
+ private ViewInputConnectionTestActivity mActivity;
+ private InputMethodManager mImm;
+
+ @Before
+ public void before() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.getActivity();
+ PollingCheck.waitFor(5 * DateUtils.SECOND_IN_MILLIS, mActivity::hasWindowFocus);
+ assertTrue(mActivity.hasWindowFocus());
+ mImm = mActivity.getSystemService(InputMethodManager.class);
+ }
+
+ @Test
+ public void testInputConnectionCallbacks() throws Throwable {
+ // Add two EditText inputs to the layout view.
+ final ViewGroup viewGroup = mActivity.findViewById(R.id.root);
+ final TestEditText editText1 = new TestEditText(mActivity, false);
+ final TestEditText editText2 = new TestEditText(mActivity, false);
+ mActivityRule.runOnUiThread(() -> {
+ viewGroup.addView(editText1);
+ viewGroup.addView(editText2);
+ });
+ mInstrumentation.waitForIdleSync();
+
+ // Focus into the first EditText.
+ mActivityRule.runOnUiThread(editText1::requestFocus);
+ mInstrumentation.waitForIdleSync();
+ assertThat(editText1.isFocused()).isTrue();
+ assertThat(editText2.isFocused()).isFalse();
+
+ // Show the IME for the first EditText. Assert that the appropriate opened/closed callbacks
+ // have been invoked (InputConnection opened for the first EditText).
+ mActivityRule.runOnUiThread(() -> mImm.showSoftInput(editText1, 0));
+ mInstrumentation.waitForIdleSync();
+ mActivityRule.runOnUiThread(() -> {
+ assertThat(editText1.mCalledOnCreateInputConnection).isTrue();
+ assertThat(editText1.mCalledOnInputConnectionOpened).isTrue();
+ assertThat(editText1.mCalledOnInputConnectionClosed).isFalse();
+
+ assertThat(editText2.mCalledOnCreateInputConnection).isFalse();
+ assertThat(editText2.mCalledOnInputConnectionOpened).isFalse();
+ assertThat(editText2.mCalledOnInputConnectionClosed).isFalse();
+ });
+
+ // Focus into the second EditText.
+ mActivityRule.runOnUiThread(editText2::requestFocus);
+ mInstrumentation.waitForIdleSync();
+ assertThat(editText1.isFocused()).isFalse();
+ assertThat(editText2.isFocused()).isTrue();
+
+ // Show the IME for the second EditText. Assert that the appropriate opened/closed callbacks
+ // have been invoked (InputConnection closed for the first EditText and opened for the
+ // second EditText).
+ mActivityRule.runOnUiThread(() -> mImm.showSoftInput(editText2, 0));
+ mInstrumentation.waitForIdleSync();
+ mActivityRule.runOnUiThread(() -> {
+ assertThat(editText1.mCalledOnCreateInputConnection).isTrue();
+ assertThat(editText1.mCalledOnInputConnectionOpened).isTrue();
+ assertThat(editText1.mCalledOnInputConnectionClosed).isTrue();
+
+ assertThat(editText2.mCalledOnCreateInputConnection).isTrue();
+ assertThat(editText2.mCalledOnInputConnectionOpened).isTrue();
+ assertThat(editText2.mCalledOnInputConnectionClosed).isFalse();
+ });
+ }
+
+ @Test
+ public void testInputConnectionCallbacks_nullInputConnection() throws Throwable {
+ // Add two EditText inputs to the layout view.
+ final ViewGroup viewGroup = mActivity.findViewById(R.id.root);
+ final TestEditText editText1 = new TestEditText(mActivity, true);
+ final TestEditText editText2 = new TestEditText(mActivity, true);
+ mActivityRule.runOnUiThread(() -> {
+ viewGroup.addView(editText1);
+ viewGroup.addView(editText2);
+ });
+ mInstrumentation.waitForIdleSync();
+
+ // Focus into the first EditText.
+ mActivityRule.runOnUiThread(editText1::requestFocus);
+ mInstrumentation.waitForIdleSync();
+ assertThat(editText1.isFocused()).isTrue();
+ assertThat(editText2.isFocused()).isFalse();
+
+ // Show the IME for the first EditText. Assert that the opened/closed callbacks are not
+ // invoked since there's no input connection.
+ mActivityRule.runOnUiThread(() -> mImm.showSoftInput(editText1, 0));
+ mInstrumentation.waitForIdleSync();
+ mActivityRule.runOnUiThread(() -> {
+ assertThat(editText1.mCalledOnCreateInputConnection).isTrue();
+ assertThat(editText1.mCalledOnInputConnectionOpened).isFalse();
+ assertThat(editText1.mCalledOnInputConnectionClosed).isFalse();
+
+ assertThat(editText2.mCalledOnCreateInputConnection).isFalse();
+ assertThat(editText2.mCalledOnInputConnectionOpened).isFalse();
+ assertThat(editText2.mCalledOnInputConnectionClosed).isFalse();
+ });
+
+ // Focus into the second EditText.
+ mActivityRule.runOnUiThread(editText2::requestFocus);
+ mInstrumentation.waitForIdleSync();
+ assertThat(editText1.isFocused()).isFalse();
+ assertThat(editText2.isFocused()).isTrue();
+
+ // Show the IME for the second EditText. Assert that the opened/closed callbacks are not
+ // invoked since there's no input connection.
+ mActivityRule.runOnUiThread(() -> mImm.showSoftInput(editText2, 0));
+ mInstrumentation.waitForIdleSync();
+ mActivityRule.runOnUiThread(() -> {
+ assertThat(editText1.mCalledOnCreateInputConnection).isTrue();
+ assertThat(editText1.mCalledOnInputConnectionOpened).isFalse();
+ assertThat(editText1.mCalledOnInputConnectionClosed).isFalse();
+
+ assertThat(editText2.mCalledOnCreateInputConnection).isTrue();
+ assertThat(editText2.mCalledOnInputConnectionOpened).isFalse();
+ assertThat(editText2.mCalledOnInputConnectionClosed).isFalse();
+ });
+ }
+
+ @Test
+ public void testInputConnectionCallbacks_nonEditableInput() throws Throwable {
+ final ViewGroup viewGroup = mActivity.findViewById(R.id.root);
+ final TestButton view1 = new TestButton(mActivity);
+ final TestButton view2 = new TestButton(mActivity);
+ mActivityRule.runOnUiThread(() -> {
+ viewGroup.addView(view1);
+ viewGroup.addView(view2);
+ });
+ mInstrumentation.waitForIdleSync();
+
+ // Request focus + IME on the first view.
+ mActivityRule.runOnUiThread(view1::requestFocus);
+ mInstrumentation.waitForIdleSync();
+ assertThat(view1.isFocused()).isTrue();
+ assertThat(view2.isFocused()).isFalse();
+ mActivityRule.runOnUiThread(() -> mImm.showSoftInput(view1, 0));
+ mInstrumentation.waitForIdleSync();
+
+ // Assert that the opened/closed callbacks are not invoked since there's no InputConnection.
+ mActivityRule.runOnUiThread(() -> {
+ assertThat(view1.mCalledOnCreateInputConnection).isTrue();
+ assertThat(view1.mCalledOnInputConnectionOpened).isFalse();
+ assertThat(view1.mCalledOnInputConnectionClosed).isFalse();
+
+ assertThat(view2.mCalledOnCreateInputConnection).isFalse();
+ assertThat(view2.mCalledOnInputConnectionOpened).isFalse();
+ assertThat(view2.mCalledOnInputConnectionClosed).isFalse();
+ });
+
+ // Request focus + IME on the second view.
+ mActivityRule.runOnUiThread(view2::requestFocus);
+ mInstrumentation.waitForIdleSync();
+ assertThat(view1.isFocused()).isFalse();
+ assertThat(view2.isFocused()).isTrue();
+ mActivityRule.runOnUiThread(() -> mImm.showSoftInput(view1, 0));
+ mInstrumentation.waitForIdleSync();
+
+ // Assert that the opened/closed callbacks are not invoked since there's no InputConnection.
+ mActivityRule.runOnUiThread(() -> {
+ assertThat(view1.mCalledOnCreateInputConnection).isTrue();
+ assertThat(view1.mCalledOnInputConnectionOpened).isFalse();
+ assertThat(view1.mCalledOnInputConnectionClosed).isFalse();
+
+ assertThat(view2.mCalledOnCreateInputConnection).isTrue();
+ assertThat(view2.mCalledOnInputConnectionOpened).isFalse();
+ assertThat(view2.mCalledOnInputConnectionClosed).isFalse();
+ });
+ }
+
+ private static class TestEditText extends EditText {
+ private final boolean mReturnNullInputConnection;
+
+ public boolean mCalledOnCreateInputConnection = false;
+ public boolean mCalledOnInputConnectionOpened = false;
+ public boolean mCalledOnInputConnectionClosed = false;
+
+ TestEditText(Context context, boolean returnNullInputConnection) {
+ super(context);
+ mReturnNullInputConnection = returnNullInputConnection;
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ mCalledOnCreateInputConnection = true;
+ if (mReturnNullInputConnection) {
+ return null;
+ } else {
+ return super.onCreateInputConnection(outAttrs);
+ }
+ }
+
+ @Override
+ public void onInputConnectionOpenedInternal(@NonNull InputConnection inputConnection,
+ @NonNull EditorInfo editorInfo, @Nullable Handler handler) {
+ mCalledOnInputConnectionOpened = true;
+ super.onInputConnectionOpenedInternal(inputConnection, editorInfo, handler);
+ }
+
+ @Override
+ public void onInputConnectionClosedInternal() {
+ mCalledOnInputConnectionClosed = true;
+ super.onInputConnectionClosedInternal();
+ }
+ }
+
+ private static class TestButton extends Button {
+ public boolean mCalledOnCreateInputConnection = false;
+ public boolean mCalledOnInputConnectionOpened = false;
+ public boolean mCalledOnInputConnectionClosed = false;
+
+ TestButton(Context context) {
+ super(context);
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ mCalledOnCreateInputConnection = true;
+ return super.onCreateInputConnection(outAttrs);
+ }
+
+ @Override
+ public void onInputConnectionOpenedInternal(@NonNull InputConnection inputConnection,
+ @NonNull EditorInfo editorInfo, @Nullable Handler handler) {
+ mCalledOnInputConnectionOpened = true;
+ super.onInputConnectionOpenedInternal(inputConnection, editorInfo, handler);
+ }
+
+ @Override
+ public void onInputConnectionClosedInternal() {
+ mCalledOnInputConnectionClosed = true;
+ super.onInputConnectionClosedInternal();
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/view/ViewInputConnectionTestActivity.java b/core/tests/coretests/src/android/view/ViewInputConnectionTestActivity.java
new file mode 100644
index 0000000..55c812d
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewInputConnectionTestActivity.java
@@ -0,0 +1,31 @@
+/*
+ * 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.view;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
+
+public class ViewInputConnectionTestActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_view_ic_test);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 628252d..402b92a 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -52,7 +52,8 @@
@Test
public void testGetLocalTextClassifier() {
- assertThat(mTcm.getTextClassifier(TextClassifier.LOCAL)).isSameAs(TextClassifier.NO_OP);
+ assertThat(mTcm.getTextClassifier(TextClassifier.LOCAL))
+ .isSameInstanceAs(TextClassifier.NO_OP);
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
index f108eb8..a2bc77a 100644
--- a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
+++ b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
@@ -81,7 +81,7 @@
future.completeExceptionally(origException);
ExecutionException executionException =
expectThrows(ExecutionException.class, future::get);
- assertThat(executionException.getCause()).isSameAs(origException);
+ assertThat(executionException.getCause()).isSameInstanceAs(origException);
}
@Test
@@ -92,7 +92,7 @@
CountDownLatch latch = new CountDownLatch(1);
future.whenComplete((obj, err) -> {
assertThat(obj).isNull();
- assertThat(err).isSameAs(origException);
+ assertThat(err).isSameInstanceAs(origException);
latch.countDown();
});
latch.await();
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 5914887..942045c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -107,7 +107,7 @@
if (!isAlive) {
return false;
}
- assertThat(mRecipient).isSameAs(recipient);
+ assertThat(mRecipient).isSameInstanceAs(recipient);
mRecipient = null;
return true;
}
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
index 9f68ef3..7eca320 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -74,7 +74,7 @@
assertThat(copy.mImeBackDisposition).isEqualTo(original.mImeBackDisposition);
assertThat(copy.mShowImeSwitcher).isEqualTo(original.mShowImeSwitcher);
assertThat(copy.mDisabledFlags2).isEqualTo(original.mDisabledFlags2);
- assertThat(copy.mImeToken).isSameAs(original.mImeToken);
+ assertThat(copy.mImeToken).isSameInstanceAs(original.mImeToken);
assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme);
assertThat(copy.mAppFullscreen).isEqualTo(original.mAppFullscreen);
assertThat(copy.mAppImmersive).isEqualTo(original.mAppImmersive);
diff --git a/core/tests/coretests/src/com/android/internal/view/RecyclerViewCaptureHelperTest.java b/core/tests/coretests/src/com/android/internal/view/RecyclerViewCaptureHelperTest.java
new file mode 100644
index 0000000..88bbcc2
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/view/RecyclerViewCaptureHelperTest.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.view.ScrollCaptureViewHelper.ScrollResult;
+import com.android.internal.widget.LinearLayoutManager;
+import com.android.internal.widget.RecyclerView;
+
+import com.google.common.truth.Truth;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+@RunWith(AndroidJUnit4.class)
+public class RecyclerViewCaptureHelperTest {
+ private static final int CHILD_VIEWS = 12;
+ private static final int CHILD_VIEW_HEIGHT = 300;
+ private static final int WINDOW_WIDTH = 800;
+ private static final int WINDOW_HEIGHT = 1200;
+ private static final int CAPTURE_HEIGHT = 600;
+
+ private FrameLayout mParent;
+ private RecyclerView mTarget;
+ private WindowManager mWm;
+
+ private WindowManager.LayoutParams mWindowLayoutParams;
+
+ private Context mContext;
+ private float mDensity;
+ private LinearLayoutManager mLinearLayoutManager;
+ private Instrumentation mInstrumentation;
+
+ @Before
+ @UiThreadTest
+ public void setUp() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = mInstrumentation.getContext();
+ mDensity = mContext.getResources().getDisplayMetrics().density;
+
+ mParent = new FrameLayout(mContext);
+
+ mTarget = new RecyclerView(mContext);
+ mParent.addView(mTarget, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+ mTarget.setAdapter(new TestAdapter());
+ mLinearLayoutManager =
+ new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false);
+ mTarget.setLayoutManager(mLinearLayoutManager);
+ mWm = mContext.getSystemService(WindowManager.class);
+
+ // Setup the window that we are going to use
+ mWindowLayoutParams = new WindowManager.LayoutParams(WINDOW_WIDTH, WINDOW_HEIGHT,
+ TYPE_APPLICATION_OVERLAY, FLAG_NOT_TOUCHABLE, PixelFormat.OPAQUE);
+ mWindowLayoutParams.setTitle("ScrollViewCaptureHelper");
+ mWindowLayoutParams.gravity = Gravity.CENTER;
+ mWm.addView(mParent, mWindowLayoutParams);
+ }
+
+ @After
+ @UiThreadTest
+ public void tearDown() {
+ mWm.removeViewImmediate(mParent);
+ }
+
+ @Test
+ @UiThreadTest
+ public void onScrollRequested_up_fromTop() {
+ mTarget.scrollBy(0, -(WINDOW_HEIGHT * 3));
+ // mTarget.createSnapshot(new ViewDebug.HardwareCanvasProvider(), false);
+
+ RecyclerViewCaptureHelper rvc = new RecyclerViewCaptureHelper();
+ Rect scrollBounds = rvc.onComputeScrollBounds(mTarget);
+ rvc.onPrepareForStart(mTarget, scrollBounds);
+
+ assertThat(scrollBounds.height()).isGreaterThan(CAPTURE_HEIGHT);
+
+ Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
+
+ ScrollResult scrollResult = rvc.onScrollRequested(mTarget,
+ scrollBounds, request);
+
+ // The result is an empty rectangle and no scrolling, since it
+ // is not possible to physically scroll further up to make the
+ // requested area visible at all (it doesn't exist).
+ assertEmpty(scrollResult.availableArea);
+ }
+
+ @Test
+ @UiThreadTest
+ public void onScrollRequested_down_fromTop() {
+ mTarget.scrollBy(0, -(WINDOW_HEIGHT * 3));
+
+ RecyclerViewCaptureHelper rvc = new RecyclerViewCaptureHelper();
+ Rect scrollBounds = rvc.onComputeScrollBounds(mTarget);
+ rvc.onPrepareForStart(mTarget, scrollBounds);
+
+ assertThat(scrollBounds.height()).isGreaterThan(CAPTURE_HEIGHT);
+
+ // Capture between y = +1200 to +1800 pixels BELOW current top
+ Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
+ WINDOW_HEIGHT + CAPTURE_HEIGHT);
+
+ ScrollResult scrollResult = rvc.onScrollRequested(mTarget, scrollBounds, request);
+ assertThat(request).isEqualTo(scrollResult.requestedArea);
+ assertThat(request).isEqualTo(scrollResult.availableArea);
+ assertThat(scrollResult.scrollDelta).isEqualTo(CAPTURE_HEIGHT);
+ assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
+ }
+
+ @Test
+ @UiThreadTest
+ public void onScrollRequested_up_fromMiddle() {
+ mTarget.scrollBy(0, WINDOW_HEIGHT);
+
+ RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+ Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+ helper.onPrepareForStart(mTarget, scrollBounds);
+
+ Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
+
+ ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
+ assertThat(request).isEqualTo(scrollResult.requestedArea);
+ assertThat(request).isEqualTo(scrollResult.availableArea);
+ assertThat(scrollResult.scrollDelta).isEqualTo(-CAPTURE_HEIGHT);
+ assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
+ }
+
+ @Test
+ @UiThreadTest
+ public void onScrollRequested_down_fromMiddle() {
+ mTarget.scrollBy(0, WINDOW_HEIGHT);
+
+ RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+ Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+ helper.onPrepareForStart(mTarget, scrollBounds);
+
+ Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
+ WINDOW_HEIGHT + CAPTURE_HEIGHT);
+
+ ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
+ assertThat(request).isEqualTo(scrollResult.requestedArea);
+ assertThat(request).isEqualTo(scrollResult.availableArea);
+ assertThat(scrollResult.scrollDelta).isEqualTo(CAPTURE_HEIGHT);
+ assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
+ }
+
+ @Test
+ @UiThreadTest
+ public void onScrollRequested_up_fromBottom() {
+ mTarget.scrollBy(0, WINDOW_HEIGHT * 2);
+
+ RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+ Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+ helper.onPrepareForStart(mTarget, scrollBounds);
+
+ Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
+
+ ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
+ assertThat(request).isEqualTo(scrollResult.requestedArea);
+ assertThat(request).isEqualTo(scrollResult.availableArea);
+ assertThat(scrollResult.scrollDelta).isEqualTo(-CAPTURE_HEIGHT);
+ assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
+ }
+
+ @Test
+ @UiThreadTest
+ public void onScrollRequested_down_fromBottom() {
+ mTarget.scrollBy(0, WINDOW_HEIGHT * 3);
+
+ RecyclerViewCaptureHelper rvc = new RecyclerViewCaptureHelper();
+ Rect scrollBounds = rvc.onComputeScrollBounds(mTarget);
+ rvc.onPrepareForStart(mTarget, scrollBounds);
+
+ Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
+ WINDOW_HEIGHT + CAPTURE_HEIGHT);
+
+ ScrollResult scrollResult = rvc.onScrollRequested(mTarget,
+ scrollBounds, request);
+ Truth.assertThat(request).isEqualTo(scrollResult.requestedArea);
+
+ // The result is an empty rectangle and no scrolling, since it
+ // is not possible to physically scroll further down to make the
+ // requested area visible at all (it doesn't exist).
+ assertEmpty(scrollResult.availableArea);
+ }
+
+ @Test
+ @UiThreadTest
+ public void onScrollRequested_offTopEdge() {
+ mTarget.scrollBy(0, -(WINDOW_HEIGHT * 3));
+
+ RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+ Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+ helper.onPrepareForStart(mTarget, scrollBounds);
+
+ // Create a request which lands halfway off the top of the content
+ //from -1500 to -900, (starting at 1200 = -300 to +300 within the content)
+ int top = 0;
+ Rect request = new Rect(
+ 0, top - (CAPTURE_HEIGHT / 2),
+ scrollBounds.width(), top + (CAPTURE_HEIGHT / 2));
+
+ ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
+ assertThat(request).isEqualTo(scrollResult.requestedArea);
+
+ ScrollResult result = helper.onScrollRequested(mTarget, scrollBounds, request);
+ // The result is a partial result
+ Rect expectedResult = new Rect(request);
+ expectedResult.top += (CAPTURE_HEIGHT / 2); // top half clipped
+ assertThat(expectedResult).isEqualTo(result.availableArea);
+ assertThat(scrollResult.scrollDelta).isEqualTo(0);
+ assertAvailableAreaPartiallyVisible(scrollResult, mTarget);
+ }
+
+ @Test
+ @UiThreadTest
+ public void onScrollRequested_offBottomEdge() {
+ mTarget.scrollBy(0, WINDOW_HEIGHT * 2);
+
+ RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+ Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+ helper.onPrepareForStart(mTarget, scrollBounds);
+
+ // Create a request which lands halfway off the bottom of the content
+ //from 600 to to 1200, (starting at 2400 = 3000 to 3600 within the content)
+
+ int bottom = WINDOW_HEIGHT;
+ Rect request = new Rect(
+ 0, bottom - (CAPTURE_HEIGHT / 2),
+ scrollBounds.width(), bottom + (CAPTURE_HEIGHT / 2));
+
+ ScrollResult result = helper.onScrollRequested(mTarget, scrollBounds, request);
+
+ Rect expectedResult = new Rect(request);
+ expectedResult.bottom -= 300; // bottom half clipped
+ assertThat(expectedResult).isEqualTo(result.availableArea);
+ assertThat(result.scrollDelta).isEqualTo(0);
+ assertAvailableAreaPartiallyVisible(result, mTarget);
+ }
+
+ static final class TestViewHolder extends RecyclerView.ViewHolder {
+ TestViewHolder(View itemView) {
+ super(itemView);
+ }
+ }
+
+ static final class TestAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+ private final Random mRandom = new Random();
+
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new TestViewHolder(new TextView(parent.getContext()));
+ }
+
+ @Override
+ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+ TextView view = (TextView) holder.itemView;
+ view.setText("Child #" + position);
+ view.setTextColor(Color.WHITE);
+ view.setTextSize(30f);
+ view.setBackgroundColor(Color.rgb(mRandom.nextFloat(), mRandom.nextFloat(),
+ mRandom.nextFloat()));
+ view.setMinHeight(CHILD_VIEW_HEIGHT);
+ }
+
+ @Override
+ public int getItemCount() {
+ return CHILD_VIEWS;
+ }
+ }
+
+ static void assertEmpty(Rect r) {
+ if (r != null && !r.isEmpty()) {
+ fail("Not true that " + r + " is empty");
+ }
+ }
+
+ static Rect getVisibleRect(View v) {
+ Rect r = new Rect(0, 0, v.getWidth(), v.getHeight());
+ v.getLocalVisibleRect(r);
+ return r;
+ }
+
+ static void assertAvailableAreaCompletelyVisible(ScrollResult result, View container) {
+ Rect requested = new Rect(result.availableArea);
+ requested.offset(0, -result.scrollDelta); // make relative
+ Rect localVisible = getVisibleRect(container);
+ if (!localVisible.contains(requested)) {
+ fail("Not true that all of " + requested + " is contained by " + localVisible);
+ }
+ }
+
+ static void assertAvailableAreaPartiallyVisible(ScrollResult result, View container) {
+ Rect requested = new Rect(result.availableArea);
+ requested.offset(0, -result.scrollDelta); // make relative
+ Rect localVisible = getVisibleRect(container);
+ if (!Rect.intersects(localVisible, requested)) {
+ fail("Not true that any of " + requested + " is contained by " + localVisible);
+ }
+ }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/Android.bp b/core/tests/powertests/PowerStatsViewer/Android.bp
new file mode 100644
index 0000000..a3dc4fb
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/Android.bp
@@ -0,0 +1,13 @@
+android_test {
+ name: "PowerStatsViewer",
+ srcs: ["src/**/*.java"],
+ defaults: ["SettingsLibDefaults"],
+ static_libs: [
+ "androidx.appcompat_appcompat",
+ "androidx.cardview_cardview",
+ "androidx.recyclerview_recyclerview",
+ "com.google.android.material_material",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml b/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml
new file mode 100644
index 0000000..378d035
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.core.powerstatsviewer"
+ android:sharedUserId="android.uid.system">
+
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.BATTERY_STATS"/>
+
+ <application
+ android:theme="@style/Theme"
+ android:label="Power Stats Viewer">
+ <activity android:name=".PowerStatsViewerActivity"
+ android:label="Power Stats Viewer"
+ android:launchMode="singleTop"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".AppPickerActivity"
+ android:label="Power Stats - Select an App"/>
+
+ </application>
+</manifest>
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/app_info_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/app_info_layout.xml
new file mode 100644
index 0000000..fe6fe2d
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/app_info_layout.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/secondary_app_icon_size"
+ android:layout_height="@dimen/secondary_app_icon_size"
+ android:layout_marginEnd="12dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:gravity="start|center_vertical"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+ <TextView
+ android:id="@+id/uid"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+ <TextView
+ android:id="@+id/packages"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:maxLines="3"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/power_mah"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|center_vertical"
+ android:layout_marginStart="16dp"
+ android:gravity="right"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:visibility="gone"/>
+
+</LinearLayout>
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/app_picker_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/app_picker_layout.xml
new file mode 100644
index 0000000..6f28999
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/app_picker_layout.xml
@@ -0,0 +1,35 @@
+<?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.
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/app_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"/>
+
+ <ProgressBar
+ style="?android:attr/progressBarStyleLarge"
+ android:id="@+id/loading_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:indeterminate="true"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_entry_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_entry_layout.xml
new file mode 100644
index 0000000..1ced825
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_entry_layout.xml
@@ -0,0 +1,49 @@
+<?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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearanceBody"/>
+
+ <TextView
+ android:id="@+id/amount"
+ android:layout_width="0dp"
+ android:layout_weight="0.7"
+ android:layout_height="wrap_content"
+ android:gravity="right"
+ android:textAppearance="@style/TextAppearanceBody"/>
+
+ <TextView
+ android:id="@+id/percent"
+ android:layout_width="64dp"
+ android:layout_height="wrap_content"
+ android:gravity="right"
+ android:textAppearance="@style/TextAppearanceBody"/>
+</LinearLayout>
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml
new file mode 100644
index 0000000..9949418
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml
@@ -0,0 +1,77 @@
+<?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.
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <androidx.cardview.widget.CardView
+ style="@style/LoadTestCardView"
+ android:id="@+id/app_card"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginEnd="10dp"
+ android:layout_marginBottom="10dp"
+ android:layout_marginStart="10dp"
+ android:padding="20dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:minHeight="80dp"
+ android:paddingStart="10dp"
+ android:paddingEnd="10dp">
+
+ <include layout="@layout/app_info_layout"/>
+
+ </LinearLayout>
+ </androidx.cardview.widget.CardView>
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/power_stats_data_view"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <TextView
+ android:id="@+id/empty_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:visibility="gone"
+ android:text="No power stats available"/>
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/loading_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#AAFFFFFF">
+ <ProgressBar
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:indeterminate="true"/>
+ </FrameLayout>
+</FrameLayout>
diff --git a/core/tests/powertests/PowerStatsViewer/res/values/styles.xml b/core/tests/powertests/PowerStatsViewer/res/values/styles.xml
new file mode 100644
index 0000000..629d729
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/values/styles.xml
@@ -0,0 +1,34 @@
+<?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.
+ -->
+
+<resources>
+ <style name="Theme" parent="Theme.MaterialComponents.Light">
+ <item name="colorPrimary">#34a853</item>
+ <item name="android:windowActionBar">true</item>
+ <item name="android:windowNoTitle">false</item>
+ </style>
+
+ <style name="LoadTestCardView" parent="Widget.MaterialComponents.CardView">
+ <item name="cardBackgroundColor">#ceead6</item>
+ </style>
+
+ <style name="TextAppearanceBody" parent="android:TextAppearance.DeviceDefault">
+ <item name="android:textColor">#000000</item>
+ <item name="android:textSize">18sp</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppInfoHelper.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppInfoHelper.java
new file mode 100644
index 0000000..8526561
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppInfoHelper.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.frameworks.core.powerstatsviewer;
+
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Process;
+
+import com.android.internal.os.BatterySipper;
+
+class AppInfoHelper {
+
+ private static final String SYSTEM_SERVER_PACKAGE_NAME = "android";
+
+ public static class AppInfo {
+ public int uid;
+ public CharSequence label;
+ public double powerMah;
+ public ApplicationInfo iconInfo;
+ public CharSequence packages;
+ }
+
+ @Nullable
+ public static AppInfo makeApplicationInfo(PackageManager packageManager, int uid,
+ @Nullable BatterySipper sipper) {
+ if (sipper != null && sipper.drainType != BatterySipper.DrainType.APP) {
+ return null;
+ }
+
+ String packageWithHighestDrain = null;
+
+ AppInfo info = new AppInfo();
+ info.uid = uid;
+ if (sipper != null) {
+ sipper.sumPower();
+ info.powerMah = sipper.totalSmearedPowerMah;
+ packageWithHighestDrain = sipper.packageWithHighestDrain;
+ }
+ if (info.uid == Process.ROOT_UID) {
+ info.label = "<root>";
+ } else {
+ String[] packages = packageManager.getPackagesForUid(info.uid);
+ String primaryPackageName = null;
+ if (info.uid == Process.SYSTEM_UID) {
+ primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME;
+ } else if (packages != null) {
+ for (String name : packages) {
+ primaryPackageName = name;
+ if (name.equals(packageWithHighestDrain)) {
+ 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;
+ }
+ }
+
+ // Default the app icon to System Server. This includes root, dex2oat and other UIDs.
+ if (info.iconInfo == null) {
+ try {
+ info.iconInfo =
+ packageManager.getApplicationInfo(SYSTEM_SERVER_PACKAGE_NAME, 0);
+ } catch (PackageManager.NameNotFoundException nameNotFoundException) {
+ // Won't happen
+ }
+ }
+ return info;
+ }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppPickerActivity.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppPickerActivity.java
new file mode 100644
index 0000000..b4fc73c
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppPickerActivity.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.frameworks.core.powerstatsviewer;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.activity.ComponentActivity;
+import androidx.activity.result.contract.ActivityResultContract;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.frameworks.core.powerstatsviewer.AppInfoHelper.AppInfo;
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settingslib.utils.AsyncLoaderCompat;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Picker, showing a sorted list of applications consuming power. Returns the selected
+ * application UID or Process.INVALID_UID.
+ */
+public class AppPickerActivity extends ComponentActivity {
+ private static final String TAG = "AppPicker";
+
+ public static final ActivityResultContract<Void, Integer> CONTRACT =
+ new ActivityResultContract<Void, Integer>() {
+ @NonNull
+ @Override
+ public Intent createIntent(@NonNull Context context, Void aVoid) {
+ return new Intent(context, AppPickerActivity.class);
+ }
+
+ @Override
+ public Integer parseResult(int resultCode, @Nullable Intent intent) {
+ if (resultCode != RESULT_OK || intent == null) {
+ return Process.INVALID_UID;
+ }
+ return intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID);
+ }
+ };
+
+ private AppListAdapter mAppListAdapter;
+ private RecyclerView mAppList;
+ private View mLoadingView;
+
+ private interface OnAppSelectedListener {
+ void onAppSelected(int uid);
+ }
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+
+ setContentView(R.layout.app_picker_layout);
+
+ mLoadingView = findViewById(R.id.loading_view);
+
+ mAppList = findViewById(R.id.app_list_view);
+ mAppList.setLayoutManager(new LinearLayoutManager(this));
+ mAppListAdapter = new AppListAdapter(AppPickerActivity.this::setSelectedUid);
+ mAppList.setAdapter(mAppListAdapter);
+
+ LoaderManager.getInstance(this).initLoader(0, null,
+ new AppListLoaderCallbacks());
+ }
+
+ protected void setSelectedUid(int uid) {
+ Intent intent = new Intent();
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+
+ @Override
+ public boolean onNavigateUp() {
+ onBackPressed();
+ return true;
+ }
+
+ private static class AppListLoader extends AsyncLoaderCompat<List<AppInfo>> {
+ private final BatteryStatsHelper mStatsHelper;
+ private final UserManager mUserManager;
+ private final PackageManager mPackageManager;
+
+ AppListLoader(Context context) {
+ super(context);
+ mUserManager = context.getSystemService(UserManager.class);
+ mStatsHelper = new BatteryStatsHelper(context, false /* collectBatteryBroadcast */);
+ mStatsHelper.create((Bundle) null);
+ mStatsHelper.clearStats();
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public List<AppInfo> loadInBackground() {
+ List<AppInfo> applicationList = new ArrayList<>();
+
+ mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
+ mUserManager.getUserProfiles());
+
+ final List<BatterySipper> usageList = mStatsHelper.getUsageList();
+ for (BatterySipper sipper : usageList) {
+ AppInfo info =
+ AppInfoHelper.makeApplicationInfo(mPackageManager, sipper.getUid(), sipper);
+ if (info != null) {
+ applicationList.add(info);
+ }
+ }
+
+ applicationList.sort(
+ Comparator.comparing((AppInfo a) -> a.powerMah).reversed());
+ return applicationList;
+ }
+
+ @Override
+ protected void onDiscardResult(List<AppInfo> result) {
+ }
+ }
+
+ private class AppListLoaderCallbacks implements
+ LoaderManager.LoaderCallbacks<List<AppInfo>> {
+
+ @NonNull
+ @Override
+ public Loader<List<AppInfo>> onCreateLoader(int id, Bundle args) {
+ return new AppListLoader(AppPickerActivity.this);
+ }
+
+ @Override
+ public void onLoadFinished(@NonNull Loader<List<AppInfo>> loader,
+ List<AppInfo> applicationList) {
+ mAppListAdapter.setApplicationList(applicationList);
+ mAppList.setVisibility(View.VISIBLE);
+ mLoadingView.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onLoaderReset(@NonNull Loader<List<AppInfo>> loader) {
+ }
+ }
+
+ public class AppListAdapter extends RecyclerView.Adapter<AppViewHolder> {
+ private final OnAppSelectedListener mListener;
+ private List<AppInfo> mApplicationList;
+
+ public AppListAdapter(OnAppSelectedListener listener) {
+ mListener = listener;
+ }
+
+ void setApplicationList(List<AppInfo> applicationList) {
+ mApplicationList = applicationList;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getItemCount() {
+ return mApplicationList.size();
+ }
+
+ @NonNull
+ @Override
+ public AppViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
+ LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
+ View view = layoutInflater.inflate(R.layout.app_info_layout, viewGroup, false);
+ return new AppViewHolder(view, mListener);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull AppViewHolder appViewHolder, int position) {
+ AppInfo item = mApplicationList.get(position);
+ appViewHolder.uid = item.uid;
+ appViewHolder.titleView.setText(item.label);
+ appViewHolder.uidView.setText(
+ String.format(Locale.getDefault(), "UID: %d", item.uid));
+ appViewHolder.powerView.setText(
+ String.format(Locale.getDefault(), "%.1f mAh", item.powerMah));
+ appViewHolder.iconView.setImageDrawable(
+ item.iconInfo.loadIcon(getPackageManager()));
+ if (item.packages != null) {
+ appViewHolder.packagesView.setText(item.packages);
+ appViewHolder.packagesView.setVisibility(View.VISIBLE);
+ } else {
+ appViewHolder.packagesView.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ // View Holder used when displaying apps
+ public static class AppViewHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+ private final OnAppSelectedListener mListener;
+
+ public int uid;
+ public TextView titleView;
+ public TextView uidView;
+ public ImageView iconView;
+ public TextView packagesView;
+ public TextView powerView;
+
+ AppViewHolder(View view, OnAppSelectedListener listener) {
+ super(view);
+ mListener = listener;
+ view.setOnClickListener(this);
+ titleView = view.findViewById(android.R.id.title);
+ uidView = view.findViewById(R.id.uid);
+ iconView = view.findViewById(android.R.id.icon);
+ packagesView = view.findViewById(R.id.packages);
+ powerView = view.findViewById(R.id.power_mah);
+ powerView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onClick(View v) {
+ mListener.onAppSelected(uid);
+ }
+ }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java
new file mode 100644
index 0000000..09f20ba
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.frameworks.core.powerstatsviewer;
+
+import android.content.Context;
+import android.os.Process;
+
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PowerStatsData {
+ 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};
+
+ enum EntryType {
+ POWER,
+ DURATION,
+ }
+
+ public static class Entry {
+ public String title;
+ public EntryType entryType;
+ public double value;
+ public double total;
+ }
+
+ private final AppInfoHelper.AppInfo mAppInfo;
+ private final List<Entry> mEntries = new ArrayList<>();
+
+ public PowerStatsData(Context context, BatteryStatsHelper batteryStatsHelper,
+ int uid) {
+ List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
+
+ 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;
+
+ 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 uidSipper = null;
+ for (BatterySipper sipper : usageList) {
+ if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
+ totalScreenPower = sipper.sumPower();
+ }
+
+ if (isHiddenDrainType(sipper.drainType)) {
+ continue;
+ }
+
+ if (sipper.drainType == BatterySipper.DrainType.APP && sipper.getUid() == uid) {
+ uidSipper = 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;
+ }
+
+ mAppInfo = AppInfoHelper.makeApplicationInfo(context.getPackageManager(), uid, uidSipper);
+
+ if (uidSipper == null) {
+ return;
+ }
+
+ addEntry("Total power", EntryType.POWER,
+ uidSipper.totalSmearedPowerMah, totalSmearedPowerMah);
+ addEntry("... excluding system", EntryType.POWER,
+ uidSipper.totalSmearedPowerMah, totalPowerExcludeSystemMah);
+ addEntry("Screen, smeared", EntryType.POWER,
+ uidSipper.screenPowerMah, totalScreenPower);
+ addEntry("Other, smeared", EntryType.POWER,
+ uidSipper.proportionalSmearMah, totalProportionalSmearMah);
+ addEntry("Excluding smeared", EntryType.POWER,
+ uidSipper.totalPowerMah, totalPowerMah);
+ addEntry("CPU", EntryType.POWER,
+ uidSipper.cpuPowerMah, totalCpuPowerMah);
+ addEntry("System services", EntryType.POWER,
+ uidSipper.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah);
+ addEntry("RAM", EntryType.POWER,
+ uidSipper.usagePowerMah, totalUsagePowerMah);
+ addEntry("Wake lock", EntryType.POWER,
+ uidSipper.wakeLockPowerMah, totalWakeLockPowerMah);
+ addEntry("Mobile radio", EntryType.POWER,
+ uidSipper.mobileRadioPowerMah, totalMobileRadioPowerMah);
+ addEntry("WiFi", EntryType.POWER,
+ uidSipper.wifiPowerMah, totalWifiPowerMah);
+ addEntry("Bluetooth", EntryType.POWER,
+ uidSipper.bluetoothPowerMah, totalBluetoothPowerMah);
+ addEntry("GPS", EntryType.POWER,
+ uidSipper.gpsPowerMah, totalGpsPowerMah);
+ addEntry("Camera", EntryType.POWER,
+ uidSipper.cameraPowerMah, totalCameraPowerMah);
+ addEntry("Flashlight", EntryType.POWER,
+ uidSipper.flashlightPowerMah, totalFlashlightPowerMah);
+ addEntry("Sensors", EntryType.POWER,
+ uidSipper.sensorPowerMah, totalSensorPowerMah);
+ addEntry("Audio", EntryType.POWER,
+ uidSipper.audioPowerMah, totalAudioPowerMah);
+ addEntry("Video", EntryType.POWER,
+ uidSipper.videoPowerMah, totalVideoPowerMah);
+
+ addEntry("CPU time", EntryType.DURATION,
+ uidSipper.cpuTimeMs, totalCpuTimeMs);
+ addEntry("CPU foreground time", EntryType.DURATION,
+ uidSipper.cpuFgTimeMs, totalCpuFgTimeMs);
+ addEntry("Wake lock time", EntryType.DURATION,
+ uidSipper.wakeLockTimeMs, totalWakeLockTimeMs);
+ addEntry("WiFi running time", EntryType.DURATION,
+ uidSipper.wifiRunningTimeMs, totalWifiRunningTimeMs);
+ addEntry("Bluetooth time", EntryType.DURATION,
+ uidSipper.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs);
+ addEntry("GPS time", EntryType.DURATION,
+ uidSipper.gpsTimeMs, totalGpsTimeMs);
+ addEntry("Camera time", EntryType.DURATION,
+ uidSipper.cameraTimeMs, totalCameraTimeMs);
+ addEntry("Flashlight time", EntryType.DURATION,
+ uidSipper.flashlightTimeMs, totalFlashlightTimeMs);
+ addEntry("Audio time", EntryType.DURATION,
+ uidSipper.audioTimeMs, totalAudioTimeMs);
+ addEntry("Video time", EntryType.DURATION,
+ uidSipper.videoTimeMs, totalVideoTimeMs);
+ }
+
+ protected boolean isHiddenDrainType(BatterySipper.DrainType drainType) {
+ return drainType == BatterySipper.DrainType.IDLE
+ || drainType == BatterySipper.DrainType.CELL
+ || drainType == BatterySipper.DrainType.SCREEN
+ || drainType == BatterySipper.DrainType.UNACCOUNTED
+ || drainType == BatterySipper.DrainType.OVERCOUNTED
+ || drainType == BatterySipper.DrainType.BLUETOOTH
+ || drainType == BatterySipper.DrainType.WIFI;
+ }
+
+ 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;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private void addEntry(String title, EntryType entryType, double amount, double totalAmount) {
+ Entry entry = new Entry();
+ entry.title = title;
+ entry.entryType = entryType;
+ entry.value = amount;
+ entry.total = totalAmount;
+ mEntries.add(entry);
+ }
+
+ public AppInfoHelper.AppInfo getAppInfo() {
+ return mAppInfo;
+ }
+
+ public List<Entry> getEntries() {
+ return mEntries;
+ }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java
new file mode 100644
index 0000000..1605e9c
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.frameworks.core.powerstatsviewer;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.activity.ComponentActivity;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.app.LoaderManager.LoaderCallbacks;
+import androidx.loader.content.Loader;
+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;
+import java.util.List;
+import java.util.Locale;
+
+public class PowerStatsViewerActivity extends ComponentActivity {
+ private static final int POWER_STATS_REFRESH_RATE_MILLIS = 60 * 1000;
+ public static final String PREF_SELECTED_UID = "selectedUid";
+ private static final String LOADER_ARG_UID = "uid";
+
+ private PowerStatsDataAdapter mPowerStatsDataAdapter;
+ private Runnable mPowerStatsRefresh = this::periodicPowerStatsRefresh;
+ private SharedPreferences mSharedPref;
+ private int mUid = Process.INVALID_UID;
+ private TextView mTitleView;
+ private TextView mUidView;
+ private ImageView mIconView;
+ private TextView mPackagesView;
+ private RecyclerView mPowerStatsDataView;
+ private View mLoadingView;
+ private View mEmptyView;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mSharedPref = getPreferences(Context.MODE_PRIVATE);
+
+ setContentView(R.layout.power_stats_viewer_layout);
+
+ View appCard = findViewById(R.id.app_card);
+ appCard.setOnClickListener((e) -> startAppPicker());
+
+ mTitleView = findViewById(android.R.id.title);
+ mUidView = findViewById(R.id.uid);
+ mIconView = findViewById(android.R.id.icon);
+ mPackagesView = findViewById(R.id.packages);
+
+ mPowerStatsDataView = findViewById(R.id.power_stats_data_view);
+ mPowerStatsDataView.setLayoutManager(new LinearLayoutManager(this));
+ mPowerStatsDataAdapter = new PowerStatsDataAdapter();
+ mPowerStatsDataView.setAdapter(mPowerStatsDataAdapter);
+
+ mLoadingView = findViewById(R.id.loading_view);
+ mEmptyView = findViewById(R.id.empty_view);
+
+ mUid = mSharedPref.getInt(PREF_SELECTED_UID, Process.INVALID_UID);
+ loadPowerStats();
+ if (mUid == Process.INVALID_UID) {
+ startAppPicker();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ periodicPowerStatsRefresh();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ getMainThreadHandler().removeCallbacks(mPowerStatsRefresh);
+ }
+
+ private void startAppPicker() {
+ registerForActivityResult(AppPickerActivity.CONTRACT, this::onApplicationSelected)
+ .launch(null);
+ }
+
+ private void onApplicationSelected(int uid) {
+ if (uid == -1) {
+ if (mUid == Process.INVALID_UID) {
+ finish();
+ }
+ } else {
+ mUid = uid;
+ mSharedPref.edit().putInt(PREF_SELECTED_UID, mUid).apply();
+ mLoadingView.setVisibility(View.VISIBLE);
+ loadPowerStats();
+ }
+ }
+
+ private void periodicPowerStatsRefresh() {
+ loadPowerStats();
+ getMainThreadHandler().postDelayed(mPowerStatsRefresh, POWER_STATS_REFRESH_RATE_MILLIS);
+ }
+
+ private void loadPowerStats() {
+ Bundle args = new Bundle();
+ args.putInt(LOADER_ARG_UID, mUid);
+ LoaderManager.getInstance(this).restartLoader(0, args, new PowerStatsDataLoaderCallbacks());
+ }
+
+ private static class PowerStatsDataLoader extends AsyncLoaderCompat<PowerStatsData> {
+ private final int mUid;
+ private final BatteryStatsHelper mBatteryStatsHelper;
+ private final UserManager mUserManager;
+
+ PowerStatsDataLoader(Context context, int uid) {
+ super(context);
+ mUid = uid;
+ mUserManager = context.getSystemService(UserManager.class);
+ mBatteryStatsHelper = new BatteryStatsHelper(context,
+ false /* collectBatteryBroadcast */);
+ mBatteryStatsHelper.create((Bundle) null);
+ mBatteryStatsHelper.clearStats();
+ }
+
+ @Override
+ public PowerStatsData loadInBackground() {
+ mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
+ mUserManager.getUserProfiles());
+ return new PowerStatsData(getContext(), mBatteryStatsHelper, mUid);
+ }
+
+ @Override
+ protected void onDiscardResult(PowerStatsData result) {
+ }
+ }
+
+ private class PowerStatsDataLoaderCallbacks implements LoaderCallbacks<PowerStatsData> {
+ @NonNull
+ @Override
+ public Loader<PowerStatsData> onCreateLoader(int id, Bundle args) {
+ return new PowerStatsDataLoader(PowerStatsViewerActivity.this,
+ args.getInt(LOADER_ARG_UID, Process.INVALID_UID));
+ }
+
+ @Override
+ public void onLoadFinished(@NonNull Loader<PowerStatsData> loader,
+ PowerStatsData powerStatsData) {
+
+ AppInfoHelper.AppInfo appInfo = powerStatsData.getAppInfo();
+ mTitleView.setText(appInfo.label);
+ mUidView.setText(String.format(Locale.getDefault(), "UID: %d", appInfo.uid));
+ mIconView.setImageDrawable(appInfo.iconInfo.loadIcon(getPackageManager()));
+
+ if (appInfo.packages != null) {
+ mPackagesView.setText(appInfo.packages);
+ mPackagesView.setVisibility(View.VISIBLE);
+ } else {
+ mPackagesView.setVisibility(View.GONE);
+ }
+
+ mPowerStatsDataAdapter.setEntries(powerStatsData.getEntries());
+
+ if (powerStatsData.getEntries().isEmpty()) {
+ mEmptyView.setVisibility(View.VISIBLE);
+ mPowerStatsDataView.setVisibility(View.GONE);
+ } else {
+ mEmptyView.setVisibility(View.GONE);
+ mPowerStatsDataView.setVisibility(View.VISIBLE);
+ }
+
+ mLoadingView.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onLoaderReset(@NonNull Loader<PowerStatsData> loader) {
+ }
+ }
+
+ private static class PowerStatsDataAdapter extends
+ RecyclerView.Adapter<PowerStatsDataAdapter.ViewHolder> {
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ public TextView titleTextView;
+ public TextView amountTextView;
+ public TextView percentTextView;
+
+ ViewHolder(View itemView) {
+ super(itemView);
+
+ titleTextView = itemView.findViewById(R.id.title);
+ amountTextView = itemView.findViewById(R.id.amount);
+ percentTextView = itemView.findViewById(R.id.percent);
+ }
+ }
+
+ private List<PowerStatsData.Entry> mEntries = Collections.emptyList();
+
+ public void setEntries(List<PowerStatsData.Entry> entries) {
+ mEntries = entries;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getItemCount() {
+ return mEntries.size();
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
+ LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
+ View itemView = layoutInflater.inflate(R.layout.power_stats_entry_layout, parent,
+ false);
+ return new ViewHolder(itemView);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
+ PowerStatsData.Entry entry = mEntries.get(position);
+ switch (entry.entryType) {
+ case POWER:
+ viewHolder.titleTextView.setText(entry.title);
+ viewHolder.amountTextView.setText(
+ String.format(Locale.getDefault(), "%.1f mAh", entry.value));
+ break;
+ case DURATION:
+ viewHolder.titleTextView.setText(entry.title);
+ viewHolder.amountTextView.setText(
+ String.format(Locale.getDefault(), "%,d ms", (long) entry.value));
+ break;
+ }
+
+ double proportion = entry.total != 0 ? entry.value * 100 / entry.total : 0;
+ viewHolder.percentTextView.setText(String.format(Locale.getDefault(), "%.1f%%",
+ proportion));
+ }
+ }
+}
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index e122e00..745de84 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -149,3 +149,11 @@
src: "com.android.car.ui.paintbooth.xml",
filename_from_src: true,
}
+
+prebuilt_etc {
+ name: "allowed_privapp_com.android.car.provision",
+ system_ext_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.car.provision.xml",
+ filename_from_src: true,
+}
\ No newline at end of file
diff --git a/data/etc/car/com.android.car.provision.xml b/data/etc/car/com.android.car.provision.xml
new file mode 100644
index 0000000..fa51d55e
--- /dev/null
+++ b/data/etc/car/com.android.car.provision.xml
@@ -0,0 +1,22 @@
+<?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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.car.provision">
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <permission name="android.permission.WRITE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 81da5c8..4c3b36f 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -437,6 +437,9 @@
<permission name="android.permission.MANAGE_DEBUGGING" />
<!-- Permissions required for CTS test - TimeManagerTest -->
<permission name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
+ <!-- Permissions required for CTS test - android.server.biometrics -->
+ <permission name="android.permission.USE_BIOMETRIC" />
+ <permission name="android.permission.TEST_BIOMETRIC" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 59727d5..31dae22 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -301,12 +301,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowState.java"
},
- "-1741065110": {
- "message": "No app is requesting an orientation, return %d for display id=%d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"-1730156332": {
"message": "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
"level": "VERBOSE",
@@ -529,6 +523,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "-1480772131": {
+ "message": "No app or window is requesting an orientation, return %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"-1474292612": {
"message": "Could not find task for id: %d",
"level": "DEBUG",
@@ -2515,12 +2515,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowToken.java"
},
- "845234215": {
- "message": "App is requesting an orientation, return %d for display id=%d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"849147756": {
"message": "Finish collecting in transition %d",
"level": "VERBOSE",
@@ -2923,6 +2917,12 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
},
+ "1381227466": {
+ "message": "App is requesting an orientation, return %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/TaskDisplayArea.java"
+ },
"1401295262": {
"message": "Mode default, asking user",
"level": "WARN",
@@ -3133,6 +3133,18 @@
"group": "WM_DEBUG_RESIZE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "1640436199": {
+ "message": "No app is requesting an orientation, return %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/TaskDisplayArea.java"
+ },
+ "1648338379": {
+ "message": "Display id=%d is ignoring all orientation requests, return %d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"1653210583": {
"message": "Removing app %s delayed=%b animation=%s animating=%b",
"level": "VERBOSE",
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java
new file mode 100644
index 0000000..3fbd51d
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java
@@ -0,0 +1,146 @@
+/*
+ * 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 com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.allOf;
+import static com.google.errorprone.matchers.Matchers.contains;
+import static com.google.errorprone.matchers.Matchers.kindIs;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+import static com.google.errorprone.matchers.Matchers.not;
+import static com.google.errorprone.matchers.Matchers.staticMethod;
+
+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.MethodInvocationTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.predicates.TypePredicate;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+import com.sun.tools.javac.code.Type;
+
+import java.util.List;
+
+/**
+ * Android offers several efficient alternatives to some upstream {@link String}
+ * operations.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkEfficientStrings",
+ summary = "Verifies efficient Strings best-practices",
+ severity = WARNING)
+public final class EfficientStringsChecker extends BugChecker
+ implements MethodInvocationTreeMatcher {
+
+ private static final Matcher<ExpressionTree> FORMAT_CALL = methodInvocation(
+ staticMethod().onClass("java.lang.String").named("format"));
+ private static final Matcher<ExpressionTree> PRECONDITIONS_CALL = methodInvocation(
+ staticMethod().onClass(withSimpleName("Preconditions")).withAnyName());
+ private static final Matcher<ExpressionTree> OBJECTS_CALL = methodInvocation(
+ staticMethod().onClass("java.util.Objects").named("requireNonNull"));
+
+ /**
+ * Match an expression which combines both string literals any other dynamic
+ * values, since these allocate a transparent StringBuilder.
+ * <p>
+ * This won't match a single isolated string literal, or a chain consisting
+ * of only string literals, since those don't require dynamic construction.
+ */
+ private static final Matcher<ExpressionTree> CONTAINS_DYNAMIC_STRING = allOf(
+ contains(ExpressionTree.class, kindIs(Kind.STRING_LITERAL)),
+ contains(ExpressionTree.class, not(kindIs(Kind.STRING_LITERAL))));
+
+ @Override
+ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+ if (FORMAT_CALL.matches(tree, state)) {
+ // Skip over possible locale to find format string
+ final List<? extends ExpressionTree> args = tree.getArguments();
+ final ExpressionTree formatArg;
+ final List<VarSymbol> vars = ASTHelpers.getSymbol(tree).params();
+ if (vars.get(0).type.toString().equals("java.util.Locale")) {
+ formatArg = args.get(1);
+ } else {
+ formatArg = args.get(0);
+ }
+
+ // Determine if format string is "simple" enough to replace
+ if (formatArg.getKind() == Kind.STRING_LITERAL) {
+ final String format = String.valueOf(((LiteralTree) formatArg).getValue());
+ if (isSimple(format)) {
+ return buildDescription(formatArg)
+ .setMessage("Simple format strings can be replaced with "
+ + "TextUtils.formatSimple() for a 6x performance improvement")
+ .build();
+ }
+ }
+ } else if (PRECONDITIONS_CALL.matches(tree, state)
+ || OBJECTS_CALL.matches(tree, state)) {
+ final List<? extends ExpressionTree> args = tree.getArguments();
+ for (int i = 1 ; i < args.size(); i++) {
+ final ExpressionTree arg = args.get(i);
+ if (CONTAINS_DYNAMIC_STRING.matches(arg, state)) {
+ return buildDescription(arg)
+ .setMessage("Building dynamic messages is discouraged, since they "
+ + "always allocate a transparent StringBuilder, even in "
+ + "the successful case")
+ .build();
+ }
+ }
+ }
+ return Description.NO_MATCH;
+ }
+
+ static boolean isSimple(String format) {
+ for (int i = 0; i < format.length(); i++) {
+ char c = format.charAt(i);
+ if (c == '%') {
+ i++;
+ c = format.charAt(i);
+ switch (c) {
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'f':
+ case 's':
+ case 'x':
+ case '%':
+ break;
+ default:
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ static TypePredicate withSimpleName(final String filter) {
+ return new TypePredicate() {
+ @Override
+ public boolean apply(Type type, VisitorState state) {
+ return type.tsym.getSimpleName().toString().equals(filter);
+ }
+ };
+ }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java
new file mode 100644
index 0000000..a755564
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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 junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class EfficientStringsCheckerTest {
+ private CompilationTestHelper compilationHelper;
+
+ @Before
+ public void setUp() {
+ compilationHelper = CompilationTestHelper.newInstance(
+ EfficientStringsChecker.class, getClass());
+ }
+
+ @Test
+ public void testSimple() {
+ assertTrue(EfficientStringsChecker.isSimple(""));
+ assertTrue(EfficientStringsChecker.isSimple("%s"));
+ assertTrue(EfficientStringsChecker.isSimple("String %s%s and %%%% number %d%d together"));
+
+ assertFalse(EfficientStringsChecker.isSimple("%04d"));
+ assertFalse(EfficientStringsChecker.isSimple("%02x:%02x:%02x"));
+ }
+
+ @Test
+ public void testFormat() {
+ compilationHelper
+ .addSourceLines("Example.java",
+ "import java.util.Locale;",
+ "public class Example {",
+ " public void example(String str) {",
+ " String.format(str, str);",
+ " // BUG: Diagnostic contains:",
+ " String.format(\"foo %s bar\", str);",
+ " // BUG: Diagnostic contains:",
+ " String.format(\"foo %d bar\", 42);",
+ " String.format(\"foo %04d bar\", 42);",
+ " }",
+ " public void exampleLocale(String str) {",
+ " String.format(Locale.US, str, str);",
+ " // BUG: Diagnostic contains:",
+ " String.format(Locale.US, \"foo %s bar\", str);",
+ " // BUG: Diagnostic contains:",
+ " String.format(Locale.US, \"foo %d bar\", 42);",
+ " String.format(Locale.US, \"foo %04d bar\", 42);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testPreconditions() {
+ compilationHelper
+ .addSourceFile("/android/util/Preconditions.java")
+ .addSourceLines("Example.java",
+ "import android.util.Preconditions;",
+ "import java.util.Objects;",
+ "public class Example {",
+ " String str;",
+ " public void checkState(boolean val) {",
+ " Preconditions.checkState(val);",
+ " Preconditions.checkState(val, str);",
+ " Preconditions.checkState(val, \"foo\");",
+ " Preconditions.checkState(val, \"foo\" + \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " Preconditions.checkState(val, \"foo \" + val);",
+ " }",
+ " public void checkArgument(boolean val) {",
+ " Preconditions.checkArgument(val);",
+ " Preconditions.checkArgument(val, str);",
+ " Preconditions.checkArgument(val, \"foo\");",
+ " Preconditions.checkArgument(val, \"foo\" + \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " Preconditions.checkArgument(val, \"foo \" + val);",
+ " }",
+ " public void checkNotNull(Object val) {",
+ " Preconditions.checkNotNull(val);",
+ " Preconditions.checkNotNull(val, str);",
+ " Preconditions.checkNotNull(val, \"foo\");",
+ " Preconditions.checkNotNull(val, \"foo\" + \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " Preconditions.checkNotNull(val, \"foo \" + val);",
+ " }",
+ " public void requireNonNull(Object val) {",
+ " Objects.requireNonNull(val);",
+ " Objects.requireNonNull(val, str);",
+ " Objects.requireNonNull(val, \"foo\");",
+ " Objects.requireNonNull(val, \"foo\" + \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " Objects.requireNonNull(val, \"foo \" + val);",
+ " }",
+ "}")
+ .doTest();
+ }
+}
diff --git a/errorprone/tests/res/android/util/Preconditions.java b/errorprone/tests/res/android/util/Preconditions.java
new file mode 100644
index 0000000..558cdaf
--- /dev/null
+++ b/errorprone/tests/res/android/util/Preconditions.java
@@ -0,0 +1,43 @@
+/*
+ * 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.util;
+
+public class Preconditions {
+ public static void checkArgument(boolean expression) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static void checkArgument(boolean expression, Object errorMessage) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static <T> T checkNotNull(T reference) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static <T> T checkNotNull(T reference, Object errorMessage) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static void checkState(boolean expression) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static void checkState(boolean expression, String message) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 4c0f890..8284042 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -32,6 +32,7 @@
private static native Surface nativeGetSurface(long ptr);
private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height);
+ private static native void nativeFlushShadowQueue(long ptr);
/** Create a new connection with the surface flinger. */
public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
@@ -69,4 +70,8 @@
super.finalize();
}
}
+
+ public void flushShadowQueue() {
+ nativeFlushShadowQueue(mNativeObject);
+ }
}
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 05df250..54f9fa8 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -26,11 +26,13 @@
import android.graphics.Canvas.VertexMode;
import android.graphics.fonts.Font;
import android.graphics.text.MeasuredText;
+import android.graphics.text.TextRunShaper;
import android.text.GraphicsOperations;
import android.text.MeasuredParagraph;
import android.text.PrecomputedText;
import android.text.SpannableString;
import android.text.SpannedString;
+import android.text.TextShaper;
import android.text.TextUtils;
import com.android.internal.util.Preconditions;
@@ -471,8 +473,8 @@
* @param font Font used for drawing.
* @param paint Paint used for drawing. The typeface set to this paint is ignored.
*
- * @see android.graphics.text.TextShaper
- * @see android.text.StyledTextShaper
+ * @see TextRunShaper
+ * @see TextShaper
*/
public void drawGlyphs(
@NonNull int[] glyphIds,
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 5e07d15..829d0f4 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -26,7 +26,9 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.fonts.Font;
import android.graphics.text.MeasuredText;
+import android.graphics.text.TextRunShaper;
import android.os.Build;
+import android.text.TextShaper;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -2053,7 +2055,7 @@
* Draw array of glyphs with specified font.
*
* @param glyphIds Array of glyph IDs. The length of array must be greater than or equal to
- * {@code glyphStart + glyphCount}.
+ * {@code glyphIdOffset + glyphCount}.
* @param glyphIdOffset Number of elements to skip before drawing in <code>glyphIds</code>
* array.
* @param positions A flattened X and Y position array. The first glyph X position must be
@@ -2071,8 +2073,8 @@
* @param font Font used for drawing.
* @param paint Paint used for drawing. The typeface set to this paint is ignored.
*
- * @see android.graphics.text.TextShaper
- * @see android.text.StyledTextShaper
+ * @see TextRunShaper
+ * @see TextShaper
*/
public void drawGlyphs(
@NonNull int[] glyphIds,
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index f768bc7..0061ea1 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -40,7 +40,7 @@
*/
public final class FrameInfo {
- public long[] frameInfo = new long[10];
+ public long[] frameInfo = new long[FRAME_INFO_SIZE];
// Various flags set to provide extra metadata about the current frame
private static final int FLAGS = 0;
@@ -87,14 +87,22 @@
// When View:draw() started
private static final int DRAW_START = 9;
+ // When the frame needs to be ready by
+ private static final int FRAME_DEADLINE = 10;
+
+ // Must be the last one
+ private static final int FRAME_INFO_SIZE = FRAME_DEADLINE + 1;
+
/** checkstyle */
- public void setVsync(long intendedVsync, long usedVsync, long frameTimelineVsyncId) {
+ public void setVsync(long intendedVsync, long usedVsync, long frameTimelineVsyncId,
+ long frameDeadline) {
frameInfo[FRAME_TIMELINE_VSYNC_ID] = frameTimelineVsyncId;
frameInfo[INTENDED_VSYNC] = intendedVsync;
frameInfo[VSYNC] = usedVsync;
frameInfo[OLDEST_INPUT_EVENT] = Long.MAX_VALUE;
frameInfo[NEWEST_INPUT_EVENT] = 0;
frameInfo[FLAGS] = 0;
+ frameInfo[FRAME_DEADLINE] = frameDeadline;
}
/** checkstyle */
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index fd5916c..a7f2739 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -355,7 +355,7 @@
*/
public @NonNull FrameRenderRequest setVsyncTime(long vsyncTime) {
// TODO(b/168552873): populate vsync Id once available to Choreographer public API
- mFrameInfo.setVsync(vsyncTime, vsyncTime, FrameInfo.INVALID_VSYNC_ID);
+ mFrameInfo.setVsync(vsyncTime, vsyncTime, FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE);
mFrameInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS);
return this;
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 28d7911..a191fe5 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -115,6 +115,21 @@
Align.LEFT, Align.CENTER, Align.RIGHT
};
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ ANTI_ALIAS_FLAG,
+ FILTER_BITMAP_FLAG,
+ DITHER_FLAG,
+ UNDERLINE_TEXT_FLAG,
+ STRIKE_THRU_TEXT_FLAG,
+ FAKE_BOLD_TEXT_FLAG,
+ LINEAR_TEXT_FLAG,
+ SUBPIXEL_TEXT_FLAG,
+ EMBEDDED_BITMAP_TEXT_FLAG
+ })
+ public @interface PaintFlag{}
+
/**
* Paint flag that enables antialiasing when drawing.
*
@@ -724,7 +739,7 @@
*
* @return the paint's flags (see enums ending in _Flag for bit masks)
*/
- public int getFlags() {
+ public @PaintFlag int getFlags() {
return nGetFlags(mNativePaint);
}
@@ -733,7 +748,7 @@
*
* @param flags The new flag bits for the paint
*/
- public void setFlags(int flags) {
+ public void setFlags(@PaintFlag int flags) {
nSetFlags(mNativePaint, flags);
}
diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java
index d408ac3..3260849 100644
--- a/graphics/java/android/graphics/ParcelableColorSpace.java
+++ b/graphics/java/android/graphics/ParcelableColorSpace.java
@@ -73,6 +73,9 @@
}
}
+ /**
+ * @return the backing ColorSpace that this ParcelableColorSpace is wrapping.
+ */
public @NonNull ColorSpace getColorSpace() {
return mColorSpace;
}
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index d71ff11..7651d01 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -83,21 +83,22 @@
public enum TileMode {
/**
- * replicate the edge color if the shader draws outside of its
- * original bounds
+ * Replicate the edge color if the shader draws outside of its
+ * original bounds.
*/
CLAMP (0),
/**
- * repeat the shader's image horizontally and vertically
+ * Repeat the shader's image horizontally and vertically.
*/
REPEAT (1),
/**
- * repeat the shader's image horizontally and vertically, alternating
- * mirror images so that adjacent images always seam
+ * Repeat the shader's image horizontally and vertically, alternating
+ * mirror images so that adjacent images always seam.
*/
MIRROR(2),
/**
- * Only draw within the original domain, return transparent-black everywhere else
+ * Render the shader's image pixels only within its original bounds. If the shader
+ * draws outside of its original bounds, transparent black is drawn instead.
*/
DECAL(3);
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 21b8fc6..97cd8ab 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -687,7 +687,9 @@
charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32);
axes[i] = new FontVariationAxis(new String(charBuffer), value);
}
- Font.Builder builder = new Font.Builder(buffer)
+ String path = nGetFontPath(ptr);
+ File file = (path == null) ? null : new File(path);
+ Font.Builder builder = new Font.Builder(buffer, file, "")
.setWeight(weight)
.setSlant(italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT)
.setTtcIndex(ttcIndex)
@@ -712,6 +714,9 @@
private static native long nGetAxisInfo(long ptr, int i);
@FastNative
+ private static native String nGetFontPath(long ptr);
+
+ @FastNative
private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);
@FastNative
diff --git a/graphics/java/android/graphics/text/GlyphStyle.java b/graphics/java/android/graphics/text/GlyphStyle.java
deleted file mode 100644
index cc8c4d2..0000000
--- a/graphics/java/android/graphics/text/GlyphStyle.java
+++ /dev/null
@@ -1,234 +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.graphics.text;
-
-import android.annotation.ColorInt;
-import android.annotation.FloatRange;
-import android.annotation.NonNull;
-import android.graphics.Paint;
-
-import java.util.Objects;
-
-/**
- * Represents subset of Paint parameters such as font size, scaleX that is used to draw a glyph.
- *
- * Glyph is a most primitive unit of text drawing.
- *
- */
-public class GlyphStyle {
- private @ColorInt int mColor;
- private float mFontSize;
- private float mScaleX;
- private float mSkewX;
- private int mFlags;
-
- /**
- * @param color a color.
- * @param fontSize a font size in pixels.
- * @param scaleX a horizontal scale factor.
- * @param skewX a horizontal skew factor
- * @param flags paint flags
- *
- * @see Paint#getFlags()
- * @see Paint#setFlags(int)
- */
- public GlyphStyle(
- @ColorInt int color,
- @FloatRange(from = 0) float fontSize,
- @FloatRange(from = 0) float scaleX,
- @FloatRange(from = 0) float skewX,
- int flags) {
- mColor = color;
- mFontSize = fontSize;
- mScaleX = scaleX;
- mSkewX = skewX;
- mFlags = flags;
- }
-
- /**
- * Create glyph style from Paint
- *
- * @param paint a paint
- */
- public GlyphStyle(@NonNull Paint paint) {
- setFromPaint(paint);
- }
-
- /**
- * Gets the color.
- *
- * @return a color
- * @see Paint#getColor()
- * @see Paint#setColor(int)
- */
- public @ColorInt int getColor() {
- return mColor;
- }
-
- /**
- * Sets the color.
- *
- * @param color a color
- * @see Paint#getColor()
- * @see Paint#setColor(int)
- */
- public void setColor(@ColorInt int color) {
- mColor = color;
- }
-
- /**
- * Gets the font size in pixels.
- *
- * @return font size
- * @see Paint#getTextSize()
- * @see Paint#setTextSize(float)
- */
- public @FloatRange(from = 0) float getFontSize() {
- return mFontSize;
- }
-
- /**
- * Sets the font size in pixels.
- *
- * @param fontSize font size in pixel
- * @see Paint#getTextSize()
- * @see Paint#setTextSize(float)
- */
- public void setFontSize(@FloatRange(from = 0) float fontSize) {
- mFontSize = fontSize;
- }
-
- /**
- * Return the horizontal scale factor
- *
- * @return a horizontal scale factor
- * @see Paint#getTextScaleX()
- * @see Paint#setTextScaleX(float)
- */
- public @FloatRange(from = 0) float getScaleX() {
- return mScaleX;
- }
-
- /**
- * Set the horizontal scale factor
- *
- * @param scaleX a horizontal scale factor
- * @see Paint#getTextScaleX()
- * @see Paint#setTextScaleX(float)
- */
- public void setScaleX(@FloatRange(from = 0) float scaleX) {
- mScaleX = scaleX;
- }
-
- /**
- * Return the horizontal skew factor
- *
- * @return a horizontal skew factor
- * @see Paint#getTextSkewX()
- * @see Paint#setTextSkewX(float)
- */
- public @FloatRange(from = 0) float getSkewX() {
- return mSkewX;
- }
-
- /**
- * Set the horizontal skew factor
- *
- * @param skewX a horizontal skew factor
- * @see Paint#getTextSkewX()
- * @see Paint#setTextSkewX(float)
- */
- public void setSkewX(@FloatRange(from = 0) float skewX) {
- mSkewX = skewX;
- }
-
- /**
- * Returns the Paint flags.
- *
- * @return a paint flags
- * @see Paint#getFlags()
- * @see Paint#setFlags(int)
- */
- public int getFlags() {
- return mFlags;
- }
-
- /**
- * Set the Paint flags.
- *
- * @param flags a paint flags
- * @see Paint#getFlags()
- * @see Paint#setFlags(int)
- */
- public void setFlags(int flags) {
- mFlags = flags;
- }
-
- /**
- * Applies glyph style to the paint object.
- *
- * @param paint a paint object
- */
- public void applyToPaint(@NonNull Paint paint) {
- paint.setColor(mColor);
- paint.setTextSize(mFontSize);
- paint.setTextScaleX(mScaleX);
- paint.setTextSkewX(mSkewX);
- paint.setFlags(mFlags);
- }
-
- /**
- * Copy parameters from a Paint object.
- *
- * @param paint a paint object
- */
- public void setFromPaint(@NonNull Paint paint) {
- mColor = paint.getColor();
- mFontSize = paint.getTextSize();
- mScaleX = paint.getTextScaleX();
- mSkewX = paint.getTextSkewX();
- mFlags = paint.getFlags();
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof GlyphStyle)) return false;
- GlyphStyle that = (GlyphStyle) o;
- return that.mColor == mColor
- && Float.compare(that.mFontSize, mFontSize) == 0
- && Float.compare(that.mScaleX, mScaleX) == 0
- && Float.compare(that.mSkewX, mSkewX) == 0
- && mFlags == that.mFlags;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mColor, mFontSize, mScaleX, mSkewX, mFlags);
- }
-
- @Override
- public String toString() {
- return "GlyphStyle{"
- + "mColor=" + mColor
- + ", mFontSize=" + mFontSize
- + ", mScaleX=" + mScaleX
- + ", mSkewX=" + mSkewX
- + ", mFlags=" + mFlags
- + '}';
- }
-}
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index 7364d54..ecbc45c 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -35,11 +35,12 @@
* Text shaping result object for single style text.
*
* You can get text shaping result by
- * {@link TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)} and
- * {@link TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)}.
+ * {@link TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)} and
+ * {@link TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean,
+ * Paint)}.
*
- * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
- * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
+ * @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
+ * @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
*/
public final class PositionedGlyphs {
private static final NativeAllocationRegistry REGISTRY =
@@ -49,7 +50,6 @@
private final long mLayoutPtr;
private final float mXOffset;
private final float mYOffset;
- private final GlyphStyle mGlyphStyle;
private final ArrayList<Font> mFonts;
/**
@@ -62,7 +62,7 @@
*
* @return total amount of advance
*/
- public float getTotalAdvance() {
+ public float getAdvance() {
return nGetTotalAdvance(mLayoutPtr);
}
@@ -91,21 +91,11 @@
}
/**
- * Returns the glyph style used for drawing the glyph at the given index.
- *
- * @return A glyph style
- */
- @NonNull
- public GlyphStyle getStyle() {
- return mGlyphStyle;
- }
-
- /**
* Returns the amount of X offset added to glyph position.
*
* @return The X offset added to glyph position.
*/
- public float getOriginX() {
+ public float getOffsetX() {
return mXOffset;
}
@@ -114,7 +104,7 @@
*
* @return The Y offset added to glyph position.
*/
- public float getOriginY() {
+ public float getOffsetY() {
return mYOffset;
}
@@ -144,7 +134,7 @@
* Returns the glyph ID used for drawing the glyph at the given index.
*
* @param index the glyph index
- * @return A font object
+ * @return An glyph ID of the font.
*/
@IntRange(from = 0)
public int getGlyphId(@IntRange(from = 0) int index) {
@@ -158,7 +148,7 @@
* @param index the glyph index
* @return A X offset in pixels
*/
- public float getPositionX(@IntRange(from = 0) int index) {
+ public float getGlyphX(@IntRange(from = 0) int index) {
Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
return nGetX(mLayoutPtr, index) + mXOffset;
}
@@ -169,7 +159,7 @@
* @param index the glyph index
* @return A Y offset in pixels.
*/
- public float getPositionY(@IntRange(from = 0) int index) {
+ public float getGlyphY(@IntRange(from = 0) int index) {
Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
return nGetY(mLayoutPtr, index) + mYOffset;
}
@@ -184,7 +174,6 @@
*/
public PositionedGlyphs(long layoutPtr, @NonNull Paint paint, float xOffset, float yOffset) {
mLayoutPtr = layoutPtr;
- mGlyphStyle = new GlyphStyle(paint);
int glyphCount = nGetGlyphCount(layoutPtr);
mFonts = new ArrayList<>(glyphCount);
mXOffset = xOffset;
@@ -229,14 +218,13 @@
if (!(o instanceof PositionedGlyphs)) return false;
PositionedGlyphs that = (PositionedGlyphs) o;
- if (!mGlyphStyle.equals(that.mGlyphStyle)) return false;
if (mXOffset != that.mXOffset || mYOffset != that.mYOffset) return false;
if (glyphCount() != that.glyphCount()) return false;
for (int i = 0; i < glyphCount(); ++i) {
if (getGlyphId(i) != that.getGlyphId(i)) return false;
- if (getPositionX(i) != that.getPositionX(i)) return false;
- if (getPositionY(i) != that.getPositionY(i)) return false;
+ if (getGlyphX(i) != that.getGlyphX(i)) return false;
+ if (getGlyphY(i) != that.getGlyphY(i)) return false;
// Intentionally using reference equality since font equality is heavy due to buffer
// compare.
if (getFont(i) != that.getFont(i)) return false;
@@ -247,10 +235,10 @@
@Override
public int hashCode() {
- int hashCode = Objects.hash(mXOffset, mYOffset, mGlyphStyle);
+ int hashCode = Objects.hash(mXOffset, mYOffset);
for (int i = 0; i < glyphCount(); ++i) {
hashCode = Objects.hash(hashCode,
- getGlyphId(i), getPositionX(i), getPositionY(i), getFont(i));
+ getGlyphId(i), getGlyphX(i), getGlyphY(i), getFont(i));
}
return hashCode;
}
@@ -263,7 +251,7 @@
sb.append(", ");
}
sb.append("[ ID = " + getGlyphId(i) + ","
- + " pos = (" + getPositionX(i) + "," + getPositionY(i) + ")"
+ + " pos = (" + getGlyphX(i) + "," + getGlyphY(i) + ")"
+ " font = " + getFont(i) + " ]");
}
sb.append("]");
@@ -271,7 +259,6 @@
+ "glyphs = " + sb.toString()
+ ", mXOffset=" + mXOffset
+ ", mYOffset=" + mYOffset
- + ", mGlyphStyle=" + mGlyphStyle
+ '}';
}
}
diff --git a/graphics/java/android/graphics/text/TextShaper.java b/graphics/java/android/graphics/text/TextRunShaper.java
similarity index 88%
rename from graphics/java/android/graphics/text/TextShaper.java
rename to graphics/java/android/graphics/text/TextRunShaper.java
index f40ed8f..b73436e 100644
--- a/graphics/java/android/graphics/text/TextShaper.java
+++ b/graphics/java/android/graphics/text/TextRunShaper.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.graphics.Paint;
import android.text.TextDirectionHeuristic;
+import android.text.TextPaint;
import android.text.TextUtils;
import com.android.internal.util.Preconditions;
@@ -31,15 +32,18 @@
* Text shaping is a preprocess for drawing text into canvas with glyphs. The glyph is a most
* primitive unit of the text drawing, consist of glyph identifier in the font file and its position
* and style. You can draw the shape result to Canvas by calling Canvas#drawGlyphs.
-
*
- * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
- * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
- * @see android.text.StyledTextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic,
- * TextPaint)
+ * For most of the use cases, {@link android.text.TextShaper} will provide text shaping
+ * functionalities needed. {@link TextRunShaper} is a lower level API that is used by
+ * {@link android.text.TextShaper}.
+ *
+ * @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
+ * @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
+ * @see android.text.TextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint,
+ * TextShaper.GlyphsConsumer)
*/
-public class TextShaper {
- private TextShaper() {} // Do not instantiate
+public class TextRunShaper {
+ private TextRunShaper() {} // Do not instantiate
/**
* Shape non-styled text.
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
index bcef154..44744bc 100644
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -7,6 +7,12 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
},
+ "-1683614271": {
+ "message": "Existing task: id=%d component=%s",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+ },
"-1534364071": {
"message": "onTransitionReady %s: %s",
"level": "VERBOSE",
@@ -61,6 +67,12 @@
"group": "WM_SHELL_TASK_ORG",
"at": "com\/android\/wm\/shell\/FullscreenTaskListener.java"
},
+ "580605218": {
+ "message": "Registering organizer",
+ "level": "VERBOSE",
+ "group": "WM_SHELL_TASK_ORG",
+ "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+ },
"980952660": {
"message": "Task root back pressed taskId=%d",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 7ce65fd..d87de5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -28,12 +28,14 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration.WindowingMode;
import android.util.Log;
-import android.util.Pair;
import android.util.SparseArray;
import android.view.SurfaceControl;
import android.window.ITaskOrganizerController;
+import android.window.TaskAppearedInfo;
import android.window.TaskOrganizer;
+import androidx.annotation.NonNull;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
@@ -42,6 +44,7 @@
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.Arrays;
+import java.util.List;
/**
* Unified task organizer for all components in the shell.
@@ -82,7 +85,7 @@
// Keeps track of all the tasks reported to this organizer (changes in windowing mode will
// require us to report to both old and new listeners)
- private final SparseArray<Pair<RunningTaskInfo, SurfaceControl>> mTasks = new SparseArray<>();
+ private final SparseArray<TaskAppearedInfo> mTasks = new SparseArray<>();
// TODO(shell-transitions): move to a more "global" Shell location as this isn't only for Tasks
private final Transitions mTransitions;
@@ -102,6 +105,19 @@
if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
}
+ @Override
+ public List<TaskAppearedInfo> registerOrganizer() {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Registering organizer");
+ final List<TaskAppearedInfo> taskInfos = super.registerOrganizer();
+ for (int i = 0; i < taskInfos.size(); i++) {
+ final TaskAppearedInfo info = taskInfos.get(i);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Existing task: id=%d component=%s",
+ info.getTaskInfo().taskId, info.getTaskInfo().baseIntent);
+ onTaskAppeared(info.getTaskInfo(), info.getLeash());
+ }
+ return taskInfos;
+ }
+
/**
* Adds a listener for tasks with given types.
*/
@@ -117,10 +133,11 @@
// Notify the listener of all existing tasks with the given type.
for (int i = mTasks.size() - 1; i >= 0; i--) {
- Pair<RunningTaskInfo, SurfaceControl> data = mTasks.valueAt(i);
- final @TaskListenerType int taskListenerType = getTaskListenerType(data.first);
+ TaskAppearedInfo data = mTasks.valueAt(i);
+ final @TaskListenerType int taskListenerType = getTaskListenerType(
+ data.getTaskInfo());
if (taskListenerType == listenerType) {
- listener.onTaskAppeared(data.first, data.second);
+ listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
}
}
}
@@ -143,7 +160,7 @@
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task appeared taskId=%d",
taskInfo.taskId);
- mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, leash));
+ mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, leash));
final TaskListener listener = mTaskListenersByType.get(getTaskListenerType(taskInfo));
if (listener != null) {
listener.onTaskAppeared(taskInfo, leash);
@@ -154,10 +171,10 @@
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task info changed taskId=%d",
taskInfo.taskId);
- final Pair<RunningTaskInfo, SurfaceControl> data = mTasks.get(taskInfo.taskId);
+ final TaskAppearedInfo data = mTasks.get(taskInfo.taskId);
final @TaskListenerType int listenerType = getTaskListenerType(taskInfo);
- final @TaskListenerType int prevListenerType = getTaskListenerType(data.first);
- mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, data.second));
+ final @TaskListenerType int prevListenerType = getTaskListenerType(data.getTaskInfo());
+ mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash()));
if (prevListenerType != listenerType) {
// TODO: We currently send vanished/appeared as the task moves between types, but
// we should consider adding a different mode-changed callback
@@ -167,7 +184,7 @@
}
listener = mTaskListenersByType.get(listenerType);
if (listener != null) {
- SurfaceControl leash = data.second;
+ SurfaceControl leash = data.getLeash();
listener.onTaskAppeared(taskInfo, leash);
}
} else {
@@ -193,7 +210,7 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task vanished taskId=%d",
taskInfo.taskId);
final @TaskListenerType int prevListenerType =
- getTaskListenerType(mTasks.get(taskInfo.taskId).first);
+ getTaskListenerType(mTasks.get(taskInfo.taskId).getTaskInfo());
mTasks.remove(taskInfo.taskId);
final TaskListener listener = mTaskListenersByType.get(prevListenerType);
if (listener != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 84b98f9..17fd16b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -276,12 +276,11 @@
long frameNumber, ClientWindowFrames outFrames,
MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
- SurfaceControl outBLASTSurfaceControl) {
+ InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
int res = super.relayout(window, attrs, requestedWidth, requestedHeight,
viewVisibility, flags, frameNumber, outFrames,
mergedConfiguration, outSurfaceControl, outInsetsState,
- outActiveControls, outSurfaceSize, outBLASTSurfaceControl);
+ outActiveControls, outSurfaceSize);
if (res != 0) {
return res;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
new file mode 100644
index 0000000..10e5c3d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+
+import java.io.PrintWriter;
+
+/**
+ * Singleton source of truth for the current state of PIP bounds.
+ */
+public final class PipBoundsState {
+ private static final String TAG = PipBoundsState.class.getSimpleName();
+
+ private final @NonNull Rect mBounds = new Rect();
+
+ void setBounds(@NonNull Rect bounds) {
+ mBounds.set(bounds);
+ }
+
+ @NonNull
+ public Rect getBounds() {
+ return new Rect(mBounds);
+ }
+
+ /**
+ * Dumps internal state.
+ */
+ public void dump(PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + TAG);
+ pw.println(innerPrefix + "mBounds=" + mBounds);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 846da0a..15fd424 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -67,7 +67,6 @@
import com.android.wm.shell.pip.phone.PipMenuActivityController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipUpdateThread;
-import com.android.wm.shell.pip.phone.PipUtils;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.PrintWriter;
@@ -134,11 +133,11 @@
private final Handler mMainHandler;
private final Handler mUpdateHandler;
+ private final PipBoundsState mPipBoundsState;
private final PipBoundsHandler mPipBoundsHandler;
private final PipAnimationController mPipAnimationController;
private final PipUiEventLogger mPipUiEventLoggerLogger;
private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
- private final Rect mLastReportedBounds = new Rect();
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
@@ -262,7 +261,8 @@
*/
private boolean mShouldIgnoreEnteringPipTransition;
- public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler,
+ public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState,
+ @NonNull PipBoundsHandler boundsHandler,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
Optional<SplitScreen> splitScreenOptional,
@NonNull DisplayController displayController,
@@ -270,6 +270,7 @@
@NonNull ShellTaskOrganizer shellTaskOrganizer) {
mMainHandler = new Handler(Looper.getMainLooper());
mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
+ mPipBoundsState = pipBoundsState;
mPipBoundsHandler = boundsHandler;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
@@ -279,30 +280,21 @@
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer;
-
- if (!PipUtils.hasSystemFeature(context)) {
- Log.w(TAG, "Device not support PIP feature");
- } else {
- mTaskOrganizer.addListener(this, TASK_LISTENER_TYPE_PIP);
- displayController.addDisplayWindowListener(this);
- }
+ mTaskOrganizer.addListener(this, TASK_LISTENER_TYPE_PIP);
+ displayController.addDisplayWindowListener(this);
}
public Handler getUpdateHandler() {
return mUpdateHandler;
}
- public Rect getLastReportedBounds() {
- return new Rect(mLastReportedBounds);
- }
-
public Rect getCurrentOrAnimatingBounds() {
PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getCurrentAnimator();
if (animator != null && animator.isRunning()) {
return new Rect(animator.getDestinationBounds());
}
- return getLastReportedBounds();
+ return mPipBoundsState.getBounds();
}
public boolean isInPip() {
@@ -347,7 +339,7 @@
* Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards.
*/
public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
- mLastReportedBounds.set(destinationBounds);
+ mPipBoundsState.setBounds(destinationBounds);
}
/**
@@ -394,7 +386,7 @@
final SurfaceControl.Transaction tx =
mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
- mLastReportedBounds);
+ mPipBoundsState.getBounds());
tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
// We set to fullscreen here for now, but later it will be set to UNDEFINED for
// the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
@@ -408,9 +400,9 @@
@Override
public void onTransactionReady(int id, SurfaceControl.Transaction t) {
t.apply();
- scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
- getValidSourceHintRect(mTaskInfo, destinationBounds), direction,
- animationDurationMs, null /* updateBoundsCallback */);
+ scheduleAnimateResizePip(mPipBoundsState.getBounds(),
+ destinationBounds, getValidSourceHintRect(mTaskInfo, destinationBounds),
+ direction, animationDurationMs, null /* updateBoundsCallback */);
mState = State.EXITING_PIP;
}
});
@@ -441,7 +433,7 @@
// removePipImmediately is expected when the following animation finishes.
mUpdateHandler.post(() -> mPipAnimationController
- .getAnimator(mLeash, mLastReportedBounds, 1f, 0f)
+ .getAnimator(mLeash, mPipBoundsState.getBounds(), 1f, 0f)
.setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
@@ -480,7 +472,7 @@
if (mShouldIgnoreEnteringPipTransition) {
// Animation has been finished together with Recents, directly apply the sync
// transaction to PiP here.
- applyEnterPipSyncTransaction(mLastReportedBounds, () -> {
+ applyEnterPipSyncTransaction(mPipBoundsState.getBounds(), () -> {
mState = State.ENTERED_PIP;
});
mShouldIgnoreEnteringPipTransition = false;
@@ -572,7 +564,7 @@
private void sendOnPipTransitionStarted(
@PipAnimationController.TransitionDirection int direction) {
- final Rect pipBounds = new Rect(mLastReportedBounds);
+ final Rect pipBounds = mPipBoundsState.getBounds();
runOnMainHandler(() -> {
for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
@@ -701,7 +693,7 @@
}
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
info.topActivity, getAspectRatioOrDefault(newParams),
- mLastReportedBounds, getMinimalSize(info.topActivityInfo),
+ mPipBoundsState.getBounds(), getMinimalSize(info.topActivityInfo),
true /* userCurrentMinEdgeSize */);
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration,
@@ -759,7 +751,7 @@
sendOnPipTransitionCancelled(direction);
sendOnPipTransitionFinished(direction);
}
- mLastReportedBounds.set(destinationBoundsOut);
+ mPipBoundsState.setBounds(destinationBoundsOut);
// Create a reset surface transaction for the new bounds and update the window
// container transaction
@@ -774,8 +766,8 @@
destinationBoundsOut.set(animator.getDestinationBounds());
}
} else {
- if (!mLastReportedBounds.isEmpty()) {
- destinationBoundsOut.set(mLastReportedBounds);
+ if (!mPipBoundsState.getBounds().isEmpty()) {
+ destinationBoundsOut.set(mPipBoundsState.getBounds());
}
}
}
@@ -827,7 +819,7 @@
Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
return;
}
- scheduleAnimateResizePip(mLastReportedBounds, toBounds, null /* sourceHintRect */,
+ scheduleAnimateResizePip(mPipBoundsState.getBounds(), toBounds, null /* sourceHintRect */,
TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
}
@@ -963,7 +955,7 @@
Log.w(TAG, "Abort animation, invalid leash");
return;
}
- mLastReportedBounds.set(destinationBounds);
+ mPipBoundsState.setBounds(destinationBounds);
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper
.crop(tx, mLeash, destinationBounds)
@@ -999,7 +991,7 @@
throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
+ "directly");
}
- mLastReportedBounds.set(destinationBounds);
+ mPipBoundsState.setBounds(destinationBounds);
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
removePipImmediately();
return;
@@ -1141,7 +1133,6 @@
pw.println(innerPrefix + "mState=" + mState);
pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
- pw.println(innerPrefix + "mLastReportedBounds=" + mLastReportedBounds);
pw.println(innerPrefix + "mInitialState:");
for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) {
pw.println(innerPrefix + " binder=" + e.getKey()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index fddd547..18b6922 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.pip.phone;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.Region;
@@ -28,6 +29,7 @@
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import com.android.wm.shell.R;
+import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -50,6 +52,7 @@
private Context mContext;
private Handler mHandler;
+ private final @NonNull PipBoundsState mPipBoundsState;
private PipMotionHelper mMotionHelper;
private PipTaskOrganizer mTaskOrganizer;
private PipSnapAlgorithm mSnapAlgorithm;
@@ -62,12 +65,14 @@
private final Rect mExpandedMovementBounds = new Rect();
private Rect mTmpBounds = new Rect();
- public PipAccessibilityInteractionConnection(Context context, PipMotionHelper motionHelper,
+ public PipAccessibilityInteractionConnection(Context context,
+ @NonNull PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
PipTaskOrganizer taskOrganizer, PipSnapAlgorithm snapAlgorithm,
AccessibilityCallbacks callbacks, Runnable updateMovementBoundCallback,
Handler handler) {
mContext = context;
mHandler = handler;
+ mPipBoundsState = pipBoundsState;
mMotionHelper = motionHelper;
mTaskOrganizer = taskOrganizer;
mSnapAlgorithm = snapAlgorithm;
@@ -148,7 +153,7 @@
private void setToExpandedBounds() {
float savedSnapFraction = mSnapAlgorithm.getSnapFraction(
- new Rect(mTaskOrganizer.getLastReportedBounds()), mNormalMovementBounds);
+ mPipBoundsState.getBounds(), mNormalMovementBounds);
mSnapAlgorithm.applySnapFraction(mExpandedBounds, mExpandedMovementBounds,
savedSnapFraction);
mTaskOrganizer.scheduleFinishResizePip(mExpandedBounds, (Rect bounds) -> {
@@ -159,7 +164,7 @@
private void setToNormalBounds() {
float savedSnapFraction = mSnapAlgorithm.getSnapFraction(
- new Rect(mTaskOrganizer.getLastReportedBounds()), mExpandedMovementBounds);
+ mPipBoundsState.getBounds(), mExpandedMovementBounds);
mSnapAlgorithm.applySnapFraction(mNormalBounds, mNormalMovementBounds, savedSnapFraction);
mTaskOrganizer.scheduleFinishResizePip(mNormalBounds, (Rect bounds) -> {
mMotionHelper.synchronizePinnedStackBounds();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 41c0a88..13f5ac3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -17,10 +17,10 @@
package com.android.wm.shell.pip.phone;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.app.RemoteAction;
@@ -33,17 +33,21 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.util.Log;
+import android.util.Slog;
import android.view.DisplayInfo;
import android.view.IPinnedStackController;
import android.window.WindowContainerTransaction;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
import java.io.PrintWriter;
@@ -66,6 +70,7 @@
private DisplayController mDisplayController;
private PipAppOpsListener mAppOpsListener;
private PipBoundsHandler mPipBoundsHandler;
+ private @NonNull PipBoundsState mPipBoundsState;
private PipMediaController mMediaController;
private PipTouchHandler mTouchHandler;
private Consumer<Boolean> mPinnedStackAnimationRecentsCallback;
@@ -98,7 +103,7 @@
// If the pip was in the offset zone earlier, adjust the new bounds to the bottom of the
// movement bounds
mTouchHandler.adjustBoundsForRotation(mTmpNormalBounds,
- mPipTaskOrganizer.getLastReportedBounds(), mTmpInsetBounds);
+ mPipBoundsState.getBounds(), mTmpInsetBounds);
// The bounds are being applied to a specific snap fraction, so reset any known offsets
// for the previous orientation before updating the movement bounds.
@@ -193,46 +198,28 @@
}
}
- public PipController(Context context,
+ protected PipController(Context context,
DisplayController displayController,
PipAppOpsListener pipAppOpsListener,
PipBoundsHandler pipBoundsHandler,
+ @NonNull PipBoundsState pipBoundsState,
PipMediaController pipMediaController,
PipMenuActivityController pipMenuActivityController,
PipTaskOrganizer pipTaskOrganizer,
PipTouchHandler pipTouchHandler,
WindowManagerShellWrapper windowManagerShellWrapper
) {
- mContext = context;
-
- if (PipUtils.hasSystemFeature(mContext)) {
- initController(context, displayController, pipAppOpsListener, pipBoundsHandler,
- pipMediaController, pipMenuActivityController, pipTaskOrganizer,
- pipTouchHandler, windowManagerShellWrapper);
- } else {
- Log.w(TAG, "Device not support PIP feature");
- }
- }
-
- private void initController(Context context,
- DisplayController displayController,
- PipAppOpsListener pipAppOpsListener,
- PipBoundsHandler pipBoundsHandler,
- PipMediaController pipMediaController,
- PipMenuActivityController pipMenuActivityController,
- PipTaskOrganizer pipTaskOrganizer,
- PipTouchHandler pipTouchHandler,
- WindowManagerShellWrapper windowManagerShellWrapper) {
-
// Ensure that we are the primary user's SystemUI.
final int processUser = UserManager.get(context).getUserHandle();
if (processUser != UserHandle.USER_SYSTEM) {
throw new IllegalStateException("Non-primary Pip component not currently supported.");
}
+ mContext = context;
mWindowManagerShellWrapper = windowManagerShellWrapper;
mDisplayController = displayController;
mPipBoundsHandler = pipBoundsHandler;
+ mPipBoundsState = pipBoundsState;
mPipTaskOrganizer = pipTaskOrganizer;
mPipTaskOrganizer.registerPipTransitionCallback(this);
mMediaController = pipMediaController;
@@ -252,7 +239,7 @@
mWindowManagerShellWrapper.addPinnedStackListener(
new PipControllerPinnedStackListener());
} catch (RemoteException e) {
- Log.e(TAG, "Failed to register pinned stack listener", e);
+ Slog.e(TAG, "Failed to register pinned stack listener", e);
}
}
@@ -361,7 +348,7 @@
final boolean changed = mPipBoundsHandler.setShelfHeight(visible, shelfHeight);
if (changed) {
mTouchHandler.onShelfVisibilityChanged(visible, shelfHeight);
- updateMovementBounds(mPipTaskOrganizer.getLastReportedBounds(),
+ updateMovementBounds(mPipBoundsState.getBounds(),
false /* fromRotation */, false /* fromImeAdjustment */,
true /* fromShelfAdjustment */, null /* windowContainerTransaction */);
}
@@ -457,5 +444,26 @@
mTouchHandler.dump(pw, innerPrefix);
mPipBoundsHandler.dump(pw, innerPrefix);
mPipTaskOrganizer.dump(pw, innerPrefix);
+ mPipBoundsState.dump(pw, innerPrefix);
+ }
+
+ /**
+ * Instantiates {@link PipController}, returns {@code null} if the feature not supported.
+ */
+ @Nullable
+ public static PipController create(Context context, DisplayController displayController,
+ PipAppOpsListener pipAppOpsListener, PipBoundsHandler pipBoundsHandler,
+ PipBoundsState pipBoundsState, PipMediaController pipMediaController,
+ PipMenuActivityController pipMenuActivityController,
+ PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler,
+ WindowManagerShellWrapper windowManagerShellWrapper) {
+ if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
+ Slog.w(TAG, "Device doesn't support Pip feature");
+ return null;
+ }
+
+ return new PipController(context, displayController, pipAppOpsListener, pipBoundsHandler,
+ pipBoundsState, pipMediaController, pipMenuActivityController,
+ pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index fe1d44c7..b5fa030 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -37,6 +37,7 @@
import com.android.wm.shell.animation.PhysicsAnimator;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
+import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -66,6 +67,7 @@
private final Context mContext;
private final PipTaskOrganizer mPipTaskOrganizer;
+ private final @NonNull PipBoundsState mPipBoundsState;
private PipMenuActivityController mMenuController;
private PipSnapAlgorithm mSnapAlgorithm;
@@ -178,11 +180,12 @@
public void onPipTransitionCanceled(ComponentName activity, int direction) {}
};
- public PipMotionHelper(Context context, PipTaskOrganizer pipTaskOrganizer,
- PipMenuActivityController menuController, PipSnapAlgorithm snapAlgorithm,
- FloatingContentCoordinator floatingContentCoordinator) {
+ public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState,
+ PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
+ PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator) {
mContext = context;
mPipTaskOrganizer = pipTaskOrganizer;
+ mPipBoundsState = pipBoundsState;
mMenuController = menuController;
mSnapAlgorithm = snapAlgorithm;
mFloatingContentCoordinator = floatingContentCoordinator;
@@ -220,7 +223,7 @@
*/
void synchronizePinnedStackBounds() {
cancelAnimations();
- mBounds.set(mPipTaskOrganizer.getLastReportedBounds());
+ mBounds.set(mPipBoundsState.getBounds());
mTemporaryBounds.setEmpty();
if (mPipTaskOrganizer.isInPip()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 07beb43..a2233e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -22,6 +22,7 @@
import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
+import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
@@ -48,6 +49,7 @@
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipUiEventLogger;
@@ -70,6 +72,7 @@
private final boolean mEnableResize;
private final Context mContext;
private final PipBoundsHandler mPipBoundsHandler;
+ private final @NonNull PipBoundsState mPipBoundsState;
private final PipUiEventLogger mPipUiEventLogger;
private final PipDismissTargetHandler mPipDismissTargetHandler;
@@ -161,6 +164,7 @@
public PipTouchHandler(Context context,
PipMenuActivityController menuController,
PipBoundsHandler pipBoundsHandler,
+ @NonNull PipBoundsState pipBoundsState,
PipTaskOrganizer pipTaskOrganizer,
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger) {
@@ -168,11 +172,12 @@
mContext = context;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mPipBoundsHandler = pipBoundsHandler;
+ mPipBoundsState = pipBoundsState;
mMenuController = menuController;
mMenuController.addListener(new PipMenuListener());
mGesture = new DefaultPipTouchGesture();
- mMotionHelper = new PipMotionHelper(mContext, pipTaskOrganizer, mMenuController,
- mPipBoundsHandler.getSnapAlgorithm(), floatingContentCoordinator);
+ mMotionHelper = new PipMotionHelper(mContext, pipBoundsState, pipTaskOrganizer,
+ mMenuController, mPipBoundsHandler.getSnapAlgorithm(), floatingContentCoordinator);
mPipResizeGestureHandler =
new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
pipTaskOrganizer, this::getMovementBounds,
@@ -189,8 +194,8 @@
reloadResources();
mFloatingContentCoordinator = floatingContentCoordinator;
- mConnection = new PipAccessibilityInteractionConnection(mContext, mMotionHelper,
- pipTaskOrganizer, mPipBoundsHandler.getSnapAlgorithm(),
+ mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
+ mMotionHelper, pipTaskOrganizer, mPipBoundsHandler.getSnapAlgorithm(),
this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler);
mPipUiEventLogger = pipUiEventLogger;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUtils.java
index 6a58ce0..bd2ba32 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUtils.java
@@ -18,7 +18,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
@@ -30,7 +29,6 @@
import android.util.Pair;
public class PipUtils {
-
private static final String TAG = "PipUtils";
/**
@@ -58,14 +56,4 @@
}
return new Pair<>(null, 0);
}
-
- /**
- * The util to check if device has PIP feature
- *
- * @param context application context
- * @return true if device has PIP feature, false otherwise.
- */
- public static boolean hasSystemFeature(Context context) {
- return context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index a0ce9da..f3dadfc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -26,7 +26,7 @@
public enum ShellProtoLogGroup implements IProtoLogGroup {
// NOTE: Since we enable these from the same WM ShellCommand, these names should not conflict
// with those in the framework ProtoLogGroup
- WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index f01fc51..5418a5b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -22,6 +22,10 @@
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -29,10 +33,12 @@
import static org.mockito.Mockito.verify;
import android.app.ActivityManager.RunningTaskInfo;
+import android.content.pm.ParceledListSlice;
import android.os.RemoteException;
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
+import android.window.TaskAppearedInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -93,8 +99,12 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mOrganizer = new ShellTaskOrganizer(mTaskOrganizerController, mSyncTransactionQueue,
- mTransactionPool, mTestExecutor, mTestExecutor);
+ try {
+ doReturn(ParceledListSlice.<TaskAppearedInfo>emptyList())
+ .when(mTaskOrganizerController).registerTaskOrganizer(any());
+ } catch (RemoteException e) {}
+ mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mSyncTransactionQueue,
+ mTransactionPool, mTestExecutor, mTestExecutor));
}
@Test
@@ -116,8 +126,29 @@
}
@Test
+ public void testRegisterWithExistingTasks() throws RemoteException {
+ // Setup some tasks
+ RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
+ RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_MULTI_WINDOW);
+ ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>();
+ taskInfos.add(new TaskAppearedInfo(task1, new SurfaceControl()));
+ taskInfos.add(new TaskAppearedInfo(task2, new SurfaceControl()));
+ doReturn(new ParceledListSlice(taskInfos))
+ .when(mTaskOrganizerController).registerTaskOrganizer(any());
+
+ // Register and expect the tasks to be stored
+ mOrganizer.registerOrganizer();
+
+ // Check that the tasks are next reported when the listener is added
+ TrackingTaskListener listener = new TrackingTaskListener();
+ mOrganizer.addListener(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+ assertTrue(listener.appeared.contains(task1));
+ assertTrue(listener.appeared.contains(task2));
+ }
+
+ @Test
public void testAppearedVanished() {
- RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+ RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
TrackingTaskListener listener = new TrackingTaskListener();
mOrganizer.addListener(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
mOrganizer.onTaskAppeared(taskInfo, null);
@@ -129,7 +160,7 @@
@Test
public void testAddListenerExistingTasks() {
- RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+ RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
mOrganizer.onTaskAppeared(taskInfo, null);
TrackingTaskListener listener = new TrackingTaskListener();
@@ -139,7 +170,7 @@
@Test
public void testWindowingModeChange() {
- RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+ RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
TrackingTaskListener mwListener = new TrackingTaskListener();
TrackingTaskListener pipListener = new TrackingTaskListener();
mOrganizer.addListener(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW);
@@ -148,14 +179,15 @@
assertTrue(mwListener.appeared.contains(taskInfo));
assertTrue(pipListener.appeared.isEmpty());
- taskInfo = createTaskInfo(WINDOWING_MODE_PINNED);
+ taskInfo = createTaskInfo(1, WINDOWING_MODE_PINNED);
mOrganizer.onTaskInfoChanged(taskInfo);
assertTrue(mwListener.vanished.contains(taskInfo));
assertTrue(pipListener.appeared.contains(taskInfo));
}
- private RunningTaskInfo createTaskInfo(int windowingMode) {
+ private RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
RunningTaskInfo taskInfo = new RunningTaskInfo();
+ taskInfo.taskId = taskId;
taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
return taskInfo;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 7d51886..255e749 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.pip;
+package com.android.wm.shell.pip;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
index f514b0b..d9e3148 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.pip;
+package com.android.wm.shell.pip;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index d305c64..a282a48 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -14,32 +14,30 @@
* limitations under the License.
*/
-package com.android.systemui.pip.phone;
+package com.android.wm.shell.pip.phone;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableContext;
import android.testing.TestableLooper;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTestCase;
-import com.android.wm.shell.pip.phone.PipAppOpsListener;
-import com.android.wm.shell.pip.phone.PipController;
-import com.android.wm.shell.pip.phone.PipMediaController;
-import com.android.wm.shell.pip.phone.PipTouchHandler;
import org.junit.Before;
import org.junit.Test;
@@ -54,47 +52,52 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class PipControllerTest extends PipTestCase {
- private com.android.wm.shell.pip.phone.PipController mPipController;
- private TestableContext mSpiedContext;
+ private PipController mPipController;
- @Mock private DisplayController mMockdDisplayController;
- @Mock private PackageManager mPackageManager;
- @Mock private com.android.wm.shell.pip.phone.PipMenuActivityController
- mMockPipMenuActivityController;
+ @Mock private DisplayController mMockDisplayController;
+ @Mock private PipMenuActivityController mMockPipMenuActivityController;
@Mock private PipAppOpsListener mMockPipAppOpsListener;
@Mock private PipBoundsHandler mMockPipBoundsHandler;
@Mock private PipMediaController mMockPipMediaController;
@Mock private PipTaskOrganizer mMockPipTaskOrganizer;
@Mock private PipTouchHandler mMockPipTouchHandler;
@Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper;
+ @Mock private PipBoundsState mMockPipBoundsState;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
-
- mSpiedContext = spy(mContext);
-
- when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
- when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
-
- mPipController = new PipController(mSpiedContext, mMockdDisplayController,
- mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipMediaController,
- mMockPipMenuActivityController, mMockPipTaskOrganizer, mMockPipTouchHandler,
- mMockWindowManagerShellWrapper);
+ mPipController = new PipController(mContext, mMockDisplayController,
+ mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState,
+ mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer,
+ mMockPipTouchHandler, mMockWindowManagerShellWrapper);
}
@Test
- public void testNonPipDevice_shouldNotRegisterPipTransitionCallback() {
- verify(mMockPipTaskOrganizer, never()).registerPipTransitionCallback(any());
+ public void instantiatePipController_registersPipTransitionCallback() {
+ verify(mMockPipTaskOrganizer).registerPipTransitionCallback(any());
}
@Test
- public void testNonPipDevice_shouldNotAddDisplayChangingController() {
- verify(mMockdDisplayController, never()).addDisplayChangingController(any());
+ public void instantiatePipController_addsDisplayChangingController() {
+ verify(mMockDisplayController).addDisplayChangingController(any());
}
@Test
- public void testNonPipDevice_shouldNotAddDisplayWindowListener() {
- verify(mMockdDisplayController, never()).addDisplayWindowListener(any());
+ public void instantiatePipController_addsDisplayWindowListener() {
+ verify(mMockDisplayController).addDisplayWindowListener(any());
+ }
+
+ @Test
+ public void createPip_notSupported_returnsNull() {
+ Context spyContext = spy(mContext);
+ PackageManager mockPackageManager = mock(PackageManager.class);
+ when(mockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
+ when(spyContext.getPackageManager()).thenReturn(mockPackageManager);
+
+ assertNull(PipController.create(spyContext, mMockDisplayController,
+ mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState,
+ mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer,
+ mMockPipTouchHandler, mMockWindowManagerShellWrapper));
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java
index 663169f..46ebbf3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java
@@ -14,27 +14,21 @@
* limitations under the License.
*/
-package com.android.systemui.pip.phone;
-
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+package com.android.wm.shell.pip.phone;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableContext;
import android.testing.TestableLooper;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTestCase;
@@ -57,37 +51,30 @@
@TestableLooper.RunWithLooper
public class PipTaskOrganizerTest extends PipTestCase {
private PipTaskOrganizer mSpiedPipTaskOrganizer;
- private TestableContext mSpiedContext;
@Mock private DisplayController mMockdDisplayController;
- @Mock private PackageManager mPackageManager;
@Mock private PipBoundsHandler mMockPipBoundsHandler;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
@Mock private Optional<SplitScreen> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
+ @Mock private PipBoundsState mMockPipBoundsState;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
-
- mSpiedContext = spy(mContext);
-
- when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
- when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
-
- mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mSpiedContext, mMockPipBoundsHandler,
- mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController,
- mMockPipUiEventLogger, mMockShellTaskOrganizer));
+ mSpiedPipTaskOrganizer = new PipTaskOrganizer(mContext, mMockPipBoundsState,
+ mMockPipBoundsHandler, mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen,
+ mMockdDisplayController, mMockPipUiEventLogger, mMockShellTaskOrganizer);
}
@Test
- public void testNonPipDevice_shellTaskOrganizer_shouldNotAddListener() {
- verify(mMockShellTaskOrganizer, never()).addListener(any(), anyInt());
+ public void instantiatePipTaskOrganizer_addsTaskListener() {
+ verify(mMockShellTaskOrganizer).addListener(any(), anyInt());
}
@Test
- public void testNonPipDevice_displayController_shouldNotAddDisplayWindowListener() {
- verify(mMockdDisplayController, never()).addDisplayWindowListener(any());
+ public void instantiatePipTaskOrganizer_addsDisplayWindowListener() {
+ verify(mMockdDisplayController).addDisplayWindowListener(any());
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index c96cb20..4713142 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.pip.phone;
+package com.android.wm.shell.pip.phone;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -33,6 +33,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTestCase;
@@ -74,6 +75,7 @@
@Mock
private PipUiEventLogger mPipUiEventLogger;
+ private PipBoundsState mPipBoundsState;
private PipBoundsHandler mPipBoundsHandler;
private PipSnapAlgorithm mPipSnapAlgorithm;
private PipMotionHelper mMotionHelper;
@@ -90,11 +92,12 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mPipBoundsState = new PipBoundsState();
mPipBoundsHandler = new PipBoundsHandler(mContext);
mPipSnapAlgorithm = mPipBoundsHandler.getSnapAlgorithm();
mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController,
- mPipBoundsHandler, mPipTaskOrganizer, mFloatingContentCoordinator,
+ mPipBoundsHandler, mPipBoundsState, mPipTaskOrganizer, mFloatingContentCoordinator,
mPipUiEventLogger);
mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
index 2702130..40667f7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.pip.phone;
+package com.android.wm.shell.pip.phone;
import static android.view.MotionEvent.ACTION_BUTTON_PRESS;
import static android.view.MotionEvent.ACTION_DOWN;
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 30ce537..fd18d2f 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -31,6 +31,7 @@
"AnimationStart",
"PerformTraversalsStart",
"DrawStart",
+ "FrameDeadline",
"SyncQueued",
"SyncStart",
"IssueDrawCommandsStart",
@@ -45,7 +46,7 @@
static_cast<int>(FrameInfoIndex::NumIndexes),
"size mismatch: FrameInfoNames doesn't match the enum!");
-static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 18,
+static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 19,
"Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");
void FrameInfo::importUiThreadInfo(int64_t* info) {
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index f5bfedd..bb875e3 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -27,7 +27,7 @@
namespace android {
namespace uirenderer {
-#define UI_THREAD_FRAME_INFO_SIZE 10
+#define UI_THREAD_FRAME_INFO_SIZE 11
enum class FrameInfoIndex {
Flags = 0,
@@ -40,6 +40,7 @@
AnimationStart,
PerformTraversalsStart,
DrawStart,
+ FrameDeadline,
// End of UI frame info
SyncQueued,
@@ -77,9 +78,11 @@
explicit UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) {
memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
set(FrameInfoIndex::FrameTimelineVsyncId) = INVALID_VSYNC_ID;
+ set(FrameInfoIndex::FrameDeadline) = std::numeric_limits<int64_t>::max();
}
- UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync, int64_t vsyncId) {
+ UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync,
+ int64_t vsyncId, int64_t frameDeadline) {
set(FrameInfoIndex::FrameTimelineVsyncId) = vsyncId;
set(FrameInfoIndex::Vsync) = vsyncTime;
set(FrameInfoIndex::IntendedVsync) = intendedVsync;
@@ -89,6 +92,7 @@
set(FrameInfoIndex::AnimationStart) = vsyncTime;
set(FrameInfoIndex::PerformTraversalsStart) = vsyncTime;
set(FrameInfoIndex::DrawStart) = vsyncTime;
+ set(FrameInfoIndex::FrameDeadline) = frameDeadline;
return *this;
}
@@ -154,7 +158,7 @@
// GPU start time is approximated to the moment before swapBuffer is invoked.
// We could add an EGLSyncKHR fence at the beginning of the frame, but that is an overhead.
int64_t endTime = get(FrameInfoIndex::GpuCompleted);
- return endTime > 0 ? endTime - get(FrameInfoIndex::SwapBuffers) : -1;
+ return endTime > 0 ? endTime - get(FrameInfoIndex::SwapBuffers) : 0;
}
inline int64_t& set(FrameInfoIndex index) { return mFrameInfo[static_cast<int>(index)]; }
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index c89463b..a146b64 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -514,7 +514,8 @@
proxy.setLightGeometry((Vector3){0, 0, 0}, 0);
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
UiFrameInfoBuilder(proxy.frameInfo())
- .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID)
+ .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID,
+ std::numeric_limits<int64_t>::max())
.addFlag(FrameInfoFlags::SurfaceCanvas);
proxy.syncAndDrawFrame();
}
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 6bc318d..aeb096d 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -221,6 +221,17 @@
return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
}
+// FastNative
+static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontHandle) {
+ const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
+ const std::string& filePath = minikinSkia->getFilePath();
+ if (filePath.empty()) {
+ return nullptr;
+ }
+ return env->NewStringUTF(filePath.c_str());
+}
+
// Critical Native
static jlong Font_getNativeFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
@@ -274,6 +285,7 @@
{ "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics },
{ "nGetFontInfo", "(J)J", (void*) Font_getFontInfo },
{ "nGetAxisInfo", "(JI)J", (void*) Font_getAxisInfo },
+ { "nGetFontPath", "(J)Ljava/lang/String;", (void*) Font_getFontPath },
{ "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr },
};
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index 9d9e91f..9785aa5 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -196,7 +196,7 @@
};
int register_android_graphics_text_TextShaper(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "android/graphics/text/TextShaper", gMethods,
+ return RegisterMethodsOrDie(env, "android/graphics/text/TextRunShaper", gMethods,
NELEM(gMethods))
+ RegisterMethodsOrDie(env, "android/graphics/text/PositionedGlyphs",
gResultMethods, NELEM(gResultMethods));
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 212a428..ad6363b 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -29,7 +29,7 @@
#include <SkSurface.h>
#include <SkTypes.h>
-#include <GrContext.h>
+#include <GrDirectContext.h>
#include <GrTypes.h>
#include <vk/GrVkTypes.h>
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 0435981..eacabfd 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -465,6 +465,7 @@
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
// Notify the callbacks, even if there's nothing to draw so they aren't waiting
// indefinitely
+ waitOnFences();
for (auto& func : mFrameCompleteCallbacks) {
std::invoke(func, mFrameNumber);
}
@@ -629,10 +630,11 @@
nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos();
int64_t vsyncId = mRenderThread.timeLord().lastVsyncId();
+ int64_t frameDeadline = mRenderThread.timeLord().lastFrameDeadline();
int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
UiFrameInfoBuilder(frameInfo)
.addFlag(FrameInfoFlags::RTAnimation)
- .setVsync(vsync, vsync, vsyncId);
+ .setVsync(vsync, vsync, vsyncId, frameDeadline);
TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node);
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 1ea595d..c9146b2 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -130,7 +130,8 @@
int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
- mRenderThread->timeLord().vsyncReceived(vsync, intendedVsync, vsyncId);
+ int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
+ mRenderThread->timeLord().vsyncReceived(vsync, intendedVsync, vsyncId, frameDeadline);
bool canDraw = mContext->makeCurrent();
mContext->unpinImages();
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 9371656..a101d46 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -52,8 +52,9 @@
void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
int64_t vsyncId = AChoreographer_getVsyncId(rt->mChoreographer);
+ int64_t frameDeadline = AChoreographer_getFrameDeadline(rt->mChoreographer);
rt->mVsyncRequested = false;
- if (rt->timeLord().vsyncReceived(frameTimeNanos, frameTimeNanos, vsyncId) &&
+ if (rt->timeLord().vsyncReceived(frameTimeNanos, frameTimeNanos, vsyncId, frameDeadline) &&
!rt->mFrameCallbackTaskPending) {
ATRACE_NAME("queue mFrameCallbackTask");
rt->mFrameCallbackTaskPending = true;
diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp
index 7dc36c4..abb6330 100644
--- a/libs/hwui/renderthread/TimeLord.cpp
+++ b/libs/hwui/renderthread/TimeLord.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
#include "TimeLord.h"
+#include <limits>
namespace android {
namespace uirenderer {
@@ -22,12 +23,15 @@
TimeLord::TimeLord() : mFrameIntervalNanos(milliseconds_to_nanoseconds(16)),
mFrameTimeNanos(0),
mFrameIntendedTimeNanos(0),
- mFrameVsyncId(-1) {}
+ mFrameVsyncId(-1),
+ mFrameDeadline(std::numeric_limits<int64_t>::max()){}
-bool TimeLord::vsyncReceived(nsecs_t vsync, nsecs_t intendedVsync, int64_t vsyncId) {
+bool TimeLord::vsyncReceived(nsecs_t vsync, nsecs_t intendedVsync, int64_t vsyncId,
+ int64_t frameDeadline) {
if (intendedVsync > mFrameIntendedTimeNanos) {
mFrameIntendedTimeNanos = intendedVsync;
mFrameVsyncId = vsyncId;
+ mFrameDeadline = frameDeadline;
}
if (vsync > mFrameTimeNanos) {
diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h
index 23c1e51..fa05c030 100644
--- a/libs/hwui/renderthread/TimeLord.h
+++ b/libs/hwui/renderthread/TimeLord.h
@@ -32,10 +32,12 @@
nsecs_t frameIntervalNanos() const { return mFrameIntervalNanos; }
// returns true if the vsync is newer, false if it was rejected for staleness
- bool vsyncReceived(nsecs_t vsync, nsecs_t indendedVsync, int64_t vsyncId);
+ bool vsyncReceived(nsecs_t vsync, nsecs_t indendedVsync, int64_t vsyncId,
+ int64_t frameDeadline);
nsecs_t latestVsync() { return mFrameTimeNanos; }
nsecs_t computeFrameTimeNanos();
int64_t lastVsyncId() const { return mFrameVsyncId; }
+ int64_t lastFrameDeadline() const { return mFrameDeadline; }
private:
friend class RenderThread;
@@ -47,6 +49,7 @@
nsecs_t mFrameTimeNanos;
nsecs_t mFrameIntendedTimeNanos;
int64_t mFrameVsyncId;
+ int64_t mFrameDeadline;
};
} /* namespace renderthread */
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index ed89c59..eda5d22 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -146,7 +146,7 @@
testContext.waitForVsync();
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
UiFrameInfoBuilder(proxy->frameInfo())
- .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID);
+ .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID, std::numeric_limits<int64_t>::max());
proxy->syncAndDrawFrame();
}
@@ -167,7 +167,7 @@
{
ATRACE_NAME("UI-Draw Frame");
UiFrameInfoBuilder(proxy->frameInfo())
- .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID);
+ .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID, std::numeric_limits<int64_t>::max());
scene->doFrame(i);
proxy->syncAndDrawFrame();
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 5ae9dd2..42cf53b 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -106,11 +106,12 @@
boolean isProviderEnabledForUser(String provider, int userId);
boolean isLocationEnabledForUser(int userId);
void setLocationEnabledForUser(boolean enabled, int userId);
+
void addTestProvider(String name, in ProviderProperties properties, String packageName, String attributionTag);
void removeTestProvider(String provider, String packageName, String attributionTag);
void setTestProviderLocation(String provider, in Location location, String packageName, String attributionTag);
void setTestProviderEnabled(String provider, boolean enabled, String packageName, String attributionTag);
- List<LocationRequest> getTestProviderCurrentRequests(String provider);
+
LocationTime getGnssTimeMillis();
void sendExtraCommand(String provider, String command, inout Bundle extras);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index f87988c..30a4ada 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1815,22 +1815,6 @@
public void clearTestProviderStatus(@NonNull String provider) {}
/**
- * Get the last list of {@link LocationRequest}s sent to the provider.
- *
- * @hide
- */
- @TestApi
- @NonNull
- public List<LocationRequest> getTestProviderCurrentRequests(String providerName) {
- Preconditions.checkArgument(providerName != null, "invalid null provider");
- try {
- return mService.getTestProviderCurrentRequests(providerName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Sets a proximity alert for the location given by the position (latitude, longitude) and the
* given radius.
*
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index e03643c..4977c21 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -21,6 +21,7 @@
import android.Manifest;
import android.annotation.FloatRange;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -38,6 +39,8 @@
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -65,16 +68,41 @@
*/
public static final long PASSIVE_INTERVAL = Long.MAX_VALUE;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({QUALITY_LOW_POWER, QUALITY_BALANCED_POWER_ACCURACY, QUALITY_HIGH_ACCURACY})
+ public @interface Quality {}
+
+ /**
+ * A quality constant indicating a location provider may choose to satisfy this request by
+ * providing very accurate locations at the expense of potentially increased power usage.
+ */
+ public static final int QUALITY_HIGH_ACCURACY = 100;
+
+ /**
+ * A quality constant indicating a location provider may choose to satisfy this request by
+ * equally balancing power and accuracy constraints.
+ */
+ public static final int QUALITY_BALANCED_POWER_ACCURACY = 102;
+
+ /**
+ * A quality constant indicating a location provider may choose to satisfy this request by
+ * providing less accurate locations in order to save power.
+ */
+ public static final int QUALITY_LOW_POWER = 104;
+
/**
* Used with {@link #setQuality} to request the most accurate locations available.
*
* <p>This may be up to 1 meter accuracy, although this is implementation dependent.
*
* @hide
+ * @deprecated Use {@link #QUALITY_HIGH_ACCURACY} instead.
*/
+ @Deprecated
@SystemApi
@TestApi
- public static final int ACCURACY_FINE = 100;
+ public static final int ACCURACY_FINE = QUALITY_HIGH_ACCURACY;
/**
* Used with {@link #setQuality} to request "block" level accuracy.
@@ -84,10 +112,12 @@
* such as this often consumes less power.
*
* @hide
+ * @deprecated Use {@link #QUALITY_BALANCED_POWER_ACCURACY} instead.
*/
+ @Deprecated
@SystemApi
@TestApi
- public static final int ACCURACY_BLOCK = 102;
+ public static final int ACCURACY_BLOCK = QUALITY_BALANCED_POWER_ACCURACY;
/**
* Used with {@link #setQuality} to request "city" level accuracy.
@@ -97,10 +127,12 @@
* such as this often consumes less power.
*
* @hide
+ * @deprecated Use {@link #QUALITY_LOW_POWER} instead.
*/
+ @Deprecated
@SystemApi
@TestApi
- public static final int ACCURACY_CITY = 104;
+ public static final int ACCURACY_CITY = QUALITY_LOW_POWER;
/**
* Used with {@link #setQuality} to require no direct power impact (passive locations).
@@ -123,7 +155,9 @@
* possible.
*
* @hide
+ * @deprecated Use {@link #QUALITY_LOW_POWER} instead.
*/
+ @Deprecated
@SystemApi
@TestApi
public static final int POWER_LOW = 201;
@@ -134,7 +168,9 @@
* <p>This location request will allow high power location work.
*
* @hide
+ * @deprecated Use {@link #QUALITY_HIGH_ACCURACY} instead.
*/
+ @Deprecated
@SystemApi
@TestApi
public static final int POWER_HIGH = 203;
@@ -144,7 +180,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link "
+ "LocationManager} methods to provide the provider explicitly.")
@Nullable private String mProvider;
- private int mQuality;
+ private @Quality int mQuality;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link "
+ "LocationRequest} instead.")
private long mInterval;
@@ -168,7 +204,7 @@
public static LocationRequest create() {
// 60 minutes is the default legacy interval
return new LocationRequest.Builder(60 * 60 * 1000)
- .setQuality(POWER_LOW)
+ .setQuality(QUALITY_LOW_POWER)
.build();
}
@@ -241,7 +277,7 @@
private LocationRequest(
@Nullable String provider,
long intervalMillis,
- int quality,
+ @Quality int quality,
long expireAtRealtimeMillis,
long durationMillis,
int maxUpdates,
@@ -251,9 +287,6 @@
boolean locationSettingsIgnored,
boolean lowPower,
WorkSource workSource) {
- Preconditions.checkArgument(intervalMillis != PASSIVE_INTERVAL || quality == POWER_NONE);
- Preconditions.checkArgument(minUpdateIntervalMillis <= intervalMillis);
-
mProvider = provider;
mInterval = intervalMillis;
mQuality = quality;
@@ -297,24 +330,39 @@
@SystemApi
@Deprecated
public @NonNull LocationRequest setQuality(int quality) {
- mQuality = Builder.checkQuality(quality, true);
+ switch (quality) {
+ case POWER_HIGH:
+ // fall through
+ case ACCURACY_FINE:
+ mQuality = QUALITY_HIGH_ACCURACY;
+ break;
+ case ACCURACY_BLOCK:
+ mQuality = QUALITY_BALANCED_POWER_ACCURACY;
+ break;
+ case POWER_LOW:
+ // fall through
+ case ACCURACY_CITY:
+ mQuality = QUALITY_LOW_POWER;
+ break;
+ case POWER_NONE:
+ mInterval = PASSIVE_INTERVAL;
+ break;
+ default:
+ throw new IllegalArgumentException("invalid quality: " + quality);
+ }
+
return this;
}
/**
- * Returns the quality of the location request.
+ * Returns the quality hint for this location request. The quality hint informs the provider how
+ * it should attempt to manage any accuracy vs power tradeoffs while attempting to satisfy this
+ * location request.
*
- * @return the quality of the location request
- *
- * @hide
+ * @return the desired quality tradeoffs between accuracy and power
*/
- @SystemApi
- public int getQuality() {
- if (mInterval == PASSIVE_INTERVAL) {
- return POWER_NONE;
- } else {
- return mQuality;
- }
+ public @Quality int getQuality() {
+ return mQuality;
}
/**
@@ -749,97 +797,65 @@
if (mProvider != null) {
s.append(mProvider).append(" ");
}
- if (mQuality != POWER_NONE && mQuality != ACCURACY_BLOCK) {
- s.append(qualityToString(mQuality)).append(" ");
- }
if (mInterval != PASSIVE_INTERVAL) {
s.append("@");
TimeUtils.formatDuration(mInterval, s);
+
+ switch (mQuality) {
+ case QUALITY_HIGH_ACCURACY:
+ s.append(" HIGH_ACCURACY");
+ break;
+ case QUALITY_BALANCED_POWER_ACCURACY:
+ s.append(" BALANCED");
+ break;
+ case QUALITY_LOW_POWER:
+ s.append(" LOW_POWER");
+ break;
+ }
} else {
s.append("PASSIVE");
}
if (mExpireAtRealtimeMillis != Long.MAX_VALUE) {
- s.append(" expireAt=").append(TimeUtils.formatRealtime(mExpireAtRealtimeMillis));
+ s.append(", expireAt=").append(TimeUtils.formatRealtime(mExpireAtRealtimeMillis));
}
if (mDurationMillis != Long.MAX_VALUE) {
- s.append(" duration=");
+ s.append(", duration=");
TimeUtils.formatDuration(mDurationMillis, s);
}
if (mMaxUpdates != Integer.MAX_VALUE) {
- s.append(" maxUpdates=").append(mMaxUpdates);
+ s.append(", maxUpdates=").append(mMaxUpdates);
}
if (mMinUpdateIntervalMillis != IMPLICIT_MIN_UPDATE_INTERVAL
&& mMinUpdateIntervalMillis < mInterval) {
- s.append(" minUpdateInterval=");
+ s.append(", minUpdateInterval=");
TimeUtils.formatDuration(mMinUpdateIntervalMillis, s);
}
if (mMinUpdateDistanceMeters > 0.0) {
- s.append(" minUpdateDistance=").append(mMinUpdateDistanceMeters);
+ s.append(", minUpdateDistance=").append(mMinUpdateDistanceMeters);
}
if (mLowPower) {
- s.append(" lowPower");
+ s.append(", lowPower");
}
if (mHideFromAppOps) {
- s.append(" hiddenFromAppOps");
+ s.append(", hiddenFromAppOps");
}
if (mLocationSettingsIgnored) {
- s.append(" locationSettingsIgnored");
+ s.append(", locationSettingsIgnored");
}
- if (mWorkSource != null) {
- s.append(" ").append(mWorkSource);
+ if (mWorkSource != null && !mWorkSource.isEmpty()) {
+ s.append(", ").append(mWorkSource);
}
s.append(']');
return s.toString();
}
- private static String qualityToString(int quality) {
- switch (quality) {
- case ACCURACY_FINE:
- return "ACCURACY_FINE";
- case ACCURACY_BLOCK:
- return "ACCURACY_BLOCK";
- case ACCURACY_CITY:
- return "ACCURACY_CITY";
- case POWER_NONE:
- return "POWER_NONE";
- case POWER_LOW:
- return "POWER_LOW";
- case POWER_HIGH:
- return "POWER_HIGH";
- default:
- return "???";
- }
- }
-
/**
* A builder class for {@link LocationRequest}.
*/
public static final class Builder {
- private static int checkQuality(int quality, boolean allowDeprecated) {
- switch (quality) {
- case ACCURACY_FINE:
- // fall through
- case ACCURACY_BLOCK:
- // fall through
- case ACCURACY_CITY:
- // fall through
- case POWER_LOW:
- // fall through
- case POWER_HIGH:
- return quality;
- case POWER_NONE:
- if (allowDeprecated) {
- return quality;
- }
- // fall through
- default:
- throw new IllegalArgumentException("invalid quality: " + quality);
- }
- }
-
private long mIntervalMillis;
- private int mQuality;
+ private @Quality int mQuality;
private long mDurationMillis;
private int mMaxUpdates;
private long mMinUpdateIntervalMillis;
@@ -857,7 +873,7 @@
// gives us a range check
setIntervalMillis(intervalMillis);
- mQuality = ACCURACY_BLOCK;
+ mQuality = QUALITY_BALANCED_POWER_ACCURACY;
mDurationMillis = Long.MAX_VALUE;
mMaxUpdates = Integer.MAX_VALUE;
mMinUpdateIntervalMillis = IMPLICIT_MIN_UPDATE_INTERVAL;
@@ -885,9 +901,6 @@
// handle edge cases that can only happen with location request that has been modified
// by deprecated SystemApi methods
- if (mQuality == POWER_NONE) {
- mIntervalMillis = PASSIVE_INTERVAL;
- }
if (mIntervalMillis == PASSIVE_INTERVAL
&& mMinUpdateIntervalMillis == IMPLICIT_MIN_UPDATE_INTERVAL) {
// this is the legacy default minimum update interval, so if we're forced to
@@ -914,11 +927,17 @@
}
/**
- * @hide
+ * Sets the request quality. The quality is a hint to providers on how they should weigh
+ * power vs accuracy tradeoffs. High accuracy locations may cost more power to produce, and
+ * lower accuracy locations may cost less power to produce. Defaults to
+ * {@link #QUALITY_BALANCED_POWER_ACCURACY}.
*/
- @SystemApi
- public @NonNull Builder setQuality(int quality) {
- mQuality = checkQuality(quality, false);
+ public @NonNull Builder setQuality(@Quality int quality) {
+ Preconditions.checkArgument(
+ quality == QUALITY_LOW_POWER || quality == QUALITY_BALANCED_POWER_ACCURACY
+ || quality == QUALITY_HIGH_ACCURACY,
+ "quality must be a defined QUALITY constant, not " + quality);
+ mQuality = quality;
return this;
}
@@ -1102,7 +1121,7 @@
return new LocationRequest(
null,
mIntervalMillis,
- mIntervalMillis != PASSIVE_INTERVAL ? mQuality : POWER_NONE,
+ mQuality,
Long.MAX_VALUE,
mDurationMillis,
mMaxUpdates,
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index eb2e23e..4a095c9 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -28,7 +28,6 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -161,7 +160,7 @@
be set to true when the phone is having emergency call, and then will
be set to false by mPhoneStateListener when the emergency call ends.
*/
- mIsInEmergencyCall = PhoneNumberUtils.isEmergencyNumber(phoneNumber);
+ mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(phoneNumber);
if (DEBUG) Log.v(TAG, "ACTION_NEW_OUTGOING_CALL - " + getInEmergency());
} else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) {
updateLocationMode();
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index fee86ce..00ba552 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -16,10 +16,15 @@
package com.android.internal.location;
+import static android.location.LocationRequest.QUALITY_BALANCED_POWER_ACCURACY;
+import static android.location.LocationRequest.QUALITY_HIGH_ACCURACY;
+import static android.location.LocationRequest.QUALITY_LOW_POWER;
+
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.location.LocationRequest;
+import android.location.LocationRequest.Quality;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -41,7 +46,7 @@
public static final long INTERVAL_DISABLED = Long.MAX_VALUE;
public static final ProviderRequest EMPTY_REQUEST = new ProviderRequest(
- INTERVAL_DISABLED, false, false, Collections.emptyList(), new WorkSource());
+ INTERVAL_DISABLED, QUALITY_BALANCED_POWER_ACCURACY, false, false, new WorkSource());
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
+ "ProviderRequest}")
@@ -49,6 +54,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
+ "ProviderRequest}")
public final long interval;
+ private final @Quality int mQuality;
private final boolean mLowPower;
private final boolean mLocationSettingsIgnored;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
@@ -56,15 +62,24 @@
public final List<LocationRequest> locationRequests;
private final WorkSource mWorkSource;
- private ProviderRequest(long intervalMillis, boolean lowPower,
- boolean locationSettingsIgnored, @NonNull List<LocationRequest> locationRequests,
- @NonNull WorkSource workSource) {
+ private ProviderRequest(long intervalMillis, @Quality int quality, boolean lowPower,
+ boolean locationSettingsIgnored, @NonNull WorkSource workSource) {
reportLocation = intervalMillis != INTERVAL_DISABLED;
interval = intervalMillis;
+ mQuality = quality;
mLowPower = lowPower;
mLocationSettingsIgnored = locationSettingsIgnored;
- this.locationRequests = locationRequests;
- mWorkSource = workSource;
+ if (intervalMillis != INTERVAL_DISABLED) {
+ locationRequests = Collections.singletonList(new LocationRequest.Builder(intervalMillis)
+ .setQuality(quality)
+ .setLowPower(lowPower)
+ .setLocationSettingsIgnored(locationSettingsIgnored)
+ .setWorkSource(workSource)
+ .build());
+ } else {
+ locationRequests = Collections.emptyList();
+ }
+ mWorkSource = Objects.requireNonNull(workSource);
}
/**
@@ -84,6 +99,15 @@
}
/**
+ * The quality hint for this location request. The quality hint informs the provider how it
+ * should attempt to manage any accuracy vs power tradeoffs while attempting to satisfy this
+ * provider request.
+ */
+ public @Quality int getQuality() {
+ return mQuality;
+ }
+
+ /**
* Whether any applicable hardware low power modes should be used to satisfy this request.
*/
public boolean isLowPower() {
@@ -100,13 +124,6 @@
}
/**
- * The full list of location requests contributing to this provider request.
- */
- public @NonNull List<LocationRequest> getLocationRequests() {
- return locationRequests;
- }
-
- /**
* The power blame for this provider request.
*/
public @NonNull WorkSource getWorkSource() {
@@ -117,13 +134,17 @@
new Parcelable.Creator<ProviderRequest>() {
@Override
public ProviderRequest createFromParcel(Parcel in) {
- return new ProviderRequest(
- /* intervalMillis= */ in.readLong(),
- /* lowPower= */ in.readBoolean(),
- /* locationSettingsIgnored= */ in.readBoolean(),
- /* locationRequests= */
- in.createTypedArrayList(LocationRequest.CREATOR),
- /* workSource= */ in.readTypedObject(WorkSource.CREATOR));
+ long intervalMillis = in.readLong();
+ if (intervalMillis == INTERVAL_DISABLED) {
+ return EMPTY_REQUEST;
+ } else {
+ return new ProviderRequest(
+ intervalMillis,
+ /* quality= */ in.readInt(),
+ /* lowPower= */ in.readBoolean(),
+ /* locationSettingsIgnored= */ in.readBoolean(),
+ /* workSource= */ in.readTypedObject(WorkSource.CREATOR));
+ }
}
@Override
@@ -140,10 +161,12 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeLong(interval);
- parcel.writeBoolean(mLowPower);
- parcel.writeBoolean(mLocationSettingsIgnored);
- parcel.writeTypedList(locationRequests);
- parcel.writeTypedObject(mWorkSource, flags);
+ if (interval != INTERVAL_DISABLED) {
+ parcel.writeInt(mQuality);
+ parcel.writeBoolean(mLowPower);
+ parcel.writeBoolean(mLocationSettingsIgnored);
+ parcel.writeTypedObject(mWorkSource, flags);
+ }
}
@Override
@@ -160,16 +183,16 @@
return that.interval == INTERVAL_DISABLED;
} else {
return interval == that.interval
+ && mQuality == that.mQuality
&& mLowPower == that.mLowPower
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored
- && locationRequests.equals(that.locationRequests)
&& mWorkSource.equals(that.mWorkSource);
}
}
@Override
public int hashCode() {
- return Objects.hash(interval, mWorkSource);
+ return Objects.hash(interval, mQuality, mWorkSource);
}
@Override
@@ -179,6 +202,13 @@
if (interval != INTERVAL_DISABLED) {
s.append("@");
TimeUtils.formatDuration(interval, s);
+ if (mQuality != QUALITY_BALANCED_POWER_ACCURACY) {
+ if (mQuality == QUALITY_HIGH_ACCURACY) {
+ s.append(", HIGH_ACCURACY");
+ } else if (mQuality == QUALITY_LOW_POWER) {
+ s.append(", LOW_POWER");
+ }
+ }
if (mLowPower) {
s.append(", lowPower");
}
@@ -200,9 +230,9 @@
*/
public static class Builder {
private long mIntervalMillis = INTERVAL_DISABLED;
+ private int mQuality = QUALITY_BALANCED_POWER_ACCURACY;
private boolean mLowPower;
private boolean mLocationSettingsIgnored;
- private List<LocationRequest> mLocationRequests = Collections.emptyList();
private WorkSource mWorkSource = new WorkSource();
/**
@@ -216,6 +246,20 @@
}
/**
+ * Sets the request quality. The quality is a hint to providers on how they should weigh
+ * power vs accuracy tradeoffs. High accuracy locations may cost more power to produce, and
+ * lower accuracy locations may cost less power to produce. Defaults to
+ * {@link LocationRequest#QUALITY_BALANCED_POWER_ACCURACY}.
+ */
+ public @NonNull Builder setQuality(@Quality int quality) {
+ Preconditions.checkArgument(
+ quality == QUALITY_LOW_POWER || quality == QUALITY_BALANCED_POWER_ACCURACY
+ || quality == QUALITY_HIGH_ACCURACY);
+ mQuality = quality;
+ return this;
+ }
+
+ /**
* Sets whether hardware low power mode should be used. False by default.
*/
public @NonNull Builder setLowPower(boolean lowPower) {
@@ -232,15 +276,6 @@
}
/**
- * Sets the {@link LocationRequest}s associated with this request. Empty by default.
- */
- public @NonNull Builder setLocationRequests(
- @NonNull List<LocationRequest> locationRequests) {
- this.mLocationRequests = Objects.requireNonNull(locationRequests);
- return this;
- }
-
- /**
* Sets the work source for power blame. Empty by default.
*/
public @NonNull Builder setWorkSource(@NonNull WorkSource workSource) {
@@ -255,8 +290,8 @@
if (mIntervalMillis == INTERVAL_DISABLED) {
return EMPTY_REQUEST;
} else {
- return new ProviderRequest(mIntervalMillis, mLowPower, mLocationSettingsIgnored,
- mLocationRequests, mWorkSource);
+ return new ProviderRequest(mIntervalMillis, mQuality, mLowPower,
+ mLocationSettingsIgnored, mWorkSource);
}
}
}
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index f43eb63..80636c6 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -29,18 +29,18 @@
field public static final String FUSED_PROVIDER = "fused";
}
- public final class LocationRequestUnbundled {
- method public long getFastestInterval();
- method public long getInterval();
- method public int getQuality();
- method public float getSmallestDisplacement();
- method public boolean isLocationSettingsIgnored();
- field public static final int ACCURACY_BLOCK = 102; // 0x66
- field public static final int ACCURACY_CITY = 104; // 0x68
- field public static final int ACCURACY_FINE = 100; // 0x64
- field public static final int POWER_HIGH = 203; // 0xcb
- field public static final int POWER_LOW = 201; // 0xc9
- field public static final int POWER_NONE = 200; // 0xc8
+ @Deprecated public final class LocationRequestUnbundled {
+ method @Deprecated public long getFastestInterval();
+ method @Deprecated public long getInterval();
+ method @Deprecated @android.location.LocationRequest.Quality public int getQuality();
+ method @Deprecated public float getSmallestDisplacement();
+ method @Deprecated public boolean isLocationSettingsIgnored();
+ field @Deprecated public static final int ACCURACY_BLOCK = 102; // 0x66
+ field @Deprecated public static final int ACCURACY_CITY = 104; // 0x68
+ field @Deprecated public static final int ACCURACY_FINE = 100; // 0x64
+ field @Deprecated public static final int POWER_HIGH = 203; // 0xcb
+ field @Deprecated public static final int POWER_LOW = 201; // 0xc9
+ field @Deprecated public static final int POWER_NONE = 200; // 0xc8
}
public final class ProviderPropertiesUnbundled {
@@ -49,7 +49,8 @@
public final class ProviderRequestUnbundled {
method public long getInterval();
- method @NonNull public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
+ method @Deprecated @NonNull public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
+ method @android.location.LocationRequest.Quality @RequiresApi(android.os.Build.VERSION_CODES.S) public int getQuality();
method public boolean getReportLocation();
method @NonNull @RequiresApi(android.os.Build.VERSION_CODES.S) public android.os.WorkSource getWorkSource();
method @RequiresApi(android.os.Build.VERSION_CODES.Q) public boolean isLocationSettingsIgnored();
diff --git a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
index 92e05ef..0e7c633 100644
--- a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java
@@ -17,6 +17,7 @@
package com.android.location.provider;
import android.location.LocationRequest;
+import android.location.LocationRequest.Quality;
/**
* This class is an interface to LocationRequests for unbundled applications.
@@ -24,55 +25,50 @@
* <p>IMPORTANT: This class is effectively a public API for unbundled
* applications, and must remain API stable. See README.txt in the root
* of this package for more information.
+ *
+ * @deprecated Do not use.
*/
+@Deprecated
public final class LocationRequestUnbundled {
+
/**
- * Returned by {@link #getQuality} when requesting the most accurate locations available.
- *
- * <p>This may be up to 1 meter accuracy, although this is implementation dependent.
+ * @deprecated Use {@link LocationRequest#QUALITY_HIGH_ACCURACY} instead.
*/
+ @Deprecated
public static final int ACCURACY_FINE = LocationRequest.ACCURACY_FINE;
/**
- * Returned by {@link #getQuality} when requesting "block" level accuracy.
- *
- * <p>Block level accuracy is considered to be about 100 meter accuracy,
- * although this is implementation dependent. Using a coarse accuracy
- * such as this often consumes less power.
+ * @deprecated Use {@link LocationRequest#QUALITY_BALANCED_POWER_ACCURACY} instead.
*/
+ @Deprecated
public static final int ACCURACY_BLOCK = LocationRequest.ACCURACY_BLOCK;
+
/**
- * Returned by {@link #getQuality} when requesting "city" level accuracy.
- *
- * <p>City level accuracy is considered to be about 10km accuracy,
- * although this is implementation dependent. Using a coarse accuracy
- * such as this often consumes less power.
+ * @deprecated Use {@link LocationRequest#QUALITY_LOW_POWER} instead.
*/
+ @Deprecated
public static final int ACCURACY_CITY = LocationRequest.ACCURACY_CITY;
+
/**
- * Returned by {@link #getQuality} when requiring no direct power impact (passive locations).
- *
- * <p>This location request will not trigger any active location requests,
- * but will receive locations triggered by other applications. Your application
- * will not receive any direct power blame for location work.
+ * @deprecated Do not use.
*/
+ @Deprecated
public static final int POWER_NONE = LocationRequest.POWER_NONE;
- /**
- * Returned by {@link #getQuality} when requesting low power impact.
- *
- * <p>This location request will avoid high power location work where
- * possible.
- */
- public static final int POWER_LOW = LocationRequest.POWER_LOW;
/**
- * Returned by {@link #getQuality} when allowing high power consumption for location.
- *
- * <p>This location request will allow high power location work.
+ * @deprecated Use {@link LocationRequest#QUALITY_LOW_POWER} instead.
*/
+ @Deprecated
+ public static final int POWER_LOW = LocationRequest.POWER_LOW;
+
+
+ /**
+ * @deprecated Use {@link LocationRequest#QUALITY_BALANCED_POWER_ACCURACY} instead.
+ */
+ @Deprecated
public static final int POWER_HIGH = LocationRequest.POWER_HIGH;
private final LocationRequest delegate;
@@ -102,9 +98,9 @@
/**
* Get the quality of the request.
*
- * @return an accuracy or power constant
+ * @return a {@link LocationRequest} QUALITY_* constant
*/
- public int getQuality() {
+ public @Quality int getQuality() {
return delegate.getQuality();
}
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index 6f5fcc7..f7bac74 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -25,7 +25,7 @@
import com.android.internal.location.ProviderRequest;
-import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -57,6 +57,16 @@
}
/**
+ * The quality hint for this location request. The quality hint informs the provider how it
+ * should attempt to manage any accuracy vs power tradeoffs while attempting to satisfy this
+ * provider request.
+ */
+ @RequiresApi(Build.VERSION_CODES.S)
+ public @LocationRequest.Quality int getQuality() {
+ return mRequest.getQuality();
+ }
+
+ /**
* The interval at which a provider should report location. Will return
* {@link #INTERVAL_DISABLED} for an inactive request.
*/
@@ -84,14 +94,22 @@
/**
* The full list of location requests contributing to this provider request.
+ *
+ * @deprecated Do not use.
*/
+ @Deprecated
public @NonNull List<LocationRequestUnbundled> getLocationRequests() {
- List<LocationRequestUnbundled> result = new ArrayList<>(
- mRequest.getLocationRequests().size());
- for (LocationRequest r : mRequest.getLocationRequests()) {
- result.add(new LocationRequestUnbundled(r));
+ if (!mRequest.isActive()) {
+ return Collections.emptyList();
}
- return result;
+
+ return Collections.singletonList(new LocationRequestUnbundled(
+ new LocationRequest.Builder(mRequest.getIntervalMillis())
+ .setQuality(mRequest.getQuality())
+ .setLowPower(mRequest.isLowPower())
+ .setLocationSettingsIgnored(mRequest.isLocationSettingsIgnored())
+ .setWorkSource(mRequest.getWorkSource())
+ .build()));
}
/**
diff --git a/media/OWNERS b/media/OWNERS
index 36df3a0..0fc781c 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -15,6 +15,8 @@
klhyun@google.com
lajos@google.com
marcone@google.com
+nchalko@google.com
philburk@google.com
+quxiangfang@google.com
sungsoo@google.com
wonsik@google.com
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index de2a7b2..fa6a4ff 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1263,14 +1263,20 @@
// TODO: Check mEncapsulationMode compatibility with MODE_STATIC, etc?
- try {
- // If the buffer size is not specified in streaming mode,
- // use a single frame for the buffer size and let the
- // native code figure out the minimum buffer size.
- if (mMode == MODE_STREAM && mBufferSizeInBytes == 0) {
- mBufferSizeInBytes = mFormat.getChannelCount()
- * mFormat.getBytesPerSample(mFormat.getEncoding());
+ // If the buffer size is not specified in streaming mode,
+ // use a single frame for the buffer size and let the
+ // native code figure out the minimum buffer size.
+ if (mMode == MODE_STREAM && mBufferSizeInBytes == 0) {
+ int bytesPerSample = 1;
+ try {
+ bytesPerSample = mFormat.getBytesPerSample(mFormat.getEncoding());
+ } catch (IllegalArgumentException e) {
+ // do nothing
}
+ mBufferSizeInBytes = mFormat.getChannelCount() * bytesPerSample;
+ }
+
+ try {
final AudioTrack track = new AudioTrack(
mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload,
mEncapsulationMode, mTunerConfiguration);
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 0747ab13..ae97a71 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2711,12 +2711,12 @@
}
};
- private final Pattern zeroPattern = new Pattern(0, 0);
+ private static final Pattern ZERO_PATTERN = new Pattern(0, 0);
/**
* The pattern applicable to the protected data in each subsample.
*/
- private Pattern pattern;
+ private Pattern mPattern = ZERO_PATTERN;
/**
* Set the subsample count, clear/encrypted sizes, key, IV and mode fields of
@@ -2735,22 +2735,30 @@
key = newKey;
iv = newIV;
mode = newMode;
- pattern = zeroPattern;
+ mPattern = ZERO_PATTERN;
+ }
+
+ /**
+ * Returns the {@link Pattern encryption pattern}.
+ */
+ public @NonNull Pattern getPattern() {
+ return new Pattern(mPattern.getEncryptBlocks(), mPattern.getSkipBlocks());
}
/**
* Set the encryption pattern on a {@link MediaCodec.CryptoInfo} instance.
- * See {@link MediaCodec.CryptoInfo.Pattern}.
+ * See {@link Pattern}.
*/
public void setPattern(Pattern newPattern) {
if (newPattern == null) {
- newPattern = zeroPattern;
+ newPattern = ZERO_PATTERN;
}
- pattern = newPattern;
+ setPattern(newPattern.getEncryptBlocks(), newPattern.getSkipBlocks());
}
+ // Accessed from android_media_MediaExtractor.cpp.
private void setPattern(int blocksToEncrypt, int blocksToSkip) {
- pattern = new Pattern(blocksToEncrypt, blocksToSkip);
+ mPattern = new Pattern(blocksToEncrypt, blocksToSkip);
}
@Override
@@ -2772,9 +2780,9 @@
builder.append(", encrypted ");
builder.append(Arrays.toString(numBytesOfEncryptedData));
builder.append(", pattern (encrypt: ");
- builder.append(pattern.mEncryptBlocks);
+ builder.append(mPattern.mEncryptBlocks);
builder.append(", skip: ");
- builder.append(pattern.mSkipBlocks);
+ builder.append(mPattern.mSkipBlocks);
builder.append(")");
return builder.toString();
}
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index dbf4ad0..78eeca1 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -427,7 +427,7 @@
private MediaMetadata(Parcel in) {
mBundle = in.readBundle();
- mBitmapDimensionLimit = Math.max(in.readInt(), 0);
+ mBitmapDimensionLimit = Math.max(in.readInt(), 1);
}
/**
@@ -518,17 +518,18 @@
/**
* Gets the width/height limit (in pixels) for the bitmaps when this metadata was created.
- * This method returns a positive value or zero.
+ * This method always returns a positive value.
* <p>
- * If it returns a positive value, then all the bitmaps in this metadata has width/height
+ * If it returns {@link Integer#MAX_VALUE}, then no scaling down was applied to the bitmaps
+ * when this metadata was created.
+ * <p>
+ * If it returns another positive value, then all the bitmaps in this metadata has width/height
* not greater than this limit. Bitmaps may have been scaled down according to the limit.
* <p>
- * If it returns zero, then no scaling down was applied to the bitmaps when this metadata
- * was created.
*
* @see Builder#setBitmapDimensionLimit(int)
*/
- public @IntRange(from = 0) int getBitmapDimensionLimit() {
+ public @IntRange(from = 1) int getBitmapDimensionLimit() {
return mBitmapDimensionLimit;
}
@@ -738,7 +739,7 @@
*/
public static final class Builder {
private final Bundle mBundle;
- private int mBitmapDimensionLimit;
+ private int mBitmapDimensionLimit = Integer.MAX_VALUE;
/**
* Create an empty Builder. Any field that should be included in the
@@ -925,14 +926,21 @@
* Bitmaps will be replaced with scaled down copies if their width (or height) is
* larger than {@code bitmapDimensionLimit}.
* <p>
- * In order to unset the limit, pass zero as {@code bitmapDimensionLimit}.
+ * In order to unset the limit, pass {@link Integer#MAX_VALUE} as
+ * {@code bitmapDimensionLimit}.
*
* @param bitmapDimensionLimit The maximum width/height (in pixels) for bitmaps
- * contained in the metadata. Pass {@code 0} to unset the limit.
+ * contained in the metadata. Non-positive values are ignored.
+ * Pass {@link Integer#MAX_VALUE} to unset the limit.
*/
@NonNull
- public Builder setBitmapDimensionLimit(int bitmapDimensionLimit) {
- mBitmapDimensionLimit = Math.max(bitmapDimensionLimit, 0);
+ public Builder setBitmapDimensionLimit(@IntRange(from = 1) int bitmapDimensionLimit) {
+ if (bitmapDimensionLimit > 0) {
+ mBitmapDimensionLimit = bitmapDimensionLimit;
+ } else {
+ Log.w(TAG, "setBitmapDimensionLimit(): Ignoring non-positive bitmapDimensionLimit: "
+ + bitmapDimensionLimit);
+ }
return this;
}
@@ -942,7 +950,7 @@
* @return The new MediaMetadata instance
*/
public MediaMetadata build() {
- if (mBitmapDimensionLimit > 0) {
+ if (mBitmapDimensionLimit != Integer.MAX_VALUE) {
for (String key : mBundle.keySet()) {
Object value = mBundle.get(key);
if (value instanceof Bitmap) {
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index cf03b06..835a709 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -16,10 +16,13 @@
package android.media;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
@@ -1331,7 +1334,6 @@
* @see MediaFormat#COLOR_RANGE_FULL
*/
public static final int METADATA_KEY_COLOR_RANGE = 37;
- // Add more here...
/**
* This key retrieves the sample rate in Hz, if available.
@@ -1344,4 +1346,13 @@
* This is a signed 32-bit integer formatted as a string in base 10.
*/
public static final int METADATA_KEY_BITS_PER_SAMPLE = 39;
+
+ /**
+ * This key retrieves the video codec mimetype if available.
+ * @hide
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40;
+
+ // Add more here...
}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 2b3f420c..4d87fb3 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -45,6 +45,9 @@
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
+import android.view.DisplayAddress;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -1737,7 +1740,9 @@
*/
private static final int DEFAULT_PLAYBACK_VOLUME = DEFAULT_PLAYBACK_MAX_VOLUME;
- RouteInfo(RouteCategory category) {
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public RouteInfo(RouteCategory category) {
mCategory = category;
mDeviceType = DEVICE_TYPE_UNKNOWN;
}
@@ -2078,7 +2083,9 @@
return mPresentationDisplay;
}
- boolean updatePresentationDisplay() {
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public boolean updatePresentationDisplay() {
Display display = choosePresentationDisplay();
if (mPresentationDisplay != display) {
mPresentationDisplay = display;
@@ -2088,41 +2095,81 @@
}
private Display choosePresentationDisplay() {
- if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
- Display[] displays = sStatic.getAllPresentationDisplays();
+ if ((getSupportedTypes() & ROUTE_TYPE_LIVE_VIDEO) == 0) {
+ return null;
+ }
+ final Display[] displays = getAllPresentationDisplays();
+ if (displays == null || displays.length == 0) {
+ return null;
+ }
- // Ensure that the specified display is valid for presentations.
- // This check will normally disallow the default display unless it was
- // configured as a presentation display for some reason.
- if (mPresentationDisplayId >= 0) {
- for (Display display : displays) {
- if (display.getDisplayId() == mPresentationDisplayId) {
- return display;
- }
+ // Ensure that the specified display is valid for presentations.
+ // This check will normally disallow the default display unless it was
+ // configured as a presentation display for some reason.
+ if (mPresentationDisplayId >= 0) {
+ for (Display display : displays) {
+ if (display.getDisplayId() == mPresentationDisplayId) {
+ return display;
}
- return null;
}
+ return null;
+ }
- // Find the indicated Wifi display by its address.
- if (mDeviceAddress != null) {
- for (Display display : displays) {
- if (display.getType() == Display.TYPE_WIFI
- && mDeviceAddress.equals(display.getAddress())) {
- return display;
- }
+ // Find the indicated Wifi display by its address.
+ if (getDeviceAddress() != null) {
+ for (Display display : displays) {
+ if (display.getType() == Display.TYPE_WIFI
+ && displayAddressEquals(display)) {
+ return display;
}
- return null;
- }
-
- // For the default route, choose the first presentation display from the list.
- if (this == sStatic.mDefaultAudioVideo && displays.length > 0) {
- return displays[0];
}
}
+
+ // Returns the first hard-wired display.
+ for (Display display : displays) {
+ if (display.getType() == Display.TYPE_EXTERNAL) {
+ return display;
+ }
+ }
+
+ // Returns the first non-default built-in display.
+ for (Display display : displays) {
+ if (display.getType() == Display.TYPE_INTERNAL) {
+ return display;
+ }
+ }
+
+ // For the default route, choose the first presentation display from the list.
+ if (this == getDefaultAudioVideo()) {
+ return displays[0];
+ }
return null;
}
/** @hide */
+ @VisibleForTesting
+ public Display[] getAllPresentationDisplays() {
+ return sStatic.getAllPresentationDisplays();
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public RouteInfo getDefaultAudioVideo() {
+ return sStatic.mDefaultAudioVideo;
+ }
+
+ private boolean displayAddressEquals(Display display) {
+ final DisplayAddress displayAddress = display.getAddress();
+ // mDeviceAddress recorded mac address. If displayAddress is not a kind of Network,
+ // return false early.
+ if (!(displayAddress instanceof DisplayAddress.Network)) {
+ return false;
+ }
+ final DisplayAddress.Network networkAddress = (DisplayAddress.Network) displayAddress;
+ return getDeviceAddress().equals(networkAddress.toString());
+ }
+
+ /** @hide */
@UnsupportedAppUsage
public String getDeviceAddress() {
return mDeviceAddress;
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index 8cbe52f..8676462 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -443,7 +443,7 @@
}
@Override
- public void onAwaitNumberOfJobsChanged(int jobId, int oldAwaitNumber,
+ public void onAwaitNumberOfSessionsChanged(int jobId, int oldAwaitNumber,
int newAwaitNumber) throws RemoteException {
//TODO(hkuang): Implement this.
}
@@ -1081,7 +1081,7 @@
private final MediaTranscodeManager mManager;
private Executor mListenerExecutor;
private OnTranscodingFinishedListener mListener;
- private int mJobId = -1;
+ private int mSessionId = -1;
// Lock for internal state.
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -1104,7 +1104,7 @@
private TranscodingJob(
@NonNull MediaTranscodeManager manager,
@NonNull TranscodingRequest request,
- @NonNull TranscodingJobParcel parcel,
+ @NonNull TranscodingSessionParcel parcel,
@NonNull @CallbackExecutor Executor executor,
@NonNull OnTranscodingFinishedListener listener) {
Objects.requireNonNull(manager, "manager must not be null");
@@ -1112,7 +1112,7 @@
Objects.requireNonNull(executor, "listenerExecutor must not be null");
Objects.requireNonNull(listener, "listener must not be null");
mManager = manager;
- mJobId = parcel.jobId;
+ mSessionId = parcel.sessionId;
mListenerExecutor = executor;
mListener = listener;
mRequest = request;
@@ -1196,16 +1196,16 @@
synchronized (mManager.mPendingTranscodingJobs) {
try {
// Submits the request to MediaTranscoding service.
- TranscodingJobParcel jobParcel = new TranscodingJobParcel();
- if (!client.submitRequest(mRequest.writeToParcel(), jobParcel)) {
+ TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel();
+ if (!client.submitRequest(mRequest.writeToParcel(), sessionParcel)) {
mHasRetried = true;
throw new UnsupportedOperationException("Failed to enqueue request");
}
// Replace the old job id wit the new one.
- mJobId = jobParcel.jobId;
+ mSessionId = sessionParcel.sessionId;
// Adds the new job back into pending jobs.
- mManager.mPendingTranscodingJobs.put(mJobId, this);
+ mManager.mPendingTranscodingJobs.put(mSessionId, this);
} catch (RemoteException re) {
throw new MediaTranscodingException.ServiceNotAvailableException(
"Failed to resubmit request to Transcoding service");
@@ -1229,7 +1229,7 @@
ITranscodingClient client = mManager.getTranscodingClient();
// The client may be gone.
if (client != null) {
- client.cancelJob(mJobId);
+ client.cancelSession(mSessionId);
}
} catch (RemoteException re) {
//TODO(hkuang): Find out what to do if failing to cancel the job.
@@ -1272,7 +1272,7 @@
* @return job id.
*/
public int getJobId() {
- return mJobId;
+ return mSessionId;
}
/**
@@ -1326,7 +1326,7 @@
break;
}
return String.format(" Job: {id: %d, status: %s, result: %s, progress: %d}",
- mJobId, status, result, mProgress);
+ mSessionId, status, result, mProgress);
}
private void updateProgress(int newProgress) {
@@ -1383,7 +1383,7 @@
// Submits the request to MediaTranscoding service.
try {
- TranscodingJobParcel jobParcel = new TranscodingJobParcel();
+ TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel();
// Synchronizes the access to mPendingTranscodingJobs to make sure the job Id is
// inserted in the mPendingTranscodingJobs in the callback handler.
synchronized (mPendingTranscodingJobs) {
@@ -1399,15 +1399,15 @@
}
}
- if (!mTranscodingClient.submitRequest(requestParcel, jobParcel)) {
+ if (!mTranscodingClient.submitRequest(requestParcel, sessionParcel)) {
throw new UnsupportedOperationException("Failed to enqueue request");
}
}
- // Wraps the TranscodingJobParcel into a TranscodingJob and returns it to client for
+ // Wraps the TranscodingSessionParcel into a TranscodingJob and returns it to client for
// tracking.
TranscodingJob job = new TranscodingJob(this, transcodingRequest,
- jobParcel,
+ sessionParcel,
listenerExecutor,
listener);
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 4380c13..ed99fad 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -25,7 +25,7 @@
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
import android.media.tv.ITvInputManagerCallback;
-import android.media.tv.TvChannelInfo;
+import android.media.tv.TunedInfo;
import android.media.tv.TvContentRatingSystemInfo;
import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvInputInfo;
@@ -89,7 +89,7 @@
void timeShiftSetPlaybackParams(in IBinder sessionToken, in PlaybackParams params, int userId);
void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);
- List<TvChannelInfo> getCurrentTvChannelInfos(int userId);
+ List<TunedInfo> getCurrentTunedInfos(int userId);
// For the recording session
void startRecording(in IBinder sessionToken, in Uri programUri, in Bundle params, int userId);
diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl
index 9f80bf5..3128ba7 100644
--- a/media/java/android/media/tv/ITvInputManagerCallback.aidl
+++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl
@@ -16,7 +16,7 @@
package android.media.tv;
-import android.media.tv.TvChannelInfo;
+import android.media.tv.TunedInfo;
import android.media.tv.TvInputInfo;
/**
@@ -29,5 +29,5 @@
void onInputUpdated(in String inputId);
void onInputStateChanged(in String inputId, int state);
void onTvInputInfoUpdated(in TvInputInfo TvInputInfo);
- void onCurrentTvChannelInfosUpdated(in List<TvChannelInfo> currentTvChannelInfos);
+ void onCurrentTunedInfosUpdated(in List<TunedInfo> currentTunedInfos);
}
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/media/java/android/media/tv/TunedInfo.aidl
similarity index 95%
rename from media/java/android/media/tv/TvChannelInfo.aidl
rename to media/java/android/media/tv/TunedInfo.aidl
index 71cd0a7..b7c1d52 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/media/java/android/media/tv/TunedInfo.aidl
@@ -16,4 +16,4 @@
package android.media.tv;
-parcelable TvChannelInfo;
+parcelable TunedInfo;
diff --git a/media/java/android/media/tv/TvChannelInfo.java b/media/java/android/media/tv/TunedInfo.java
similarity index 87%
rename from media/java/android/media/tv/TvChannelInfo.java
rename to media/java/android/media/tv/TunedInfo.java
index 11cb1f7..6199c89 100644
--- a/media/java/android/media/tv/TvChannelInfo.java
+++ b/media/java/android/media/tv/TunedInfo.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -31,11 +32,13 @@
/**
- * This class is used to specify information of a TV channel.
+ * Contains information about a {@link TvInputService.Session} that is currently tuned to a channel
+ * or pass-through input.
* @hide
*/
-public final class TvChannelInfo implements Parcelable {
- static final String TAG = "TvChannelInfo";
+@SystemApi
+public final class TunedInfo implements Parcelable {
+ static final String TAG = "TunedInfo";
/**
* App tag for {@link #getAppTag()}: the corresponding application of the channel is the same as
@@ -67,21 +70,21 @@
@Retention(RetentionPolicy.SOURCE)
public @interface AppType {}
- public static final @NonNull Parcelable.Creator<TvChannelInfo> CREATOR =
- new Parcelable.Creator<TvChannelInfo>() {
+ public static final @NonNull Parcelable.Creator<TunedInfo> CREATOR =
+ new Parcelable.Creator<TunedInfo>() {
@Override
- public TvChannelInfo createFromParcel(Parcel source) {
+ public TunedInfo createFromParcel(Parcel source) {
try {
- return new TvChannelInfo(source);
+ return new TunedInfo(source);
} catch (Exception e) {
- Log.e(TAG, "Exception creating TvChannelInfo from parcel", e);
+ Log.e(TAG, "Exception creating TunedInfo from parcel", e);
return null;
}
}
@Override
- public TvChannelInfo[] newArray(int size) {
- return new TvChannelInfo[size];
+ public TunedInfo[] newArray(int size) {
+ return new TunedInfo[size];
}
};
@@ -94,7 +97,7 @@
private final int mAppTag;
/** @hide */
- public TvChannelInfo(
+ public TunedInfo(
String inputId, @Nullable Uri channelUri, boolean isRecordingSession,
boolean isForeground, @AppType int appType, int appTag) {
mInputId = inputId;
@@ -106,7 +109,7 @@
}
- private TvChannelInfo(Parcel source) {
+ private TunedInfo(Parcel source) {
mInputId = source.readString();
String uriString = source.readString();
mChannelUri = uriString == null ? null : Uri.parse(uriString);
@@ -194,11 +197,11 @@
@Override
public boolean equals(Object o) {
- if (!(o instanceof TvChannelInfo)) {
+ if (!(o instanceof TunedInfo)) {
return false;
}
- TvChannelInfo other = (TvChannelInfo) o;
+ TunedInfo other = (TunedInfo) o;
return TextUtils.equals(mInputId, other.getInputId())
&& Objects.equals(mChannelUri, other.mChannelUri)
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index c80f3c6..e9959be 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -901,12 +901,13 @@
}
/**
- * This is called when the information about current TV channels has been updated.
+ * This is called when the information about current tuned information has been updated.
*
- * @param tvChannelInfos a list of {@link TvChannelInfo} objects of new current channels.
+ * @param tunedInfos a list of {@link TunedInfo} objects of new tuned information.
* @hide
*/
- public void onCurrentTvChannelInfosUpdated(@NonNull List<TvChannelInfo> tvChannelInfos) {
+ @SystemApi
+ public void onCurrentTunedInfosUpdated(@NonNull List<TunedInfo> tunedInfos) {
}
}
@@ -968,12 +969,11 @@
});
}
- public void postCurrentTvChannelInfosUpdated(
- final List<TvChannelInfo> currentTvChannelInfos) {
+ public void postCurrentTunedInfosUpdated(final List<TunedInfo> currentTunedInfos) {
mHandler.post(new Runnable() {
@Override
public void run() {
- mCallback.onCurrentTvChannelInfosUpdated(currentTvChannelInfos);
+ mCallback.onCurrentTunedInfosUpdated(currentTunedInfos);
}
});
}
@@ -1283,10 +1283,10 @@
}
@Override
- public void onCurrentTvChannelInfosUpdated(List<TvChannelInfo> currentTvChannelInfos) {
+ public void onCurrentTunedInfosUpdated(List<TunedInfo> currentTunedInfos) {
synchronized (mLock) {
for (TvInputCallbackRecord record : mCallbackRecords) {
- record.postCurrentTvChannelInfosUpdated(currentTvChannelInfos);
+ record.postCurrentTunedInfosUpdated(currentTunedInfos);
}
}
}
@@ -1981,18 +1981,19 @@
}
/**
- * Returns the list of TV channel information for {@link TvInputService.Session} that are
+ * Returns the list of session information for {@link TvInputService.Session} that are
* currently in use.
* <p> Permission com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS is required to get
- * the channel URIs. If the permission is not granted, {@link TvChannelInfo#getChannelUri()}
- * returns {@code null}.
+ * the channel URIs. If the permission is not granted,
+ * {@link TunedInfo#getChannelUri()} returns {@code null}.
* @hide
*/
+ @SystemApi
@RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS")
@NonNull
- public List<TvChannelInfo> getCurrentTvChannelInfos() {
+ public List<TunedInfo> getCurrentTunedInfos() {
try {
- return mService.getCurrentTvChannelInfos(mUserId);
+ return mService.getCurrentTunedInfos(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index e148d0e..56499e2 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -98,15 +98,49 @@
* Invalid timestamp.
*
* <p>Returned by {@link android.media.tv.tuner.filter.TimeFilter#getSourceTime()},
- * {@link android.media.tv.tuner.filter.TimeFilter#getTimeStamp()}, or
- * {@link Tuner#getAvSyncTime(int)} when the requested timestamp is not available.
+ * {@link android.media.tv.tuner.filter.TimeFilter#getTimeStamp()},
+ * {@link Tuner#getAvSyncTime(int)} or {@link TsRecordEvent#getPts()} and
+ * {@link MmtpRecordEvent#getPts()} when the requested timestamp is not available.
*
* @see android.media.tv.tuner.filter.TimeFilter#getSourceTime()
* @see android.media.tv.tuner.filter.TimeFilter#getTimeStamp()
* @see Tuner#getAvSyncTime(int)
+ * @see android.media.tv.tuner.filter.TsRecordEvent#getPts()
+ * @see android.media.tv.tuner.filter.MmtpRecordEvent#getPts()
*/
- public static final long INVALID_TIMESTAMP = -1L;
-
+ public static final long INVALID_TIMESTAMP =
+ android.hardware.tv.tuner.V1_1.Constants.Constant64Bit.INVALID_PRESENTATION_TIME_STAMP;
+ /**
+ * Invalid mpu sequence number in MmtpRecordEvent.
+ *
+ * <p>Returned by {@link MmtpRecordEvent#getMpuSequenceNumber()} when the requested sequence
+ * number is not available.
+ *
+ * @see android.media.tv.tuner.filter.MmtpRecordEvent#getMpuSequenceNumber()
+ */
+ public static final int INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM =
+ android.hardware.tv.tuner.V1_1.Constants.Constant
+ .INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM;
+ /**
+ * Invalid local transport stream id.
+ *
+ * <p>Returned by {@link #linkFrontendToCiCam(int)} when the requested failed
+ * or the hal implementation does not support the operation.
+ *
+ * @see #linkFrontendToCiCam(int)
+ */
+ public static final int INVALID_LTS_ID =
+ android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_LTS_ID;
+ /**
+ * Invalid 64-bit filter ID.
+ */
+ public static final long INVALID_FILTER_ID_64BIT =
+ android.hardware.tv.tuner.V1_1.Constants.Constant64Bit.INVALID_FILTER_ID_64BIT;
+ /**
+ * Invalid frequency that is used as the default frontend frequency setting.
+ */
+ public static final int INVALID_FRONTEND_SETTING_FREQUENCY =
+ android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_FRONTEND_SETTING_FREQUENCY;
/** @hide */
@IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
@@ -204,6 +238,7 @@
private final Context mContext;
private final TunerResourceManager mTunerResourceManager;
private final int mClientId;
+ private static int sTunerVersion = TunerVersionChecker.TUNER_VERSION_UNKNOWN;
private Frontend mFrontend;
private EventHandler mHandler;
@@ -255,6 +290,14 @@
public Tuner(@NonNull Context context, @Nullable String tvInputSessionId,
@TvInputService.PriorityHintUseCaseType int useCase) {
nativeSetup();
+ sTunerVersion = nativeGetTunerVersion();
+ if (sTunerVersion == TunerVersionChecker.TUNER_VERSION_UNKNOWN) {
+ Log.e(TAG, "Unknown Tuner version!");
+ } else {
+ Log.d(TAG, "Current Tuner version is "
+ + TunerVersionChecker.getMajorVersion(sTunerVersion) + "."
+ + TunerVersionChecker.getMinorVersion(sTunerVersion) + ".");
+ }
mContext = context;
mTunerResourceManager = (TunerResourceManager)
context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE);
@@ -295,6 +338,11 @@
}
/** @hide */
+ public static int getTunerVersion() {
+ return sTunerVersion;
+ }
+
+ /** @hide */
public List<Integer> getFrontendIds() {
return nativeGetFrontendIds();
}
@@ -419,6 +467,11 @@
/**
* Native method to get all frontend IDs.
*/
+ private native int nativeGetTunerVersion();
+
+ /**
+ * Native method to get all frontend IDs.
+ */
private native List<Integer> nativeGetFrontendIds();
/**
@@ -437,7 +490,9 @@
private native Integer nativeGetAvSyncHwId(Filter filter);
private native Long nativeGetAvSyncTime(int avSyncId);
private native int nativeConnectCiCam(int ciCamId);
+ private native int nativeLinkCiCam(int ciCamId);
private native int nativeDisconnectCiCam();
+ private native int nativeUnlinkCiCam(int ciCamId);
private native FrontendInfo nativeGetFrontendInfo(int id);
private native Filter nativeOpenFilter(int type, int subType, long bufferSize);
private native TimeFilter nativeOpenTimeFilter();
@@ -568,6 +623,10 @@
* OnTuneEventListener#SIGNAL_NO_SIGNAL} events sent to the {@link OnTuneEventListener}
* specified in {@link #setOnTuneEventListener(Executor, OnTuneEventListener)}.
*
+ * <p>Tuning with {@link android.media.tv.tuner.frontend.DtmbFrontendSettings} is only
+ * supported in Tuner 1.1 or higher version. Unsupported version will cause no-op. Use {@link
+ * TunerVersionChecker.getTunerVersion()} to get the version information.
+ *
* @param settings Signal delivery information the frontend uses to
* search and lock the signal.
* @return result status of tune operation.
@@ -578,6 +637,12 @@
public int tune(@NonNull FrontendSettings settings) {
Log.d(TAG, "Tune to " + settings.getFrequency());
mFrontendType = settings.getType();
+ if (mFrontendType == FrontendSettings.TYPE_DTMB) {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_1_1, "Tuner with DTMB Frontend")) {
+ return RESULT_UNAVAILABLE;
+ }
+ }
if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
mFrontendInfo = null;
Log.d(TAG, "Write Stats Log for tuning.");
@@ -607,6 +672,10 @@
*
* <p>Details for channels found are returned via {@link ScanCallback}.
*
+ * <p>Scanning with {@link android.media.tv.tuner.frontend.DtmbFrontendSettings} is only
+ * supported in Tuner 1.1 or higher version. Unsupported version will cause no-op. Use {@link
+ * TunerVersionChecker.getTunerVersion()} to get the version information.
+ *
* @param settings A {@link FrontendSettings} to configure the frontend.
* @param scanType The scan type.
* @throws SecurityException if the caller does not have appropriate permissions.
@@ -622,6 +691,12 @@
+ "started.");
}
mFrontendType = settings.getType();
+ if (mFrontendType == FrontendSettings.TYPE_DTMB) {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_1_1, "Scan with DTMB Frontend")) {
+ return RESULT_UNAVAILABLE;
+ }
+ }
if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
mScanCallback = scanCallback;
mScanCallbackExecutor = executor;
@@ -760,6 +835,33 @@
}
/**
+ * Link Conditional Access Modules (CAM) Frontend to support Common Interface (CI) by-pass mode.
+ *
+ * <p>It is used by the client to link CI-CAM to a Frontend. CI by-pass mode requires that
+ * the CICAM also receives the TS concurrently from the frontend when the Demux is receiving
+ * the TS directly from the frontend.
+ *
+ * <p>Use {@link #unlinkFrontendToCicam(int)} to disconnect.
+ *
+ * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+ * no-op and return {@link INVALID_LTS_ID}. Use {@link TunerVersionChecker.getTunerVersion()} to
+ * check the version.
+ *
+ * @param ciCamId specify CI-CAM Id to link.
+ * @return Local transport stream id when connection is successfully established. Failed
+ * operation returns {@link INVALID_LTS_ID}.
+ */
+ public int linkFrontendToCiCam(int ciCamId) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
+ "linkFrontendToCiCam")) {
+ if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
+ return nativeLinkCiCam(ciCamId);
+ }
+ }
+ return INVALID_LTS_ID;
+ }
+
+ /**
* Disconnects Conditional Access Modules (CAM)
*
* <p>The demux will use the output from the frontend as the input after this call.
@@ -775,6 +877,28 @@
}
/**
+ * Unlink Conditional Access Modules (CAM) Frontend.
+ *
+ * <p>It is used by the client to unlink CI-CAM to a Frontend.
+ *
+ * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+ * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+ *
+ * @param ciCamId specify CI-CAM Id to unlink.
+ * @return result status of the operation.
+ */
+ @Result
+ public int unlinkFrontendToCiCam(int ciCamId) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1,
+ "unlinkFrontendToCiCam")) {
+ if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
+ return nativeUnlinkCiCam(ciCamId);
+ }
+ }
+ return RESULT_UNAVAILABLE;
+ }
+
+ /**
* Gets the frontend information.
*
* @return The frontend information. {@code null} if the operation failed.
diff --git a/media/java/android/media/tv/tuner/TunerVersionChecker.java b/media/java/android/media/tv/tuner/TunerVersionChecker.java
new file mode 100644
index 0000000..739f87d
--- /dev/null
+++ b/media/java/android/media/tv/tuner/TunerVersionChecker.java
@@ -0,0 +1,153 @@
+/*
+ * 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.media.tv.tuner;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Utility class to check the currently running Tuner Hal implementation version.
+ *
+ * APIs that are not supported by the HAL implementation version would be no-op.
+ *
+ * @hide
+ */
+@TestApi
+@SystemApi
+public final class TunerVersionChecker {
+ private static final String TAG = "TunerVersionChecker";
+
+ private TunerVersionChecker() {}
+
+ /** @hide */
+ @IntDef(prefix = "TUNER_VERSION_", value = {TUNER_VERSION_UNKNOWN, TUNER_VERSION_1_0,
+ TUNER_VERSION_1_1})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TunerVersion {}
+ /**
+ * Unknown Tuner version.
+ */
+ public static final int TUNER_VERSION_UNKNOWN = 0;
+ /**
+ * Tuner version 1.0.
+ */
+ public static final int TUNER_VERSION_1_0 = (1 << 16);
+ /**
+ * Tuner version 1.1.
+ */
+ public static final int TUNER_VERSION_1_1 = ((1 << 16) | 1);
+
+ /**
+ * Get the current running Tuner version.
+ *
+ * @return Tuner version.
+ */
+ @TunerVersion
+ public static int getTunerVersion() {
+ return Tuner.getTunerVersion();
+ }
+
+ /**
+ * Check if the current running Tuner version supports the given version.
+ *
+ * <p>Note that we treat different major versions as unsupported among each other. If any
+ * feature could be supported across major versions, please use
+ * {@link #isHigherOrEqualVersionTo(int)} to check.
+ *
+ * @param version the version to support.
+ *
+ * @return true if the current version is under the same major version as the given version
+ * and has higher or the same minor version as the given version.
+ * @hide
+ */
+ @TestApi
+ public static boolean supportTunerVersion(@TunerVersion int version) {
+ int currentVersion = Tuner.getTunerVersion();
+ return isHigherOrEqualVersionTo(version)
+ && (getMajorVersion(version) == getMajorVersion(currentVersion));
+ }
+
+ /**
+ * Check if the current running Tuner version is higher than or equal to a given version.
+ *
+ * @param version the version to compare.
+ *
+ * @return true if the current version is higher or equal to the support version.
+ * @hide
+ */
+ @TestApi
+ public static boolean isHigherOrEqualVersionTo(@TunerVersion int version) {
+ int currentVersion = Tuner.getTunerVersion();
+ return currentVersion >= version;
+ }
+
+ /**
+ * Get the major version from a version number.
+ *
+ * @param version the version to be checked.
+ *
+ * @return the major version number.
+ * @hide
+ */
+ @TestApi
+ public static int getMajorVersion(@TunerVersion int version) {
+ return ((version & 0xFFFF0000) >>> 16);
+ }
+
+ /**
+ * Get the major version from a version number.
+ *
+ * @param version the version to be checked.
+ *
+ * @return the minor version number.
+ * @hide
+ */
+ @TestApi
+ public static int getMinorVersion(@TunerVersion int version) {
+ return (version & 0xFFFF);
+ }
+
+ /** @hide */
+ public static boolean checkHigherOrEqualVersionTo(
+ @TunerVersion int version, String methodName) {
+ if (!TunerVersionChecker.isHigherOrEqualVersionTo(version)) {
+ Log.e(TAG, "Current Tuner version "
+ + TunerVersionChecker.getMajorVersion(Tuner.getTunerVersion()) + "."
+ + TunerVersionChecker.getMinorVersion(Tuner.getTunerVersion())
+ + " does not support " + methodName + ".");
+ return false;
+ }
+ return true;
+ }
+
+ /** @hide */
+ public static boolean checkSupportVersion(@TunerVersion int version, String methodName) {
+ if (!TunerVersionChecker.supportTunerVersion(version)) {
+ Log.e(TAG, "Current Tuner version "
+ + TunerVersionChecker.getMajorVersion(Tuner.getTunerVersion()) + "."
+ + TunerVersionChecker.getMinorVersion(Tuner.getTunerVersion())
+ + " does not support " + methodName + ".");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index bb00bb3..597278b 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -123,13 +123,14 @@
/**
* Attaches a filter to DVR interface for playback.
*
- * <p>This method will be deprecated. Now it's a no-op.
- * <p>Filters opened by {@link Tuner#openFilter} are used for DVR playback.
+ * @deprecated attaching filters is not valid in Dvr Playback use case. This API is a no-op.
+ * Filters opened by {@link Tuner#openFilter} are used for DVR playback.
*
* @param filter the filter to be attached.
* @return result status of the operation.
*/
@Result
+ @Deprecated
public int attachFilter(@NonNull Filter filter) {
// no-op
return Tuner.RESULT_UNAVAILABLE;
@@ -138,13 +139,14 @@
/**
* Detaches a filter from DVR interface.
*
- * <p>This method will be deprecated. Now it's a no-op.
- * <p>Filters opened by {@link Tuner#openFilter} are used for DVR playback.
+ * @deprecated detaching filters is not valid in Dvr Playback use case. This API is a no-op.
+ * Filters opened by {@link Tuner#openFilter} are used for DVR playback.
*
* @param filter the filter to be detached.
* @return result status of the operation.
*/
@Result
+ @Deprecated
public int detachFilter(@NonNull Filter filter) {
// no-op
return Tuner.RESULT_UNAVAILABLE;
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index f0015b7..2f2d8f7 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -185,7 +185,7 @@
private long mNativeContext;
private FilterCallback mCallback;
private Executor mExecutor;
- private final int mId;
+ private final long mId;
private int mMainType;
private int mSubtype;
private Filter mSource;
@@ -196,6 +196,7 @@
private native int nativeConfigureFilter(
int type, int subType, FilterConfiguration settings);
private native int nativeGetId();
+ private native long nativeGetId64Bit();
private native int nativeSetDataSource(Filter source);
private native int nativeStartFilter();
private native int nativeStopFilter();
@@ -204,7 +205,7 @@
private native int nativeClose();
// Called by JNI
- private Filter(int id) {
+ private Filter(long id) {
mId = id;
}
@@ -269,6 +270,16 @@
}
/**
+ * Gets the 64-bit filter Id.
+ */
+ public long getId64Bit() {
+ synchronized (mLock) {
+ TunerUtils.checkResourceState(TAG, mIsClosed);
+ return nativeGetId64Bit();
+ }
+ }
+
+ /**
* Sets the filter's data source.
*
* A filter uses demux as data source by default. If the data was packetized
diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
index f54b686..2649fcf 100644
--- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.SystemApi;
+import android.media.tv.tuner.TunerVersionChecker;
/**
* Filter configuration for a IP filter.
@@ -28,20 +29,28 @@
*/
@SystemApi
public final class IpFilterConfiguration extends FilterConfiguration {
+ /**
+ * Undefined filter type.
+ */
+ public static final int INVALID_IP_FILTER_CONTEXT_ID =
+ android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_IP_FILTER_CONTEXT_ID;
+
private final byte[] mSrcIpAddress;
private final byte[] mDstIpAddress;
private final int mSrcPort;
private final int mDstPort;
private final boolean mPassthrough;
+ private final int mIpFilterContextId;
private IpFilterConfiguration(Settings settings, byte[] srcAddr, byte[] dstAddr, int srcPort,
- int dstPort, boolean passthrough) {
+ int dstPort, boolean passthrough, int ipCid) {
super(settings);
mSrcIpAddress = srcAddr;
mDstIpAddress = dstAddr;
mSrcPort = srcPort;
mDstPort = dstPort;
mPassthrough = passthrough;
+ mIpFilterContextId = ipCid;
}
@Override
@@ -86,6 +95,15 @@
public boolean isPassthrough() {
return mPassthrough;
}
+ /**
+ * Gets the ip filter context id. Default value is {@link #INVALID_IP_FILTER_CONTEXT_ID}.
+ *
+ * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would return
+ * default value. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+ */
+ public int getIpFilterContextId() {
+ return mIpFilterContextId;
+ }
/**
* Creates a builder for {@link IpFilterConfiguration}.
@@ -105,6 +123,7 @@
private int mDstPort = 0;
private boolean mPassthrough = false;
private Settings mSettings;
+ private int mIpCid = INVALID_IP_FILTER_CONTEXT_ID;
private Builder() {
}
@@ -170,6 +189,21 @@
}
/**
+ * Sets the ip filter context id. Default value is {@link #INVALID_IP_FILTER_CONTEXT_ID}.
+ *
+ * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+ * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+ */
+ @NonNull
+ public Builder setIpFilterContextId(int ipContextId) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_1_1, "setIpFilterContextId")) {
+ mIpCid = ipContextId;
+ }
+ return this;
+ }
+
+ /**
* Builds a {@link IpFilterConfiguration} object.
*/
@NonNull
@@ -180,8 +214,8 @@
"The lengths of src and dst IP address must be 4 or 16 and must be the same."
+ "srcLength=" + ipAddrLength + ", dstLength=" + mDstIpAddress.length);
}
- return new IpFilterConfiguration(
- mSettings, mSrcIpAddress, mDstIpAddress, mSrcPort, mDstPort, mPassthrough);
+ return new IpFilterConfiguration(mSettings, mSrcIpAddress, mDstIpAddress, mSrcPort,
+ mDstPort, mPassthrough, mIpCid);
}
}
}
diff --git a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
index 466fa3e..7060bd72 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
@@ -29,11 +29,15 @@
public class MmtpRecordEvent extends FilterEvent {
private final int mScHevcIndexMask;
private final long mDataLength;
+ private final int mMpuSequenceNumber;
+ private final long mPts;
// This constructor is used by JNI code only
- private MmtpRecordEvent(int scHevcIndexMask, long dataLength) {
+ private MmtpRecordEvent(int scHevcIndexMask, long dataLength, int mpuSequenceNumber, long pts) {
mScHevcIndexMask = scHevcIndexMask;
mDataLength = dataLength;
+ mMpuSequenceNumber = mpuSequenceNumber;
+ mPts = pts;
}
/**
@@ -51,4 +55,20 @@
public long getDataLength() {
return mDataLength;
}
+
+ /**
+ * Get the MPU sequence number of the filtered data.
+ */
+ public int getMpuSequenceNumber() {
+ return mMpuSequenceNumber;
+ }
+
+ /**
+ * Get the Presentation Time Stamp(PTS) for the audio or video frame. It is based on 90KHz
+ * and has the same format as the PTS in ISO/IEC 13818-1. It is used only for the SC and
+ * the SC_HEVC.
+ */
+ public long getPts() {
+ return mPts;
+ }
}
diff --git a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
index 7a14bb8..258e2f2 100644
--- a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
@@ -32,13 +32,15 @@
private final int mTsIndexMask;
private final int mScIndexMask;
private final long mDataLength;
+ private final long mPts;
// This constructor is used by JNI code only
- private TsRecordEvent(int pid, int tsIndexMask, int scIndexMask, long dataLength) {
+ private TsRecordEvent(int pid, int tsIndexMask, int scIndexMask, long dataLength, long pts) {
mPid = pid;
mTsIndexMask = tsIndexMask;
mScIndexMask = scIndexMask;
mDataLength = dataLength;
+ mPts = pts;
}
/**
@@ -72,4 +74,13 @@
public long getDataLength() {
return mDataLength;
}
+
+ /**
+ * Gets the Presentation Time Stamp(PTS) for the audio or video frame. It is based on 90KHz
+ * and has the same format as the PTS in ISO/IEC 13818-1. It is used only for the SC and
+ * the SC_HEVC.
+ */
+ public long getPts() {
+ return mPts;
+ }
}
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
index 1d36da3..c6a5bb0 100644
--- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.TunerVersionChecker;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -164,9 +165,32 @@
*/
public static final int SIF_L_PRIME = Constants.FrontendAnalogSifStandard.L_PRIME;
+ /** @hide */
+ @IntDef(prefix = "AFT_FLAG_",
+ value = {AFT_FLAG_UNDEFINED, AFT_FLAG_TRUE, AFT_FLAG_FALSE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AftFlag {}
+
+ /**
+ * Aft flag is not defined.
+ */
+ public static final int AFT_FLAG_UNDEFINED =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendAnalogAftFlag.UNDEFINED;
+ /**
+ * Aft flag is set true.
+ */
+ public static final int AFT_FLAG_TRUE =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendAnalogAftFlag.AFT_TRUE;
+ /**
+ * Aft flag is not set.
+ */
+ public static final int AFT_FLAG_FALSE =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendAnalogAftFlag.AFT_FALSE;
+
private final int mSignalType;
private final int mSifStandard;
+ private final int mAftFlag;
@Override
public int getType() {
@@ -191,6 +215,14 @@
}
/**
+ * Gets AFT flag.
+ */
+ @AftFlag
+ public int getAftFlag() {
+ return mAftFlag;
+ }
+
+ /**
* Creates a builder for {@link AnalogFrontendSettings}.
*/
@NonNull
@@ -198,10 +230,11 @@
return new Builder();
}
- private AnalogFrontendSettings(int frequency, int signalType, int sifStandard) {
+ private AnalogFrontendSettings(int frequency, int signalType, int sifStandard, int aftFlag) {
super(frequency);
mSignalType = signalType;
mSifStandard = sifStandard;
+ mAftFlag = aftFlag;
}
/**
@@ -211,6 +244,7 @@
private int mFrequency = 0;
private int mSignalType = SIGNAL_TYPE_UNDEFINED;
private int mSifStandard = SIF_UNDEFINED;
+ private int mAftFlag = AFT_FLAG_UNDEFINED;
private Builder() {}
@@ -227,6 +261,24 @@
}
/**
+ * Set Aft flag.
+ *
+ * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+ * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+ *
+ * @param aftFlag the value to set the aft flag. The default value is
+ * {@link #AFT_FLAG_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setAftFlag(@AftFlag int aftFlag) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_1_1, "setAftFlag")) {
+ mAftFlag = aftFlag;
+ }
+ return this;
+ }
+
+ /**
* Sets analog signal type.
*
* <p>Default value is {@link #SIGNAL_TYPE_UNDEFINED}.
@@ -253,7 +305,7 @@
*/
@NonNull
public AnalogFrontendSettings build() {
- return new AnalogFrontendSettings(mFrequency, mSignalType, mSifStandard);
+ return new AnalogFrontendSettings(mFrequency, mSignalType, mSifStandard, mAftFlag);
}
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
index f9eabc5..ed1ce2d 100644
--- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
@@ -377,8 +377,8 @@
*/
@NonNull
public Atsc3FrontendSettings build() {
- return new Atsc3FrontendSettings(
- mFrequency, mBandwidth, mDemodOutputFormat, mPlpSettings);
+ return new Atsc3FrontendSettings(mFrequency, mBandwidth, mDemodOutputFormat,
+ mPlpSettings);
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java
new file mode 100644
index 0000000..9fc3a23
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java
@@ -0,0 +1,114 @@
+/*
+ * 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.media.tv.tuner.frontend;
+
+import android.annotation.SystemApi;
+
+/**
+ * DTMB Capabilities.
+ *
+ * <p>DTMB Frontend is only supported in Tuner HAL 1.1 or higher.
+ * @hide
+ */
+@SystemApi
+public class DtmbFrontendCapabilities extends FrontendCapabilities {
+ private final int mModulationCap;
+ private final int mTransmissionModeCap;
+ private final int mGuardIntervalCap;
+ private final int mTimeInterleaveModeCap;
+ private final int mCodeRateCap;
+ private final int mBandwidthCap;
+
+ private DtmbFrontendCapabilities(int modulationCap, int transmissionModeCap,
+ int guardIntervalCap, int timeInterleaveModeCap, int codeRateCap, int bandwidthCap) {
+ mModulationCap = modulationCap;
+ mTransmissionModeCap = transmissionModeCap;
+ mGuardIntervalCap = guardIntervalCap;
+ mTimeInterleaveModeCap = timeInterleaveModeCap;
+ mCodeRateCap = codeRateCap;
+ mBandwidthCap = bandwidthCap;
+ }
+
+ /**
+ * Gets modulation capability.
+ *
+ * @return full modulation capabilies. If the caps bitwise AND with any value from
+ * bit masks {@link DtmbFrontendSettings.Modulation} is true, then that modulation is supported.
+ */
+ @DtmbFrontendSettings.Modulation
+ public int getModulationCapability() {
+ return mModulationCap;
+ }
+
+ /**
+ * Gets Transmission Mode capability.
+ *
+ * @return full Transmission Mode capabilies. If the caps bitwise AND with any value from
+ * bit masks {@link DtmbFrontendSettings.TransmissionMode} is true, then that transmission mode
+ * is supported.
+ */
+ @DtmbFrontendSettings.TransmissionMode
+ public int getTransmissionModeCapability() {
+ return mTransmissionModeCap;
+ }
+
+ /**
+ * Gets Guard Interval capability.
+ *
+ * @return full Guard Interval capabilies. If the caps bitwise AND with any value from
+ * bit masks {@link DtmbFrontendSettings.GuardInterval} is true, then that Guard Interval is
+ * supported.
+ */
+ @DtmbFrontendSettings.GuardInterval
+ public int getGuardIntervalCapability() {
+ return mGuardIntervalCap;
+ }
+
+ /**
+ * Gets Time Interleave Mode capability.
+ *
+ * @return full Time Interleave Mode capabilies. If the caps bitwise AND with any value from
+ * bit masks {@link DtmbFrontendSettings.TimeInterleaveMode} is true, then that Time Interleave
+ * Mode is supported.
+ */
+ @DtmbFrontendSettings.TimeInterleaveMode
+ public int getTimeInterleaveModeCapability() {
+ return mTimeInterleaveModeCap;
+ }
+
+ /**
+ * Gets Code Rate capability.
+ *
+ * @return full Code Rate capabilies. If the caps bitwise AND with any value from
+ * bit masks {@link DtmbFrontendSettings.CodeRate} is true, then that Code Rate is supported.
+ */
+ @DtmbFrontendSettings.CodeRate
+ public int getCodeRateCapability() {
+ return mCodeRateCap;
+ }
+
+ /**
+ * Gets Bandwidth capability.
+ *
+ * @return full Bandwidth capabilies. If the caps bitwise AND with any value from
+ * bit masks {@link DtmbFrontendSettings.Bandwidth} is true, then that Bandwidth is supported.
+ */
+ @DtmbFrontendSettings.Bandwidth
+ public int getBandwidthCapability() {
+ return mBandwidthCap;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
new file mode 100644
index 0000000..2c3fe6a
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
@@ -0,0 +1,441 @@
+/*
+ * 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.media.tv.tuner.frontend;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Frontend settings for DTMB.
+ *
+ * <p>DTMB Frontend is only supported in Tuner HAL 1.1 or higher. Use {@link
+ * android.media.tv.tuner.TunerVersionChecker.getTunerVersion()} to get the version information.
+ *
+ * @hide
+ */
+@SystemApi
+public class DtmbFrontendSettings extends FrontendSettings {
+
+ /** @hide */
+ @IntDef(flag = true,
+ prefix = "BANDWIDTH_",
+ value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_6MHZ, BANDWIDTH_8MHZ})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Bandwidth {}
+
+ /**
+ * Bandwidth not defined.
+ */
+ public static final int BANDWIDTH_UNDEFINED =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.UNDEFINED;
+ /**
+ * Hardware is able to detect and set bandwidth automatically
+ */
+ public static final int BANDWIDTH_AUTO =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.AUTO;
+ /**
+ * 6 MHz bandwidth.
+ */
+ public static final int BANDWIDTH_6MHZ =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.BANDWIDTH_6MHZ;
+ /**
+ * 8 MHz bandwidth.
+ */
+ public static final int BANDWIDTH_8MHZ =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.BANDWIDTH_8MHZ;
+
+
+ /** @hide */
+ @IntDef(flag = true,
+ prefix = "TIME_INTERLEAVE_MODE_",
+ value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO,
+ TIME_INTERLEAVE_MODE_TIMER_INT_240, TIME_INTERLEAVE_MODE_TIMER_INT_720})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TimeInterleaveMode {}
+
+ /**
+ * Time Interleave Mode undefined.
+ */
+ public static final int TIME_INTERLEAVE_MODE_UNDEFINED =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.UNDEFINED;
+ /**
+ * Hardware is able to detect and set time interleave mode automatically
+ */
+ public static final int TIME_INTERLEAVE_MODE_AUTO =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.AUTO;
+ /**
+ * Time Interleave Mode timer int 240.
+ */
+ public static final int TIME_INTERLEAVE_MODE_TIMER_INT_240 =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.TIMER_INT_240;
+ /**
+ * Time Interleave Mode timer int 720.
+ */
+ public static final int TIME_INTERLEAVE_MODE_TIMER_INT_720 =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.TIMER_INT_720;
+
+
+ /** @hide */
+ @IntDef(flag = true,
+ prefix = "GUARD_INTERVAL_",
+ value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO,
+ GUARD_INTERVAL_PN_420_VARIOUS, GUARD_INTERVAL_PN_595_CONST,
+ GUARD_INTERVAL_PN_945_VARIOUS, GUARD_INTERVAL_PN_420_CONST,
+ GUARD_INTERVAL_PN_945_CONST, GUARD_INTERVAL_PN_RESERVED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface GuardInterval {}
+
+ /**
+ * Guard Interval undefined.
+ */
+ public static final int GUARD_INTERVAL_UNDEFINED =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.UNDEFINED;
+ /**
+ * Hardware is able to detect and set Guard Interval automatically.
+ */
+ public static final int GUARD_INTERVAL_AUTO =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.AUTO;
+ /**
+ * PN_420_VARIOUS Guard Interval.
+ */
+ public static final int GUARD_INTERVAL_PN_420_VARIOUS =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_420_VARIOUS;
+ /**
+ * PN_595_CONST Guard Interval.
+ */
+ public static final int GUARD_INTERVAL_PN_595_CONST =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_595_CONST;
+ /**
+ * PN_945_VARIOUS Guard Interval.
+ */
+ public static final int GUARD_INTERVAL_PN_945_VARIOUS =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_945_VARIOUS;
+ /**
+ * PN_420_CONST Guard Interval.
+ */
+ public static final int GUARD_INTERVAL_PN_420_CONST =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_420_CONST;
+ /**
+ * PN_945_CONST Guard Interval.
+ */
+ public static final int GUARD_INTERVAL_PN_945_CONST =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_945_CONST;
+ /**
+ * PN_RESERVED Guard Interval.
+ */
+ public static final int GUARD_INTERVAL_PN_RESERVED =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_RESERVED;
+
+
+ /** @hide */
+ @IntDef(flag = true,
+ prefix = "MODULATION_",
+ value = {MODULATION_CONSTELLATION_UNDEFINED, MODULATION_CONSTELLATION_AUTO,
+ MODULATION_CONSTELLATION_4QAM, MODULATION_CONSTELLATION_4QAM_NR,
+ MODULATION_CONSTELLATION_16QAM, MODULATION_CONSTELLATION_32QAM,
+ MODULATION_CONSTELLATION_64QAM})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Modulation {}
+
+ /**
+ * Constellation not defined.
+ */
+ public static final int MODULATION_CONSTELLATION_UNDEFINED =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.UNDEFINED;
+ /**
+ * Hardware is able to detect and set Constellation automatically.
+ */
+ public static final int MODULATION_CONSTELLATION_AUTO =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.AUTO;
+ /**
+ * 4QAM Constellation.
+ */
+ public static final int MODULATION_CONSTELLATION_4QAM =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_4QAM;
+ /**
+ * 4QAM_NR Constellation.
+ */
+ public static final int MODULATION_CONSTELLATION_4QAM_NR =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_4QAM_NR;
+ /**
+ * 16QAM Constellation.
+ */
+ public static final int MODULATION_CONSTELLATION_16QAM =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_16QAM;
+ /**
+ * 32QAM Constellation.
+ */
+ public static final int MODULATION_CONSTELLATION_32QAM =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_32QAM;
+ /**
+ * 64QAM Constellation.
+ */
+ public static final int MODULATION_CONSTELLATION_64QAM =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_64QAM;
+
+ /** @hide */
+ @IntDef(flag = true,
+ prefix = "CODERATE_",
+ value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_2_5, CODERATE_3_5, CODERATE_4_5})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CodeRate {}
+
+ /**
+ * Code rate undefined.
+ */
+ public static final int CODERATE_UNDEFINED =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.UNDEFINED;
+ /**
+ * Hardware is able to detect and set code rate automatically.
+ */
+ public static final int CODERATE_AUTO =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.AUTO;
+ /**
+ * 2/5 code rate.
+ */
+ public static final int CODERATE_2_5 =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.CODERATE_2_5;
+ /**
+ * 3/5 code rate.
+ */
+ public static final int CODERATE_3_5 =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.CODERATE_3_5;
+ /**
+ * 4/5 code rate.
+ */
+ public static final int CODERATE_4_5 =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.CODERATE_4_5;
+
+ /** @hide */
+ @IntDef(flag = true,
+ prefix = "TRANSMISSION_MODE_",
+ value = {TRANSMISSION_MODE_UNDEFINED, TRANSMISSION_MODE_AUTO,
+ TRANSMISSION_MODE_C1, TRANSMISSION_MODE_C3780})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TransmissionMode {}
+
+ /**
+ * Transmission Mode undefined.
+ */
+ public static final int TRANSMISSION_MODE_UNDEFINED =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.UNDEFINED;
+ /**
+ * Hardware is able to detect and set Transmission Mode automatically
+ */
+ public static final int TRANSMISSION_MODE_AUTO =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.AUTO;
+ /**
+ * C1 Transmission Mode.
+ */
+ public static final int TRANSMISSION_MODE_C1 =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.C1;
+ /**
+ * C3780 Transmission Mode.
+ */
+ public static final int TRANSMISSION_MODE_C3780 =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.C3780;
+
+
+ private final int mModulation;
+ private final int mCodeRate;
+ private final int mTransmissionMode;
+ private final int mBandwidth;
+ private final int mGuardInterval;
+ private final int mTimeInterleaveMode;
+
+ private DtmbFrontendSettings(int frequency, int modulation, int codeRate, int transmissionMode,
+ int guardInterval, int timeInterleaveMode, int bandwidth) {
+ super(frequency);
+ mModulation = modulation;
+ mCodeRate = codeRate;
+ mTransmissionMode = transmissionMode;
+ mGuardInterval = guardInterval;
+ mTimeInterleaveMode = timeInterleaveMode;
+ mBandwidth = bandwidth;
+ }
+
+ /**
+ * Gets Modulation.
+ */
+ @Modulation
+ public int getModulation() {
+ return mModulation;
+ }
+
+ /**
+ * Gets Code Rate.
+ */
+ @Modulation
+ public int getCodeRate() {
+ return mCodeRate;
+ }
+
+ /**
+ * Gets Transmission Mode.
+ */
+ @Modulation
+ public int getTransmissionMode() {
+ return mTransmissionMode;
+ }
+
+ /**
+ * Gets Bandwidth.
+ */
+ @Modulation
+ public int getBandwidth() {
+ return mBandwidth;
+ }
+
+ /**
+ * Gets Time Interleave Mode.
+ */
+ @Modulation
+ public int getTimeInterleaveMode() {
+ return mTimeInterleaveMode;
+ }
+
+
+ /**
+ * Gets Guard Interval.
+ */
+ @Modulation
+ public int getGuardInterval() {
+ return mGuardInterval;
+ }
+
+ /**
+ * Creates a builder for {@link AtscFrontendSettings}.
+ */
+ @NonNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link AtscFrontendSettings}.
+ */
+ public static final class Builder {
+ private int mFrequency = 0;
+ private int mModulation = MODULATION_CONSTELLATION_UNDEFINED;
+ private int mCodeRate = CODERATE_UNDEFINED;
+ private int mTransmissionMode = TRANSMISSION_MODE_UNDEFINED;
+ private int mBandwidth = BANDWIDTH_UNDEFINED;
+ private int mTimeInterleaveMode = TIME_INTERLEAVE_MODE_UNDEFINED;
+ private int mGuardInterval = GUARD_INTERVAL_UNDEFINED;
+
+ private Builder() {
+ }
+
+ /**
+ * Sets frequency in Hz.
+ *
+ * <p>Default value is 0.
+ */
+ @NonNull
+ @IntRange(from = 1)
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setFrequency(int frequency) {
+ mFrequency = frequency;
+ return this;
+ }
+
+ /**
+ * Sets Modulation.
+ *
+ * <p>Default value is {@link #MODULATION_CONSTELLATION_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setModulation(@Modulation int modulation) {
+ mModulation = modulation;
+ return this;
+ }
+
+ /**
+ * Sets Code Rate.
+ *
+ * <p>Default value is {@link #CODERATE_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setCodeRate(@CodeRate int codeRate) {
+ mCodeRate = codeRate;
+ return this;
+ }
+
+ /**
+ * Sets Bandwidth.
+ *
+ * <p>Default value is {@link #BANDWIDTH_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setBandwidth(@Bandwidth int bandwidth) {
+ mBandwidth = bandwidth;
+ return this;
+ }
+
+ /**
+ * Sets Time Interleave Mode.
+ *
+ * <p>Default value is {@link #TIME_INTERLEAVE_MODE_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setTimeInterleaveMode(@TimeInterleaveMode int timeInterleaveMode) {
+ mTimeInterleaveMode = timeInterleaveMode;
+ return this;
+ }
+
+ /**
+ * Sets Guard Interval.
+ *
+ * <p>Default value is {@link #GUARD_INTERVAL_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setGuardInterval(@GuardInterval int guardInterval) {
+ mGuardInterval = guardInterval;
+ return this;
+ }
+ /**
+ * Sets Transmission Mode.
+ *
+ * <p>Default value is {@link #TRANSMISSION_MODE_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setTransmissionMode(@TransmissionMode int transmissionMode) {
+ mTransmissionMode = transmissionMode;
+ return this;
+ }
+
+ /**
+ * Builds a {@link DtmbFrontendSettings} object.
+ */
+ @NonNull
+ public DtmbFrontendSettings build() {
+ return new DtmbFrontendSettings(mFrequency, mModulation, mCodeRate,
+ mTransmissionMode, mGuardInterval, mTimeInterleaveMode, mBandwidth);
+ }
+ }
+
+ @Override
+ public int getType() {
+ return FrontendSettings.TYPE_DTMB;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
index 271e91e..e6968bb 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -21,6 +21,8 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.TunerVersionChecker;
+import android.media.tv.tuner.frontend.FrontendSettings.FrontendSpectralInversion;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -117,7 +119,11 @@
public static final int ANNEX_C = Constants.FrontendDvbcAnnex.C;
- /** @hide */
+ /**
+ * @deprecated Use the {@link FrontendSettings#FrontendSpectralInversion} instead.
+ * @hide
+ */
+ @Deprecated
@IntDef(prefix = "SPECTRAL_INVERSION_",
value = {SPECTRAL_INVERSION_UNDEFINED, SPECTRAL_INVERSION_NORMAL,
SPECTRAL_INVERSION_INVERTED})
@@ -126,20 +132,97 @@
/**
* Spectral Inversion Type undefined.
+ *
+ * @deprecated Use the {@link FrontendSettings#FRONTEND_SPECTRAL_INVERSION_UNDEFINED} instead.
*/
+ @Deprecated
public static final int SPECTRAL_INVERSION_UNDEFINED =
Constants.FrontendDvbcSpectralInversion.UNDEFINED;
/**
* Normal Spectral Inversion.
+ *
+ * @deprecated Use the {@link FrontendSettings#FRONTEND_SPECTRAL_INVERSION_NORMAL} instead.
*/
+ @Deprecated
public static final int SPECTRAL_INVERSION_NORMAL =
Constants.FrontendDvbcSpectralInversion.NORMAL;
/**
* Inverted Spectral Inversion.
+ *
+ * @deprecated Use the {@link FrontendSettings#FRONTEND_SPECTRAL_INVERSION_INVERTED} instead.
*/
+ @Deprecated
public static final int SPECTRAL_INVERSION_INVERTED =
Constants.FrontendDvbcSpectralInversion.INVERTED;
+ /** @hide */
+ @IntDef(flag = true,
+ prefix = "TIME_INTERLEAVE_MODE_",
+ value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO,
+ TIME_INTERLEAVE_MODE_128_1_0, TIME_INTERLEAVE_MODE_128_1_1,
+ TIME_INTERLEAVE_MODE_64_2, TIME_INTERLEAVE_MODE_32_4,
+ TIME_INTERLEAVE_MODE_16_8, TIME_INTERLEAVE_MODE_8_16,
+ TIME_INTERLEAVE_MODE_128_2, TIME_INTERLEAVE_MODE_128_3,
+ TIME_INTERLEAVE_MODE_128_4})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TimeInterleaveMode {}
+
+ /**
+ * Time interleave mode undefined.
+ */
+ public static final int TIME_INTERLEAVE_MODE_UNDEFINED =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendCableTimeInterleaveMode.UNDEFINED;
+ /**
+ * Hardware is able to detect and set Time Interleave Mode automatically.
+ */
+ public static final int TIME_INTERLEAVE_MODE_AUTO =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendCableTimeInterleaveMode.AUTO;
+ /**
+ * 128/1/0 Time Interleave Mode.
+ */
+ public static final int TIME_INTERLEAVE_MODE_128_1_0 = android.hardware.tv.tuner.V1_1.Constants
+ .FrontendCableTimeInterleaveMode.INTERLEAVING_128_1_0;
+ /**
+ * 128/1/1 Time Interleave Mode.
+ */
+ public static final int TIME_INTERLEAVE_MODE_128_1_1 = android.hardware.tv.tuner.V1_1.Constants
+ .FrontendCableTimeInterleaveMode.INTERLEAVING_128_1_1;
+ /**
+ * 64/2 Time Interleave Mode.
+ */
+ public static final int TIME_INTERLEAVE_MODE_64_2 = android.hardware.tv.tuner.V1_1.Constants
+ .FrontendCableTimeInterleaveMode.INTERLEAVING_64_2;
+ /**
+ * 32/4 Time Interleave Mode.
+ */
+ public static final int TIME_INTERLEAVE_MODE_32_4 = android.hardware.tv.tuner.V1_1.Constants
+ .FrontendCableTimeInterleaveMode.INTERLEAVING_32_4;
+ /**
+ * 16/8 Time Interleave Mode.
+ */
+ public static final int TIME_INTERLEAVE_MODE_16_8 = android.hardware.tv.tuner.V1_1.Constants
+ .FrontendCableTimeInterleaveMode.INTERLEAVING_16_8;
+ /**
+ * 8/16 Time Interleave Mode.
+ */
+ public static final int TIME_INTERLEAVE_MODE_8_16 = android.hardware.tv.tuner.V1_1.Constants
+ .FrontendCableTimeInterleaveMode.INTERLEAVING_8_16;
+ /**
+ * 128/2 Time Interleave Mode.
+ */
+ public static final int TIME_INTERLEAVE_MODE_128_2 = android.hardware.tv.tuner.V1_1.Constants
+ .FrontendCableTimeInterleaveMode.INTERLEAVING_128_2;
+ /**
+ * 128/3 Time Interleave Mode.
+ */
+ public static final int TIME_INTERLEAVE_MODE_128_3 = android.hardware.tv.tuner.V1_1.Constants
+ .FrontendCableTimeInterleaveMode.INTERLEAVING_128_3;
+ /**
+ * 128/4 Time Interleave Mode.
+ */
+ public static final int TIME_INTERLEAVE_MODE_128_4 = android.hardware.tv.tuner.V1_1.Constants
+ .FrontendCableTimeInterleaveMode.INTERLEAVING_128_4;
+
private final int mModulation;
private final long mInnerFec;
@@ -147,9 +230,11 @@
private final int mOuterFec;
private final int mAnnex;
private final int mSpectralInversion;
+ // Dvbc time interleave mode is only supported in Tuner 1.1 or higher.
+ private final int mInterleaveMode;
private DvbcFrontendSettings(int frequency, int modulation, long innerFec, int symbolRate,
- int outerFec, int annex, int spectralInversion) {
+ int outerFec, int annex, int spectralInversion, int interleaveMode) {
super(frequency);
mModulation = modulation;
mInnerFec = innerFec;
@@ -157,6 +242,7 @@
mOuterFec = outerFec;
mAnnex = annex;
mSpectralInversion = spectralInversion;
+ mInterleaveMode = interleaveMode;
}
/**
@@ -196,10 +282,17 @@
/**
* Gets Spectral Inversion.
*/
- @SpectralInversion
+ @FrontendSpectralInversion
public int getSpectralInversion() {
return mSpectralInversion;
}
+ /**
+ * Gets Time Interleave Mode.
+ */
+ @TimeInterleaveMode
+ public int getTimeInterleaveMode() {
+ return mInterleaveMode;
+ }
/**
* Creates a builder for {@link DvbcFrontendSettings}.
@@ -219,7 +312,8 @@
private int mSymbolRate = 0;
private int mOuterFec = OUTER_FEC_UNDEFINED;
private int mAnnex = ANNEX_UNDEFINED;
- private int mSpectralInversion = SPECTRAL_INVERSION_UNDEFINED;
+ private int mSpectralInversion = FrontendSettings.FRONTEND_SPECTRAL_INVERSION_UNDEFINED;
+ private int mInterleaveMode = TIME_INTERLEAVE_MODE_UNDEFINED;
private Builder() {
}
@@ -289,13 +383,30 @@
/**
* Sets Spectral Inversion.
*
- * <p>Default value is {@link #SPECTRAL_INVERSION_UNDEFINED}.
+ * <p>Default value is {@link FrontendSettings#FRONTEND_SPECTRAL_INVERSION_UNDEFINED}.
*/
@NonNull
- public Builder setSpectralInversion(@SpectralInversion int spectralInversion) {
+ public Builder setSpectralInversion(@FrontendSpectralInversion int spectralInversion) {
mSpectralInversion = spectralInversion;
return this;
}
+ /**
+ * Set the time interleave mode.
+ *
+ * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+ * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+ *
+ * @param interleaveMode the value to set as the time interleave mode. Default value is
+ * {@link #TIME_INTERLEAVE_MODE_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setTimeInterleaveMode(@TimeInterleaveMode int interleaveMode) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_1_1, "setTimeInterleaveMode")) {
+ mInterleaveMode = interleaveMode;
+ }
+ return this;
+ }
/**
* Builds a {@link DvbcFrontendSettings} object.
@@ -303,7 +414,7 @@
@NonNull
public DvbcFrontendSettings build() {
return new DvbcFrontendSettings(mFrequency, mModulation, mInnerFec, mSymbolRate,
- mOuterFec, mAnnex, mSpectralInversion);
+ mOuterFec, mAnnex, mSpectralInversion, mInterleaveMode);
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index 60b070f..343dbb1 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -23,6 +23,8 @@
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.Tuner;
+import android.media.tv.tuner.TunerVersionChecker;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -36,6 +38,44 @@
public class DvbsFrontendSettings extends FrontendSettings {
/** @hide */
@IntDef(flag = true,
+ prefix = "SCAN_TYPE_",
+ value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_DIRECT, SCAN_TYPE_DISEQC,
+ SCAN_TYPE_UNICABLE, SCAN_TYPE_JESS})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ScanType {}
+
+ /**
+ * Dvbs scan type undefined.
+ */
+ public static final int SCAN_TYPE_UNDEFINED =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.UNDEFINED;
+
+ /**
+ * Dvbs scan type DIRECT.
+ */
+ public static final int SCAN_TYPE_DIRECT =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.DIRECT;
+
+ /**
+ * Dvbs scan type DISEQC.
+ */
+ public static final int SCAN_TYPE_DISEQC =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.DISEQC;
+
+ /**
+ * Dvbs scan type UNICABLE.
+ */
+ public static final int SCAN_TYPE_UNICABLE =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.UNICABLE;
+
+ /**
+ * Dvbs scan type JESS.
+ */
+ public static final int SCAN_TYPE_JESS =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.JESS;
+
+ /** @hide */
+ @IntDef(flag = true,
prefix = "MODULATION_",
value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_QPSK,
MODULATION_MOD_8PSK, MODULATION_MOD_16QAM, MODULATION_MOD_16PSK,
@@ -218,9 +258,12 @@
private final int mInputStreamId;
private final int mStandard;
private final int mVcmMode;
+ // Dvbs scan type is only supported in Tuner 1.1 or higher.
+ private final int mScanType;
private DvbsFrontendSettings(int frequency, int modulation, DvbsCodeRate codeRate,
- int symbolRate, int rolloff, int pilot, int inputStreamId, int standard, int vcm) {
+ int symbolRate, int rolloff, int pilot, int inputStreamId, int standard, int vcm,
+ int scanType) {
super(frequency);
mModulation = modulation;
mCodeRate = codeRate;
@@ -230,6 +273,7 @@
mInputStreamId = inputStreamId;
mStandard = standard;
mVcmMode = vcm;
+ mScanType = scanType;
}
/**
@@ -286,6 +330,13 @@
public int getVcmMode() {
return mVcmMode;
}
+ /**
+ * Get scan type.
+ */
+ @ScanType
+ public int getScanType() {
+ return mScanType;
+ }
/**
* Creates a builder for {@link DvbsFrontendSettings}.
@@ -308,6 +359,7 @@
private int mInputStreamId = Tuner.INVALID_STREAM_ID;
private int mStandard = STANDARD_AUTO;
private int mVcmMode = VCM_MODE_UNDEFINED;
+ private int mScanType = SCAN_TYPE_UNDEFINED;
private Builder() {
}
@@ -325,6 +377,24 @@
}
/**
+ * Set the scan type.
+ *
+ * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+ * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+ *
+ * @param scanType the value to set as the scan type. Default value is
+ * {@link android.media.tv.tuner.frontend.DvbsFrontendSettings#DVBS_SCAN_TYPE_UNDEFINED}.
+ */
+ @NonNull
+ public Builder setScanType(@ScanType int scanType) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_1_1, "setScanType")) {
+ mScanType = scanType;
+ }
+ return this;
+ }
+
+ /**
* Sets Modulation.
*
* <p>Default value is {@link #MODULATION_UNDEFINED}.
@@ -411,7 +481,7 @@
@NonNull
public DvbsFrontendSettings build() {
return new DvbsFrontendSettings(mFrequency, mModulation, mCodeRate, mSymbolRate,
- mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode);
+ mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode, mScanType);
}
}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index 5c057de..07d1797 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.TunerVersionChecker;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -75,8 +76,21 @@
* 32K Transmission Mode.
*/
public static final int TRANSMISSION_MODE_32K = Constants.FrontendDvbtTransmissionMode.MODE_32K;
-
-
+ /**
+ * 8K Transmission Extended Mode.
+ */
+ public static final int TRANSMISSION_MODE_EXTENDED_8K =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtTransmissionMode.MODE_8K_E;
+ /**
+ * 16K Transmission Extended Mode.
+ */
+ public static final int TRANSMISSION_MODE_EXTENDED_16K =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtTransmissionMode.MODE_16K_E;
+ /**
+ * 32K Transmission Extended Mode.
+ */
+ public static final int TRANSMISSION_MODE_EXTENDED_32K =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtTransmissionMode.MODE_32K_E;
/** @hide */
@IntDef(flag = true,
@@ -124,8 +138,9 @@
@IntDef(flag = true,
prefix = "CONSTELLATION_",
value = {CONSTELLATION_UNDEFINED, CONSTELLATION_AUTO, CONSTELLATION_QPSK,
- CONSTELLATION_16QAM, CONSTELLATION_64QAM,
- CONSTELLATION_256QAM})
+ CONSTELLATION_16QAM, CONSTELLATION_64QAM, CONSTELLATION_256QAM,
+ CONSTELLATION_QPSK_R, CONSTELLATION_16QAM_R, CONSTELLATION_64QAM_R,
+ CONSTELLATION_256QAM_R})
@Retention(RetentionPolicy.SOURCE)
public @interface Constellation {}
@@ -157,7 +172,30 @@
*/
public static final int CONSTELLATION_256QAM =
Constants.FrontendDvbtConstellation.CONSTELLATION_256QAM;
-
+ /**
+ * QPSK Rotated Constellation.
+ */
+ public static final int CONSTELLATION_QPSK_R =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation
+ .CONSTELLATION_QPSK_R;
+ /**
+ * 16QAM Rotated Constellation.
+ */
+ public static final int CONSTELLATION_16QAM_R =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation
+ .CONSTELLATION_16QAM_R;
+ /**
+ * 64QAM Rotated Constellation.
+ */
+ public static final int CONSTELLATION_64QAM_R =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation
+ .CONSTELLATION_64QAM_R;
+ /**
+ * 256QAM Rotated Constellation.
+ */
+ public static final int CONSTELLATION_256QAM_R =
+ android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation
+ .CONSTELLATION_256QAM_R;
/** @hide */
@IntDef(flag = true,
@@ -366,8 +404,7 @@
*/
public static final int PLP_MODE_MANUAL = Constants.FrontendDvbtPlpMode.MANUAL;
-
- private final int mTransmissionMode;
+ private int mTransmissionMode;
private final int mBandwidth;
private final int mConstellation;
private final int mHierarchy;
@@ -489,6 +526,19 @@
return mPlpGroupId;
}
+ private static boolean isExtendedTransmissionMode(@TransmissionMode int transmissionMode) {
+ return transmissionMode == TRANSMISSION_MODE_EXTENDED_8K
+ || transmissionMode == TRANSMISSION_MODE_EXTENDED_16K
+ || transmissionMode == TRANSMISSION_MODE_EXTENDED_32K;
+ }
+
+ private static boolean isExtendedConstellation(@Constellation int constellation) {
+ return constellation == CONSTELLATION_QPSK_R
+ || constellation == CONSTELLATION_16QAM_R
+ || constellation == CONSTELLATION_64QAM_R
+ || constellation == CONSTELLATION_256QAM_R;
+ }
+
/**
* Creates a builder for {@link DvbtFrontendSettings}.
*/
@@ -534,13 +584,23 @@
/**
* Sets Transmission Mode.
*
+ * <p>{@link #TRANSMISSION_MODE_EXTENDED_8K}, {@link #TRANSMISSION_MODE_EXTENDED_16K} and
+ * {@link #TRANSMISSION_MODE_EXTENDED_32K} are only supported by Tuner HAL 1.1 or higher.
+ * Unsupported version would cause no-op. Use {@link TunerVersionChecker.getTunerVersion()}
+ * to check the version.
+ *
* <p>Default value is {@link #TRANSMISSION_MODE_UNDEFINED}.
*/
@NonNull
public Builder setTransmissionMode(@TransmissionMode int transmissionMode) {
- mTransmissionMode = transmissionMode;
+ if (!isExtendedTransmissionMode(transmissionMode)
+ || TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_1_1, "set TransmissionMode Ext")) {
+ mTransmissionMode = transmissionMode;
+ }
return this;
}
+
/**
* Sets Bandwidth.
*
@@ -554,11 +614,20 @@
/**
* Sets Constellation.
*
+ * <p>{@link #CONSTELLATION_QPSK_R}, {@link #CONSTELLATION_16QAM_R},
+ * {@link #CONSTELLATION_64QAM_R} and {@link #CONSTELLATION_256QAM_Rare} are only supported
+ * by Tuner HAL 1.1 or higher. Unsupported version would cause no-op. Use
+ * {@link TunerVersionChecker.getTunerVersion()} to check the version.
+ *
* <p>Default value is {@link #CONSTELLATION_UNDEFINED}.
*/
@NonNull
public Builder setConstellation(@Constellation int constellation) {
- mConstellation = constellation;
+ if (!isExtendedConstellation(constellation)
+ || TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_1_1, "set Constellation Ext")) {
+ mConstellation = constellation;
+ }
return this;
}
/**
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
index 2f2fa97..2147622 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -17,9 +17,12 @@
package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.LongDef;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.Tuner;
+import android.media.tv.tuner.TunerVersionChecker;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -34,7 +37,7 @@
/** @hide */
@IntDef(prefix = "TYPE_",
value = {TYPE_UNDEFINED, TYPE_ANALOG, TYPE_ATSC, TYPE_ATSC3, TYPE_DVBC, TYPE_DVBS,
- TYPE_DVBT, TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT})
+ TYPE_DVBT, TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT, TYPE_DTMB})
@Retention(RetentionPolicy.SOURCE)
public @interface Type {}
@@ -78,7 +81,10 @@
* Integrated Services Digital Broadcasting-Terrestrial (ISDB-T) frontend type.
*/
public static final int TYPE_ISDBT = Constants.FrontendType.ISDBT;
-
+ /**
+ * Digital Terrestrial Multimedia Broadcast standard (DTMB) frontend type.
+ */
+ public static final int TYPE_DTMB = android.hardware.tv.tuner.V1_1.Constants.FrontendType.DTMB;
/** @hide */
@@ -241,9 +247,36 @@
*/
public static final long FEC_77_90 = Constants.FrontendInnerFec.FEC_77_90;
+ /** @hide */
+ @IntDef(prefix = "FRONTEND_SPECTRAL_INVERSION_",
+ value = {FRONTEND_SPECTRAL_INVERSION_UNDEFINED, FRONTEND_SPECTRAL_INVERSION_NORMAL,
+ FRONTEND_SPECTRAL_INVERSION_INVERTED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FrontendSpectralInversion {}
+
+ /**
+ * Spectral Inversion Type undefined.
+ */
+ public static final int FRONTEND_SPECTRAL_INVERSION_UNDEFINED =
+ Constants.FrontendDvbcSpectralInversion.UNDEFINED;
+ /**
+ * Normal Spectral Inversion.
+ */
+ public static final int FRONTEND_SPECTRAL_INVERSION_NORMAL =
+ Constants.FrontendDvbcSpectralInversion.NORMAL;
+ /**
+ * Inverted Spectral Inversion.
+ */
+ public static final int FRONTEND_SPECTRAL_INVERSION_INVERTED =
+ Constants.FrontendDvbcSpectralInversion.INVERTED;
+
private final int mFrequency;
+ // End frequency is only supported in Tuner 1.1 or higher.
+ private int mEndFrequency = Tuner.INVALID_FRONTEND_SETTING_FREQUENCY;
+ // General spectral inversion is only supported in Tuner 1.1 or higher.
+ private int mSpectralInversion = FRONTEND_SPECTRAL_INVERSION_UNDEFINED;
FrontendSettings(int frequency) {
mFrequency = frequency;
@@ -263,4 +296,57 @@
public int getFrequency() {
return mFrequency;
}
+
+ /**
+ * Get the end frequency.
+ *
+ * @return the end frequency in Hz.
+ */
+ public int getEndFrequency() {
+ return mEndFrequency;
+ }
+
+ /**
+ * Get the spectral inversion.
+ *
+ * @return the value of the spectral inversion.
+ */
+ @FrontendSpectralInversion
+ public int getFrontendSpectralInversion() {
+ return mSpectralInversion;
+ }
+
+ /**
+ * Set Spectral Inversion.
+ *
+ * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+ * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+ *
+ * @param inversion the value to set as the spectral inversion. Default value is {@link
+ * #FRONTEND_SPECTRAL_INVERSION_UNDEFINED}.
+ */
+ public void setSpectralInversion(@FrontendSpectralInversion int inversion) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_1_1, "setSpectralInversion")) {
+ mSpectralInversion = inversion;
+ }
+ }
+
+ /**
+ * Set End Frequency. This API is only supported with Tuner HAL 1.1 or higher. Otherwise it
+ * would be no-op.
+ *
+ * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+ * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+ *
+ * @param endFrequency the end frequency used during blind scan. The default value is
+ * {@link android.media.tv.tuner.Tuner#INVALID_FRONTEND_SETTING_FREQUENCY}.
+ */
+ @IntRange(from = 1)
+ public void setEndFrequency(int endFrequency) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_1_1, "setEndFrequency")) {
+ mEndFrequency = endFrequency;
+ }
+ }
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 4e27c8e..724965d 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -142,6 +142,7 @@
shared_libs: [
"android.hardware.graphics.bufferqueue@2.0",
"android.hardware.tv.tuner@1.0",
+ "android.hardware.tv.tuner@1.1",
"libandroid_runtime",
"libcutils",
"libfmq",
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 0b0e162..71c86cc 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -2662,7 +2662,7 @@
gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
CHECK(gFields.cryptoInfoModeID != NULL);
- gFields.cryptoInfoPatternID = env->GetFieldID(clazz.get(), "pattern",
+ gFields.cryptoInfoPatternID = env->GetFieldID(clazz.get(), "mPattern",
"Landroid/media/MediaCodec$CryptoInfo$Pattern;");
CHECK(gFields.cryptoInfoPatternID != NULL);
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index 2ef7b9e..b6c47fca 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -140,6 +140,27 @@
fmt = applyFormatOverrides(fmt, containerFormat);
switch (fmt) {
case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ // Width and height should be multiple of 2. Wrong dataSize would be returned otherwise.
+ if (buffer->width % 2 != 0) {
+ ALOGE("YCbCr_420_888: width (%d) should be a multiple of 2", buffer->width);
+ return BAD_VALUE;
+ }
+
+ if (buffer->height % 2 != 0) {
+ ALOGE("YCbCr_420_888: height (%d) should be a multiple of 2", buffer->height);
+ return BAD_VALUE;
+ }
+
+ if (buffer->width <= 0) {
+ ALOGE("YCbCr_420_888: width (%d) should be a > 0", buffer->width);
+ return BAD_VALUE;
+ }
+
+ if (buffer->height <= 0) {
+ ALOGE("YCbCr_420_888: height (%d) should be a > 0", buffer->height);
+ return BAD_VALUE;
+ }
+
pData =
(idx == 0) ?
buffer->data :
@@ -160,6 +181,27 @@
break;
// NV21
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ // Width and height should be multiple of 2. Wrong dataSize would be returned otherwise.
+ if (buffer->width % 2 != 0) {
+ ALOGE("YCrCb_420_SP: width (%d) should be a multiple of 2", buffer->width);
+ return BAD_VALUE;
+ }
+
+ if (buffer->height % 2 != 0) {
+ ALOGE("YCrCb_420_SP: height (%d) should be a multiple of 2", buffer->height);
+ return BAD_VALUE;
+ }
+
+ if (buffer->width <= 0) {
+ ALOGE("YCrCb_420_SP: width (%d) should be a > 0", buffer->width);
+ return BAD_VALUE;
+ }
+
+ if (buffer->height <= 0) {
+ ALOGE("YCrCb_420_SP: height (%d) should be a > 0", buffer->height);
+ return BAD_VALUE;
+ }
+
cr = buffer->data + (buffer->stride * buffer->height);
cb = cr + 1;
// only map until last pixel
@@ -178,6 +220,27 @@
rStride = buffer->width;
break;
case HAL_PIXEL_FORMAT_YV12:
+ // Width and height should be multiple of 2. Wrong dataSize would be returned otherwise.
+ if (buffer->width % 2 != 0) {
+ ALOGE("YV12: width (%d) should be a multiple of 2", buffer->width);
+ return BAD_VALUE;
+ }
+
+ if (buffer->height % 2 != 0) {
+ ALOGE("YV12: height (%d) should be a multiple of 2", buffer->height);
+ return BAD_VALUE;
+ }
+
+ if (buffer->width <= 0) {
+ ALOGE("YV12: width (%d) should be a > 0", buffer->width);
+ return BAD_VALUE;
+ }
+
+ if (buffer->height <= 0) {
+ ALOGE("YV12: height (%d) should be a > 0", buffer->height);
+ return BAD_VALUE;
+ }
+
// Y and C stride need to be 16 pixel aligned.
LOG_ALWAYS_FATAL_IF(buffer->stride % 16,
"Stride is not 16 pixel aligned %d", buffer->stride);
@@ -344,6 +407,11 @@
int flexFormat = format;
if (isPossiblyYUV(format)) {
res = buffer->lockAsyncYCbCr(inUsage, rect, &ycbcr, fenceFd);
+
+ if (res != OK) {
+ ALOGW("lockAsyncYCbCr failed with error %d", res);
+ }
+
pData = ycbcr.y;
flexFormat = HAL_PIXEL_FORMAT_YCbCr_420_888;
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 5daf8b0..6a1acede 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -22,7 +22,6 @@
#include "android_runtime/AndroidRuntime.h"
#include <android-base/logging.h>
-#include <android/hardware/tv/tuner/1.0/ITuner.h>
#include <media/stagefright/foundation/ADebug.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
@@ -34,7 +33,6 @@
using ::android::hardware::hidl_bitfield;
using ::android::hardware::hidl_vec;
using ::android::hardware::tv::tuner::V1_0::AudioExtraMetaData;
-using ::android::hardware::tv::tuner::V1_0::Constant;
using ::android::hardware::tv::tuner::V1_0::DataFormat;
using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings;
using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
@@ -129,12 +127,29 @@
using ::android::hardware::tv::tuner::V1_0::FrontendStatusAtsc3PlpInfo;
using ::android::hardware::tv::tuner::V1_0::FrontendStatusType;
using ::android::hardware::tv::tuner::V1_0::FrontendType;
-using ::android::hardware::tv::tuner::V1_0::ITuner;
using ::android::hardware::tv::tuner::V1_0::LnbPosition;
using ::android::hardware::tv::tuner::V1_0::LnbTone;
using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
using ::android::hardware::tv::tuner::V1_0::PlaybackSettings;
using ::android::hardware::tv::tuner::V1_0::RecordSettings;
+using ::android::hardware::tv::tuner::V1_1::Constant;
+using ::android::hardware::tv::tuner::V1_1::Constant64Bit;
+using ::android::hardware::tv::tuner::V1_1::FrontendAnalogAftFlag;
+using ::android::hardware::tv::tuner::V1_1::FrontendAnalogSettingsExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbsScanType;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbcSettingsExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbsSettingsExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbtSettingsExt1_1;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbCapabilities;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbCodeRate;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbSettings;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion;
struct fields_t {
jfieldID tunerContext;
@@ -310,9 +325,9 @@
/////////////// MediaEvent ///////////////////////
-MediaEvent::MediaEvent(sp<IFilter> iFilter, hidl_handle avHandle,
- uint64_t dataId, uint64_t dataLength, jobject obj) : mIFilter(iFilter),
- mDataId(dataId), mDataLength(dataLength), mBuffer(nullptr),
+MediaEvent::MediaEvent(sp<Filter> filter, hidl_handle avHandle,
+ uint64_t dataId, uint64_t dataSize, jobject obj) : mFilter(filter),
+ mDataId(dataId), mDataSize(dataSize), mBuffer(nullptr),
mDataIdRefCnt(0), mAvHandleRefCnt(0), mIonHandle(nullptr) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
mMediaEventObj = env->NewWeakGlobalRef(obj);
@@ -336,7 +351,8 @@
void MediaEvent::finalize() {
if (mAvHandleRefCnt == 0) {
- mIFilter->releaseAvHandle(hidl_handle(mAvHandle), mDataIdRefCnt == 0 ? mDataId : 0);
+ mFilter->mFilterSp->releaseAvHandle(
+ hidl_handle(mAvHandle), mDataIdRefCnt == 0 ? mDataId : 0);
native_handle_close(mAvHandle);
}
}
@@ -349,7 +365,47 @@
if (mLinearBlockObj != NULL) {
return mLinearBlockObj;
}
- mIonHandle = new C2HandleIon(dup(mAvHandle->data[0]), mDataLength);
+
+ int fd;
+ int numInts = 0;
+ int memIndex;
+ int dataSize;
+ if (mAvHandle->numFds == 0) {
+ if (mFilter->mAvSharedHandle == NULL) {
+ ALOGE("Shared AV memory handle is not initialized.");
+ return NULL;
+ }
+ if (mFilter->mAvSharedHandle->numFds == 0) {
+ ALOGE("Shared AV memory handle is empty.");
+ return NULL;
+ }
+ fd = mFilter->mAvSharedHandle->data[0];
+ dataSize = mFilter->mAvSharedMemSize;
+ numInts = mFilter->mAvSharedHandle->numInts;
+ if (numInts > 0) {
+ // If the first int in the shared native handle has value, use it as the index
+ memIndex = mFilter->mAvSharedHandle->data[mFilter->mAvSharedHandle->numFds];
+ }
+ } else {
+ fd = mAvHandle->data[0];
+ dataSize = mDataSize;
+ numInts = mAvHandle->numInts;
+ if (numInts > 0) {
+ // Otherwise if the first int in the av native handle returned from the filter
+ // event has value, use it as the index
+ memIndex = mAvHandle->data[mAvHandle->numFds];
+ } else {
+ if (mFilter->mAvSharedHandle != NULL) {
+ numInts = mFilter->mAvSharedHandle->numInts;
+ if (numInts > 0) {
+ // If the first int in the shared native handle has value, use it as the index
+ memIndex = mFilter->mAvSharedHandle->data[mFilter->mAvSharedHandle->numFds];
+ }
+ }
+ }
+ }
+
+ mIonHandle = new C2HandleIon(dup(fd), dataSize);
std::shared_ptr<C2LinearBlock> block = _C2BlockFactory::CreateLinearBlock(mIonHandle);
if (block != nullptr) {
// CreateLinearBlock delete mIonHandle after it create block successfully.
@@ -358,13 +414,11 @@
JNIEnv *env = AndroidRuntime::getJNIEnv();
std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
context->mBlock = block;
- std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, mDataLength);
+ std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, dataSize);
context->mBuffer = pC2Buffer;
mC2Buffer = pC2Buffer;
- if (mAvHandle->numInts > 0) {
- // use first int in the native_handle as the index
- int index = mAvHandle->data[mAvHandle->numFds];
- std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(index, mDataId);
+ if (numInts > 0) {
+ std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(memIndex, mDataId);
std::shared_ptr<C2Info> info(std::static_pointer_cast<C2Info>(c2param));
pC2Buffer->setInfo(info);
}
@@ -471,7 +525,7 @@
if (mediaEvent.avMemory.getNativeHandle() != NULL || mediaEvent.avDataId != 0) {
sp<MediaEvent> mediaEventSp =
- new MediaEvent(mIFilter, mediaEvent.avMemory,
+ new MediaEvent(mFilter, mediaEvent.avMemory,
mediaEvent.avDataId, dataLength + offset, obj);
mediaEventSp->mAvHandleRefCnt++;
env->SetLongField(obj, eventContext, (jlong) mediaEventSp.get());
@@ -505,10 +559,11 @@
}
jobjectArray FilterCallback::getTsRecordEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+ jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events,
+ const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TsRecordEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJ)V");
+ jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJJ)V");
for (int i = 0; i < events.size(); i++) {
auto event = events[i];
@@ -537,28 +592,39 @@
jlong byteNumber = static_cast<jlong>(tsRecordEvent.byteNumber);
+ jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].tsRecord().pts)
+ : static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
+
jobject obj =
- env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber);
+ env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber, pts);
env->SetObjectArrayElement(arr, i, obj);
}
return arr;
}
jobjectArray FilterCallback::getMmtpRecordEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+ jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events,
+ const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MmtpRecordEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJ)V");
+ jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJIJ)V");
for (int i = 0; i < events.size(); i++) {
auto event = events[i];
+
DemuxFilterMmtpRecordEvent mmtpRecordEvent = event.mmtpRecord();
jint scHevcIndexMask = static_cast<jint>(mmtpRecordEvent.scHevcIndexMask);
jlong byteNumber = static_cast<jlong>(mmtpRecordEvent.byteNumber);
+ jint mpuSequenceNumber = (eventsExt.size() > i)
+ ? static_cast<jint>(eventsExt[i].mmtpRecord().mpuSequenceNumber)
+ : static_cast<jint>(Constant::INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM);
+ jlong pts = (eventsExt.size() > i) ? static_cast<jlong>(eventsExt[i].mmtpRecord().pts)
+ : static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
jobject obj =
- env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber);
+ env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber,
+ mpuSequenceNumber, pts);
env->SetObjectArrayElement(arr, i, obj);
}
return arr;
@@ -627,12 +693,14 @@
return arr;
}
-Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent) {
- ALOGD("FilterCallback::onFilterEvent");
+Return<void> FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
+ const DemuxFilterEventExt& filterEventExt) {
+ ALOGD("FilterCallback::onFilterEvent_1_1");
JNIEnv *env = AndroidRuntime::getJNIEnv();
std::vector<DemuxFilterEvent::Event> events = filterEvent.events;
+ std::vector<DemuxFilterEventExt::Event> eventsExt = filterEventExt.events;
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/FilterEvent");
jobjectArray array = env->NewObjectArray(events.size(), eventClazz, NULL);
@@ -652,11 +720,11 @@
break;
}
case DemuxFilterEvent::Event::hidl_discriminator::tsRecord: {
- array = getTsRecordEvent(array, events);
+ array = getTsRecordEvent(array, events, eventsExt);
break;
}
case DemuxFilterEvent::Event::hidl_discriminator::mmtpRecord: {
- array = getMmtpRecordEvent(array, events);
+ array = getMmtpRecordEvent(array, events, eventsExt);
break;
}
case DemuxFilterEvent::Event::hidl_discriminator::download: {
@@ -677,18 +745,26 @@
}
}
env->CallVoidMethod(
- mFilter,
+ mFilter->mFilterObj,
gFields.onFilterEventID,
array);
return Void();
}
+Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent) {
+ ALOGD("FilterCallback::onFilterEvent");
+ std::vector<DemuxFilterEventExt::Event> emptyEventsExt;
+ DemuxFilterEventExt emptyFilterEventExt {
+ .events = emptyEventsExt,
+ };
+ return onFilterEvent_1_1(filterEvent, emptyFilterEventExt);
+}
Return<void> FilterCallback::onFilterStatus(const DemuxFilterStatus status) {
ALOGD("FilterCallback::onFilterStatus");
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(
- mFilter,
+ mFilter->mFilterObj,
gFields.onFilterStatusID,
(jint)status);
return Void();
@@ -696,17 +772,11 @@
void FilterCallback::setFilter(const sp<Filter> filter) {
ALOGD("FilterCallback::setFilter");
- mFilter = filter->mFilterObj;
- mIFilter = filter->mFilterSp;
+ // JNI Object
+ mFilter = filter;
}
-FilterCallback::~FilterCallback() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (mFilter != NULL) {
- env->DeleteWeakGlobalRef(mFilter);
- mFilter = NULL;
- }
-}
+FilterCallback::~FilterCallback() {}
/////////////// Filter ///////////////////////
@@ -919,6 +989,8 @@
/////////////// Tuner ///////////////////////
sp<ITuner> JTuner::mTuner;
+sp<::android::hardware::tv::tuner::V1_1::ITuner> JTuner::mTuner_1_1;
+int JTuner::mTunerVersion = 0;
JTuner::JTuner(JNIEnv *env, jobject thiz)
: mClass(NULL) {
@@ -950,13 +1022,28 @@
sp<ITuner> JTuner::getTunerService() {
if (mTuner == nullptr) {
- mTuner = ITuner::getService();
+ mTunerVersion = 0;
+ mTuner_1_1 = ::android::hardware::tv::tuner::V1_1::ITuner::getService();
- if (mTuner == nullptr) {
- ALOGW("Failed to get tuner service.");
- }
- }
- return mTuner;
+ if (mTuner_1_1 == nullptr) {
+ ALOGW("Failed to get tuner 1.1 service.");
+ mTuner = ITuner::getService();
+ if (mTuner == nullptr) {
+ ALOGW("Failed to get tuner 1.0 service.");
+ } else {
+ mTunerVersion = 1 << 16;
+ }
+ } else {
+ mTuner = static_cast<sp<ITuner>>(mTuner_1_1);
+ mTunerVersion = ((1 << 16) | 1);
+ }
+ }
+ return mTuner;
+}
+
+jint JTuner::getTunerVersion() {
+ ALOGD("JTuner::getTunerVersion()");
+ return (jint) mTunerVersion;
}
jobject JTuner::getFrontendIds() {
@@ -996,6 +1083,7 @@
return NULL;
}
mFe = fe;
+ mFe_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe);
mFeId = id;
if (mDemux != NULL) {
mDemux->setFrontendDataSource(mFeId);
@@ -1128,6 +1216,33 @@
guardIntervalCap);
}
+jobject JTuner::getDtmbFrontendCaps(JNIEnv *env, int id) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DtmbFrontendCapabilities");
+ jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIII)V");
+
+ if (mTuner_1_1 == NULL) {
+ ALOGD("1.1 Tuner is not found. Dtmb Frontend Caps are not supported.");
+ return NULL;
+ }
+
+ Result result;
+ FrontendDtmbCapabilities dtmbCaps;
+ mTuner_1_1->getFrontendDtmbCapabilities(id,
+ [&](Result r, const FrontendDtmbCapabilities& caps) {
+ dtmbCaps = caps;
+ result = r;
+ });
+ jint modulationCap = dtmbCaps.modulationCap;
+ jint transmissionModeCap = dtmbCaps.transmissionModeCap;
+ jint guardIntervalCap = dtmbCaps.guardIntervalCap;
+ jint interleaveModeCap = dtmbCaps.interleaveModeCap;
+ jint codeRateCap = dtmbCaps.codeRateCap;
+ jint bandwidthCap = dtmbCaps.bandwidthCap;
+
+ return env->NewObject(clazz, capsInit, modulationCap, transmissionModeCap, guardIntervalCap,
+ interleaveModeCap, codeRateCap, bandwidthCap);
+}
+
jobject JTuner::getFrontendInfo(int id) {
FrontendInfo feInfo;
Result res;
@@ -1158,6 +1273,15 @@
FrontendInfo::FrontendCapabilities caps = feInfo.frontendCaps;
jobject jcaps = NULL;
+
+ if (feInfo.type == static_cast<FrontendType>(
+ ::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) {
+ if (mTuner_1_1 == NULL) {
+ return NULL;
+ }
+ jcaps = getDtmbFrontendCaps(env, id);
+ }
+
switch(feInfo.type) {
case FrontendType::ANALOG:
if (FrontendInfo::FrontendCapabilities::hidl_discriminator::analogCaps
@@ -1305,12 +1429,21 @@
return lnbObj;
}
-int JTuner::tune(const FrontendSettings& settings) {
+int JTuner::tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1) {
if (mFe == NULL) {
ALOGE("frontend is not initialized");
return (int)Result::INVALID_STATE;
}
- Result result = mFe->tune(settings);
+ Result result;
+ sp<::android::hardware::tv::tuner::V1_1::IFrontend> fe_1_1 =
+ ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe);
+ if (fe_1_1 == NULL) {
+ ALOGD("1.1 frontend is not found. Using 1.0 instead.");
+ result = mFe->tune(settings);
+ return (int)result;
+ }
+
+ result = fe_1_1->tune_1_1(settings, settingsExt1_1);
return (int)result;
}
@@ -1323,12 +1456,22 @@
return (int)result;
}
-int JTuner::scan(const FrontendSettings& settings, FrontendScanType scanType) {
+int JTuner::scan(const FrontendSettings& settings, FrontendScanType scanType,
+ const FrontendSettingsExt1_1& settingsExt1_1) {
if (mFe == NULL) {
ALOGE("frontend is not initialized");
return (int)Result::INVALID_STATE;
}
- Result result = mFe->scan(settings, scanType);
+ Result result;
+ sp<::android::hardware::tv::tuner::V1_1::IFrontend> fe_1_1 =
+ ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe);
+ if (fe_1_1 == NULL) {
+ ALOGD("1.1 frontend is not found. Using 1.0 instead.");
+ result = mFe->scan(settings, scanType);
+ return (int)result;
+ }
+
+ result = fe_1_1->scan_1_1(settings, scanType, settingsExt1_1);
return (int)result;
}
@@ -1455,6 +1598,27 @@
return (int) r;
}
+int JTuner::linkCiCam(int id) {
+ if (mFe_1_1 == NULL) {
+ ALOGE("frontend 1.1 is not initialized");
+ return (int)Constant::INVALID_LTS_ID;
+ }
+
+ Result res;
+ uint32_t ltsId;
+ mFe_1_1->linkCiCam(static_cast<uint32_t>(id),
+ [&](Result r, uint32_t id) {
+ res = r;
+ ltsId = id;
+ });
+
+ if (res != Result::SUCCESS) {
+ return (int)Constant::INVALID_LTS_ID;
+ }
+
+ return (int) ltsId;
+}
+
int JTuner::disconnectCiCam() {
if (mDemux == NULL) {
Result r = openDemux();
@@ -1466,6 +1630,18 @@
return (int) r;
}
+
+int JTuner::unlinkCiCam(int id) {
+ if (mFe_1_1 == NULL) {
+ ALOGE("frontend 1.1 is not initialized");
+ return (int)Result::INVALID_STATE;
+ }
+
+ Result r = mFe_1_1->unlinkCiCam(static_cast<uint32_t>(id));
+
+ return (int) r;
+}
+
jobject JTuner::openDescrambler() {
ALOGD("JTuner::openDescrambler");
if (mTuner == nullptr || mDemux == nullptr) {
@@ -1504,6 +1680,7 @@
}
sp<IFilter> iFilterSp;
+ sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1;
sp<FilterCallback> callback = new FilterCallback();
Result res;
mDemux->openFilter(type, bufferSize, callback,
@@ -1515,24 +1692,54 @@
ALOGD("Failed to open filter, type = %d", type.mainType);
return NULL;
}
- int fId;
+ uint64_t fId;
iFilterSp->getId([&](Result, uint32_t filterId) {
fId = filterId;
});
+ iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp);
+ if (iFilterSp_1_1 != NULL) {
+ iFilterSp_1_1->getId64Bit([&](Result, uint64_t filterId64Bit) {
+ fId = filterId64Bit;
+ });
+ }
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject filterObj =
env->NewObject(
env->FindClass("android/media/tv/tuner/filter/Filter"),
gFields.filterInitID,
- (jint) fId);
+ (jlong) fId);
sp<Filter> filterSp = new Filter(iFilterSp, filterObj);
filterSp->incStrong(filterObj);
env->SetLongField(filterObj, gFields.filterContext, (jlong)filterSp.get());
-
+ filterSp->mIsMediaFilter = false;
+ filterSp->mAvSharedHandle = NULL;
callback->setFilter(filterSp);
+ if (type.mainType == DemuxFilterMainType::MMTP) {
+ if (type.subType.mmtpFilterType() == DemuxMmtpFilterType::AUDIO ||
+ type.subType.mmtpFilterType() == DemuxMmtpFilterType::VIDEO) {
+ filterSp->mIsMediaFilter = true;
+ }
+ }
+
+ if (type.mainType == DemuxFilterMainType::TS) {
+ if (type.subType.tsFilterType() == DemuxTsFilterType::AUDIO ||
+ type.subType.tsFilterType() == DemuxTsFilterType::VIDEO) {
+ filterSp->mIsMediaFilter = true;
+ }
+ }
+
+ if (iFilterSp_1_1 != NULL && filterSp->mIsMediaFilter) {
+ iFilterSp_1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) {
+ if (r == Result::SUCCESS) {
+ filterSp->mAvSharedHandle = native_handle_clone(avMemory.getNativeHandle());
+ filterSp->mAvSharedMemSize = avMemSize;
+ }
+ });
+ }
+
return filterObj;
}
@@ -1902,6 +2109,10 @@
if (mFe != NULL) {
r = mFe->close();
}
+ if (r == Result::SUCCESS) {
+ mFe = NULL;
+ mFe_1_1 = NULL;
+ }
return (jint) r;
}
@@ -1910,6 +2121,9 @@
if (mDemux != NULL) {
r = mDemux->close();
}
+ if (r == Result::SUCCESS) {
+ mDemux = NULL;
+ }
return (jint) r;
}
@@ -1962,6 +2176,22 @@
return freq;
}
+static uint32_t getFrontendSettingsEndFreq(JNIEnv *env, const jobject& settings) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings");
+ jfieldID endFreqField = env->GetFieldID(clazz, "mEndFrequency", "I");
+ uint32_t endFreq = static_cast<uint32_t>(env->GetIntField(settings, endFreqField));
+ return endFreq;
+}
+
+static FrontendSpectralInversion getFrontendSettingsSpectralInversion(
+ JNIEnv *env, const jobject& settings) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings");
+ jfieldID inversionField = env->GetFieldID(clazz, "mSpectralInversion", "I");
+ FrontendSpectralInversion inversion =
+ static_cast<FrontendSpectralInversion>(env->GetIntField(settings, inversionField));
+ return inversion;
+}
+
static FrontendSettings getAnalogFrontendSettings(JNIEnv *env, const jobject& settings) {
FrontendSettings frontendSettings;
uint32_t freq = getFrontendSettingsFreq(env, settings);
@@ -1981,6 +2211,18 @@
return frontendSettings;
}
+static void getAnalogFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings,
+ FrontendSettingsExt1_1& settingsExt1_1) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendSettings");
+ FrontendAnalogAftFlag aftFlag =
+ static_cast<FrontendAnalogAftFlag>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mAftFlag", "I")));
+ FrontendAnalogSettingsExt1_1 analogExt1_1 {
+ .aftFlag = aftFlag,
+ };
+ settingsExt1_1.settingExt.analog(analogExt1_1);
+}
+
static hidl_vec<FrontendAtsc3PlpSettings> getAtsc3PlpSettings(
JNIEnv *env, const jobject& settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3FrontendSettings");
@@ -2100,6 +2342,19 @@
return frontendSettings;
}
+static void getDvbcFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings,
+ FrontendSettingsExt1_1& settingsExt1_1) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbcFrontendSettings");
+ FrontendCableTimeInterleaveMode interleaveMode =
+ static_cast<FrontendCableTimeInterleaveMode>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mInterleaveMode", "I")));
+
+ FrontendDvbcSettingsExt1_1 dvbcExt1_1 {
+ .interleaveMode = interleaveMode,
+ };
+ settingsExt1_1.settingExt.dvbc(dvbcExt1_1);
+}
+
static FrontendDvbsCodeRate getDvbsCodeRate(JNIEnv *env, const jobject& settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings");
jobject jcodeRate =
@@ -2141,7 +2396,6 @@
uint32_t freq = getFrontendSettingsFreq(env, settings);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings");
-
FrontendDvbsModulation modulation =
static_cast<FrontendDvbsModulation>(
env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I")));
@@ -2180,6 +2434,22 @@
return frontendSettings;
}
+static void getDvbsFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings,
+ FrontendSettingsExt1_1& settingsExt1_1) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings");
+ FrontendDvbsScanType scanType =
+ static_cast<FrontendDvbsScanType>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mScanType", "I")));
+ bool isDiseqcRxMessage = static_cast<bool>(env->GetBooleanField(
+ settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "B")));
+
+ FrontendDvbsSettingsExt1_1 dvbsExt1_1 {
+ .scanType = scanType,
+ .isDiseqcRxMessage = isDiseqcRxMessage,
+ };
+ settingsExt1_1.settingExt.dvbs(dvbsExt1_1);
+}
+
static FrontendSettings getDvbtFrontendSettings(JNIEnv *env, const jobject& settings) {
FrontendSettings frontendSettings;
uint32_t freq = getFrontendSettingsFreq(env, settings);
@@ -2246,6 +2516,25 @@
return frontendSettings;
}
+static void getDvbtFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings,
+ FrontendSettingsExt1_1& settingsExt1_1) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbtFrontendSettings");
+
+ FrontendDvbtSettingsExt1_1 dvbtExt1_1;
+ int transmissionMode =
+ env->GetIntField(settings, env->GetFieldID(clazz, "mTransmissionMode", "I"));
+ dvbtExt1_1.transmissionMode = static_cast<
+ ::android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode>(
+ transmissionMode);
+
+ int constellation =
+ env->GetIntField(settings, env->GetFieldID(clazz, "mConstellation", "I"));
+ dvbtExt1_1.constellation = static_cast<
+ ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation>(constellation);
+
+ settingsExt1_1.settingExt.dvbt(dvbtExt1_1);
+}
+
static FrontendSettings getIsdbsFrontendSettings(JNIEnv *env, const jobject& settings) {
FrontendSettings frontendSettings;
uint32_t freq = getFrontendSettingsFreq(env, settings);
@@ -2354,6 +2643,41 @@
return frontendSettings;
}
+static void getDtmbFrontendSettings(JNIEnv *env, const jobject& settings,
+ FrontendSettingsExt1_1& settingsExt1_1) {
+ uint32_t freq = getFrontendSettingsFreq(env, settings);
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DtmbFrontendSettings");
+ FrontendDtmbModulation modulation =
+ static_cast<FrontendDtmbModulation>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I")));
+ FrontendDtmbBandwidth bandwidth =
+ static_cast<FrontendDtmbBandwidth>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I")));
+ FrontendDtmbTransmissionMode transmissionMode =
+ static_cast<FrontendDtmbTransmissionMode>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mTransmissionMode", "I")));
+ FrontendDtmbCodeRate codeRate =
+ static_cast<FrontendDtmbCodeRate>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mCodeRate", "I")));
+ FrontendDtmbGuardInterval guardInterval =
+ static_cast<FrontendDtmbGuardInterval>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mGuardInterval", "I")));
+ FrontendDtmbTimeInterleaveMode interleaveMode =
+ static_cast<FrontendDtmbTimeInterleaveMode>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mTimeInterleaveMode", "I")));
+
+ FrontendDtmbSettings frontendDtmbSettings {
+ .frequency = freq,
+ .modulation = modulation,
+ .bandwidth = bandwidth,
+ .transmissionMode = transmissionMode,
+ .codeRate = codeRate,
+ .guardInterval = guardInterval,
+ .interleaveMode = interleaveMode,
+ };
+ settingsExt1_1.settingExt.dtmb(frontendDtmbSettings);
+}
+
static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) {
ALOGD("getFrontendSettings %d", type);
@@ -2386,6 +2710,59 @@
}
}
+static FrontendSettingsExt1_1 getFrontendSettingsExt1_1(JNIEnv *env, int type, jobject settings) {
+ ALOGD("getFrontendSettingsExt1_1 %d", type);
+
+ FrontendSettingsExt1_1 settingsExt1_1 {
+ .endFrequency = static_cast<uint32_t>(Constant::INVALID_FRONTEND_SETTING_FREQUENCY),
+ .inversion = FrontendSpectralInversion::UNDEFINED,
+ };
+ settingsExt1_1.settingExt.noinit();
+
+ if (type == static_cast<int>(::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) {
+ getDtmbFrontendSettings(env, settings, settingsExt1_1);
+ } else {
+ FrontendType feType = static_cast<FrontendType>(type);
+ switch(feType) {
+ case FrontendType::DVBS:
+ getDvbsFrontendSettingsExt1_1(env, settings, settingsExt1_1);
+ break;
+ case FrontendType::DVBT:
+ getDvbtFrontendSettingsExt1_1(env, settings, settingsExt1_1);
+ break;
+ case FrontendType::ANALOG:
+ getAnalogFrontendSettingsExt1_1(env, settings, settingsExt1_1);
+ break;
+ case FrontendType::ATSC3:
+ break;
+ case FrontendType::ATSC:
+ break;
+ case FrontendType::DVBC:
+ getDvbcFrontendSettingsExt1_1(env, settings, settingsExt1_1);
+ break;
+ case FrontendType::ISDBS:
+ break;
+ case FrontendType::ISDBS3:
+ break;
+ case FrontendType::ISDBT:
+ break;
+ default:
+ // should never happen because a type is associated with a subclass of
+ // FrontendSettings and not set by users
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "Unsupported frontend type %d", type);
+ return FrontendSettingsExt1_1();
+ }
+ }
+
+ uint32_t endFreq = getFrontendSettingsEndFreq(env, settings);
+ FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings);
+ settingsExt1_1.endFrequency = endFreq;
+ settingsExt1_1.inversion = inversion;
+
+ return settingsExt1_1;
+}
+
static sp<Filter> getFilter(JNIEnv *env, jobject filter) {
return (Filter *)env->GetLongField(filter, gFields.filterContext);
}
@@ -2460,7 +2837,7 @@
jclass filterClazz = env->FindClass("android/media/tv/tuner/filter/Filter");
gFields.filterContext = env->GetFieldID(filterClazz, "mNativeContext", "J");
gFields.filterInitID =
- env->GetMethodID(filterClazz, "<init>", "(I)V");
+ env->GetMethodID(filterClazz, "<init>", "(J)V");
gFields.onFilterStatusID =
env->GetMethodID(filterClazz, "onFilterStatus", "(I)V");
gFields.onFilterEventID =
@@ -2501,6 +2878,11 @@
setTuner(env,thiz, tuner);
}
+static jint android_media_tv_Tuner_native_get_tuner_version(JNIEnv *env, jobject thiz) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->getTunerVersion();
+}
+
static jobject android_media_tv_Tuner_get_frontend_ids(JNIEnv *env, jobject thiz) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->getFrontendIds();
@@ -2522,7 +2904,9 @@
static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) {
sp<JTuner> tuner = getTuner(env, thiz);
- return tuner->tune(getFrontendSettings(env, type, settings));
+ FrontendSettings setting = getFrontendSettings(env, type, settings);
+ FrontendSettingsExt1_1 settingExt = getFrontendSettingsExt1_1(env, type, settings);
+ return tuner->tune(setting, settingExt);
}
static int android_media_tv_Tuner_stop_tune(JNIEnv *env, jobject thiz) {
@@ -2533,8 +2917,9 @@
static int android_media_tv_Tuner_scan(
JNIEnv *env, jobject thiz, jint settingsType, jobject settings, jint scanType) {
sp<JTuner> tuner = getTuner(env, thiz);
- return tuner->scan(getFrontendSettings(
- env, settingsType, settings), static_cast<FrontendScanType>(scanType));
+ FrontendSettings setting = getFrontendSettings(env, settingsType, settings);
+ FrontendSettingsExt1_1 settingExt = getFrontendSettingsExt1_1(env, settingsType, settings);
+ return tuner->scan(setting, static_cast<FrontendScanType>(scanType), settingExt);
}
static int android_media_tv_Tuner_stop_scan(JNIEnv *env, jobject thiz) {
@@ -2579,11 +2964,21 @@
return tuner->connectCiCam(id);
}
+static int android_media_tv_Tuner_link_cicam(JNIEnv *env, jobject thiz, jint id) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->linkCiCam(id);
+}
+
static int android_media_tv_Tuner_disconnect_cicam(JNIEnv *env, jobject thiz) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->disconnectCiCam();
}
+static int android_media_tv_Tuner_unlink_cicam(JNIEnv *env, jobject thiz, jint id) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->unlinkCiCam(id);
+}
+
static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv *env, jobject thiz, jint id) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->getFrontendInfo(id);
@@ -2991,6 +3386,29 @@
return filterSettings;
}
+static Result configureIpFilterContextId(
+ JNIEnv *env, sp<IFilter> iFilterSp, jobject ipFilterConfigObj) {
+ jclass clazz = env->FindClass(
+ "android/media/tv/tuner/filter/IpFilterConfiguration");
+ uint32_t cid = env->GetIntField(ipFilterConfigObj, env->GetFieldID(
+ clazz, "mIpFilterContextId", "I"));
+ Result res = Result::SUCCESS;
+ if (cid != static_cast<uint32_t>(Constant::INVALID_IP_FILTER_CONTEXT_ID)) {
+ sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1;
+ iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp);
+
+ if (iFilterSp_1_1 != NULL) {
+ res = iFilterSp_1_1->configureIpCid(cid);
+ if (res != Result::SUCCESS) {
+ return res;
+ }
+ } else {
+ ALOGW("configureIpCid is not supported with the current HAL implementation.");
+ }
+ }
+ return res;
+}
+
static jint copyData(JNIEnv *env, std::unique_ptr<MQ>& mq, EventFlag* flag, jbyteArray buffer,
jlong offset, jlong size) {
ALOGD("copyData, size=%ld, offset=%ld", (long) size, (long) offset);
@@ -3034,6 +3452,13 @@
return (jint) res;
}
+ if (static_cast<DemuxFilterMainType>(type) == DemuxFilterMainType::IP) {
+ res = configureIpFilterContextId(env, iFilterSp, settings);
+ if (res != Result::SUCCESS) {
+ return (jint) res;
+ }
+ }
+
MQDescriptorSync<uint8_t> filterMQDesc;
Result getQueueDescResult = Result::UNKNOWN_ERROR;
if (filterSp->mFilterMQ == NULL) {
@@ -3071,6 +3496,36 @@
return (jint) id;
}
+static jlong android_media_tv_Tuner_get_filter_64bit_id(JNIEnv* env, jobject filter) {
+ sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
+ if (iFilterSp == NULL) {
+ ALOGD("Failed to get filter ID: filter not found");
+ return static_cast<jlong>(
+ ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT);
+ }
+
+ sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1;
+ iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp);
+ Result res;
+ uint64_t id;
+
+ if (iFilterSp_1_1 != NULL) {
+ iFilterSp_1_1->getId64Bit(
+ [&](Result r, uint64_t filterId64Bit) {
+ res = r;
+ id = filterId64Bit;
+ });
+ } else {
+ ALOGW("getId64Bit is not supported with the current HAL implementation.");
+ return static_cast<jlong>(
+ ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT);
+ }
+
+ return (res == Result::SUCCESS) ?
+ static_cast<jlong>(id) : static_cast<jlong>(
+ ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT);
+}
+
static jint android_media_tv_Tuner_set_filter_data_source(
JNIEnv* env, jobject filter, jobject srcFilter) {
sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter();
@@ -3684,6 +4139,7 @@
static const JNINativeMethod gTunerMethods[] = {
{ "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init },
{ "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
+ { "nativeGetTunerVersion", "()I", (void *)android_media_tv_Tuner_native_get_tuner_version },
{ "nativeGetFrontendIds", "()Ljava/util/List;",
(void *)android_media_tv_Tuner_get_frontend_ids },
{ "nativeOpenFrontendByHandle", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
@@ -3705,6 +4161,10 @@
{ "nativeGetAvSyncTime", "(I)Ljava/lang/Long;",
(void *)android_media_tv_Tuner_get_av_sync_time },
{ "nativeConnectCiCam", "(I)I", (void *)android_media_tv_Tuner_connect_cicam },
+ { "nativeLinkCiCam", "(I)I",
+ (void *)android_media_tv_Tuner_link_cicam },
+ { "nativeUnlinkCiCam", "(I)I",
+ (void *)android_media_tv_Tuner_unlink_cicam },
{ "nativeDisconnectCiCam", "()I", (void *)android_media_tv_Tuner_disconnect_cicam },
{ "nativeGetFrontendInfo", "(I)Landroid/media/tv/tuner/frontend/FrontendInfo;",
(void *)android_media_tv_Tuner_get_frontend_info },
@@ -3735,6 +4195,8 @@
{ "nativeConfigureFilter", "(IILandroid/media/tv/tuner/filter/FilterConfiguration;)I",
(void *)android_media_tv_Tuner_configure_filter },
{ "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id },
+ { "nativeGetId64Bit", "()J",
+ (void *)android_media_tv_Tuner_get_filter_64bit_id },
{ "nativeSetDataSource", "(Landroid/media/tv/tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_set_filter_data_source },
{ "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter },
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index c4deeaf..2b73f31 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -17,7 +17,12 @@
#ifndef _ANDROID_MEDIA_TV_TUNER_H_
#define _ANDROID_MEDIA_TV_TUNER_H_
-#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <android/hardware/tv/tuner/1.1/IFilter.h>
+#include <android/hardware/tv/tuner/1.1/IFilterCallback.h>
+#include <android/hardware/tv/tuner/1.1/IFrontend.h>
+#include <android/hardware/tv/tuner/1.1/ITuner.h>
+#include <android/hardware/tv/tuner/1.1/types.h>
+
#include <C2BlockInternal.h>
#include <C2HandleIonInternal.h>
#include <C2ParamDef.h>
@@ -38,6 +43,7 @@
using ::android::hardware::hidl_vec;
using ::android::hardware::kSynchronizedReadWrite;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
+using ::android::hardware::tv::tuner::V1_1::DemuxFilterEventExt;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
using ::android::hardware::tv::tuner::V1_0::DemuxPid;
@@ -49,12 +55,13 @@
using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
using ::android::hardware::tv::tuner::V1_0::FrontendScanType;
using ::android::hardware::tv::tuner::V1_0::FrontendSettings;
+using ::android::hardware::tv::tuner::V1_1::FrontendSettingsExt1_1;
using ::android::hardware::tv::tuner::V1_0::IDemux;
using ::android::hardware::tv::tuner::V1_0::IDescrambler;
using ::android::hardware::tv::tuner::V1_0::IDvr;
using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
using ::android::hardware::tv::tuner::V1_0::IFilter;
-using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
+using ::android::hardware::tv::tuner::V1_1::IFilterCallback;
using ::android::hardware::tv::tuner::V1_0::IFrontend;
using ::android::hardware::tv::tuner::V1_0::IFrontendCallback;
using ::android::hardware::tv::tuner::V1_0::ILnb;
@@ -112,18 +119,32 @@
int mFd;
};
+struct Filter : public RefBase {
+ Filter(sp<IFilter> sp, jobject obj);
+ ~Filter();
+ int close();
+ sp<IFilter> getIFilter();
+ sp<IFilter> mFilterSp;
+ std::unique_ptr<MQ> mFilterMQ;
+ EventFlag* mFilterMQEventFlag;
+ jweak mFilterObj;
+ native_handle_t* mAvSharedHandle;
+ uint64_t mAvSharedMemSize;
+ bool mIsMediaFilter;
+};
+
struct MediaEvent : public RefBase {
- MediaEvent(sp<IFilter> iFilter, hidl_handle avHandle, uint64_t dataId,
- uint64_t dataLength, jobject obj);
+ MediaEvent(sp<Filter> filter, hidl_handle avHandle, uint64_t dataId,
+ uint64_t dataSize, jobject obj);
~MediaEvent();
jobject getLinearBlock();
uint64_t getAudioHandle();
void finalize();
- sp<IFilter> mIFilter;
+ sp<Filter> mFilter;
native_handle_t* mAvHandle;
uint64_t mDataId;
- uint64_t mDataLength;
+ uint64_t mDataSize;
uint8_t* mBuffer;
android::Mutex mLock;
int mDataIdRefCnt;
@@ -134,26 +155,16 @@
std::weak_ptr<C2Buffer> mC2Buffer;
};
-struct Filter : public RefBase {
- Filter(sp<IFilter> sp, jobject obj);
- ~Filter();
- int close();
- sp<IFilter> getIFilter();
- sp<IFilter> mFilterSp;
- std::unique_ptr<MQ> mFilterMQ;
- EventFlag* mFilterMQEventFlag;
- jweak mFilterObj;
-};
-
struct FilterCallback : public IFilterCallback {
~FilterCallback();
+ virtual Return<void> onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
+ const DemuxFilterEventExt& filterEventExt);
virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent);
virtual Return<void> onFilterStatus(const DemuxFilterStatus status);
void setFilter(const sp<Filter> filter);
private:
- jweak mFilter;
- sp<IFilter> mIFilter;
+ sp<Filter> mFilter;
jobjectArray getSectionEvent(
jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
jobjectArray getMediaEvent(
@@ -161,9 +172,11 @@
jobjectArray getPesEvent(
jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
jobjectArray getTsRecordEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
+ jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>&events,
+ const std::vector<DemuxFilterEventExt::Event>& eventsExt);
jobjectArray getMmtpRecordEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
+ jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>&events,
+ const std::vector<DemuxFilterEventExt::Event>& eventsExt);
jobjectArray getDownloadEvent(
jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
jobjectArray getIpPayloadEvent(
@@ -194,17 +207,21 @@
struct JTuner : public RefBase {
JTuner(JNIEnv *env, jobject thiz);
sp<ITuner> getTunerService();
+ int getTunerVersion();
jobject getAvSyncHwId(sp<Filter> filter);
jobject getAvSyncTime(jint id);
int connectCiCam(jint id);
+ int linkCiCam(jint id);
int disconnectCiCam();
+ int unlinkCiCam(jint id);
jobject getFrontendIds();
jobject openFrontendById(int id);
jint closeFrontendById(int id);
jobject getFrontendInfo(int id);
- int tune(const FrontendSettings& settings);
+ int tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
int stopTune();
- int scan(const FrontendSettings& settings, FrontendScanType scanType);
+ int scan(const FrontendSettings& settings, FrontendScanType scanType,
+ const FrontendSettingsExt1_1& settingsExt1_1);
int stopScan();
int setLnb(int id);
int setLna(bool enable);
@@ -229,8 +246,13 @@
jclass mClass;
jweak mObject;
static sp<ITuner> mTuner;
+ static sp<::android::hardware::tv::tuner::V1_1::ITuner> mTuner_1_1;
+ // An integer that carries the Tuner version. The high 16 bits are the major version number
+ // while the low 16 bits are the minor version. Default value is unknown version 0.
+ static int mTunerVersion;
hidl_vec<FrontendId> mFeIds;
sp<IFrontend> mFe;
+ sp<::android::hardware::tv::tuner::V1_1::IFrontend> mFe_1_1;
int mFeId;
hidl_vec<LnbId> mLnbIds;
sp<ILnb> mLnb;
@@ -245,6 +267,7 @@
static jobject getIsdbs3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
static jobject getIsdbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
static jobject getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
+ static jobject getDtmbFrontendCaps(JNIEnv *env, int id);
};
class C2DataIdInfo : public C2Param {
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 45c49e5..0d53ab1 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -331,7 +331,7 @@
}
if (deviceType != AUDIO_DEVICE_NONE) {
- device.mType = deviceType;
+ device.mType = (audio_devices_t)deviceType;
ScopedUtfChars address(env, deviceAddress);
device.setAddress(address.c_str());
}
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index ca3cc855..26725f8 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -200,7 +200,7 @@
paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
paa->content_type =
(audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
- paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
+ paa->flags = (audio_flags_mask_t) env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
ALOGV("android_media_SoundPool_native_setup");
auto *ap = new SoundPool(maxChannels, paa);
diff --git a/media/tests/AudioPolicyTest/AndroidManifest.xml b/media/tests/AudioPolicyTest/AndroidManifest.xml
index adb058c..a7ab828 100644
--- a/media/tests/AudioPolicyTest/AndroidManifest.xml
+++ b/media/tests/AudioPolicyTest/AndroidManifest.xml
@@ -25,7 +25,7 @@
<application>
<uses-library android:name="android.test.runner" />
<activity android:label="@string/app_name" android:name="AudioPolicyTest"
- android:screenOrientation="landscape">
+ android:screenOrientation="landscape" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
index 5a0a50c..4d0c258 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -11,7 +11,8 @@
static_libs: [
"android-support-test",
"mockito-target-minus-junit4",
- "testng"
+ "testng",
+ "truth-prebuilt",
],
platform_apis: true,
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouteInfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouteInfoTest.java
new file mode 100644
index 0000000..92e4c95
--- /dev/null
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouteInfoTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaroutertest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hardware.display.DisplayManagerGlobal;
+import android.media.MediaRouter;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Display;
+import android.view.DisplayAddress;
+import android.view.DisplayAdjustments;
+import android.view.DisplayInfo;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaRouteInfoTest {
+ private TestableRouteInfo mRouteInfo;
+ private static Display sWifiDisplay;
+ private static Display sExternalDisplay;
+ private static Display sInternalDisplay;
+ private static final String FAKE_MAC_ADDRESS = "fake MAC address";
+
+ @BeforeClass
+ public static void setUpOnce() {
+ final DisplayManagerGlobal global = DisplayManagerGlobal.getInstance();
+ final DisplayInfo wifiInfo = new DisplayInfo();
+ wifiInfo.flags = Display.FLAG_PRESENTATION;
+ wifiInfo.type = Display.TYPE_WIFI;
+ wifiInfo.address = DisplayAddress.fromMacAddress(FAKE_MAC_ADDRESS);
+ sWifiDisplay = new Display(global, 2, wifiInfo, new DisplayAdjustments());
+
+ final DisplayInfo externalInfo = new DisplayInfo();
+ externalInfo.flags = Display.FLAG_PRESENTATION;
+ externalInfo.type = Display.TYPE_EXTERNAL;
+ sExternalDisplay = new Display(global, 3, externalInfo, new DisplayAdjustments());
+
+ final DisplayInfo internalInfo = new DisplayInfo();
+ internalInfo.flags = Display.FLAG_PRESENTATION;
+ internalInfo.type = Display.TYPE_INTERNAL;
+ sInternalDisplay = new Display(global, 4, internalInfo, new DisplayAdjustments());
+ }
+
+ @Before
+ public void setUp() {
+ mRouteInfo = new TestableRouteInfo();
+ }
+
+ @Test
+ public void testGetPresentationDisplay_notLiveVideo() {
+ mRouteInfo.setPresentationDisplays(sWifiDisplay);
+ mRouteInfo.mSupportedType = MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
+
+ mRouteInfo.updatePresentationDisplay();
+
+ assertThat(mRouteInfo.getPresentationDisplay()).isNull();
+ }
+
+ @Test
+ public void testGetPresentationDisplay_includesLiveVideo() {
+ mRouteInfo.setPresentationDisplays(sWifiDisplay);
+ mRouteInfo.mSupportedType |= MediaRouter.ROUTE_TYPE_LIVE_AUDIO;
+
+ mRouteInfo.updatePresentationDisplay();
+
+ assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay);
+ }
+
+ @Test
+ public void testGetPresentationDisplay_noPresentationDisplay() {
+ mRouteInfo.updatePresentationDisplay();
+
+ assertThat(mRouteInfo.getPresentationDisplay()).isNull();
+ }
+
+ @Test
+ public void testGetPresentationDisplay_wifiDisplayOnly() {
+ mRouteInfo.setPresentationDisplays(sWifiDisplay);
+
+ mRouteInfo.updatePresentationDisplay();
+
+ assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay);
+ }
+
+ @Test
+ public void testGetPresentationDisplay_externalDisplayOnly() {
+ mRouteInfo.setPresentationDisplays(sExternalDisplay);
+
+ mRouteInfo.updatePresentationDisplay();
+
+ assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sExternalDisplay);
+ }
+
+ @Test
+ public void testGetPresentationDisplay_internalDisplayOnly() {
+ mRouteInfo.setPresentationDisplays(sInternalDisplay);
+
+ mRouteInfo.updatePresentationDisplay();
+
+ assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sInternalDisplay);
+ }
+
+ @Test
+ public void testGetPresentationDisplay_addressNotMatch() {
+ mRouteInfo.setPresentationDisplays(sWifiDisplay);
+ mRouteInfo.mDeviceAddress = "Not match";
+
+ mRouteInfo.updatePresentationDisplay();
+
+ assertThat(mRouteInfo.getPresentationDisplay()).isNull();
+ }
+
+ @Test
+ public void testGetPresentationDisplay_containsWifiAndExternalDisplays_returnWifiDisplay() {
+ mRouteInfo.setPresentationDisplays(sExternalDisplay, sWifiDisplay);
+
+ mRouteInfo.updatePresentationDisplay();
+
+ assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay);
+ }
+
+ @Test
+ public void testGetPresentationDisplay_containsExternalAndInternalDisplays_returnExternal() {
+ mRouteInfo.setPresentationDisplays(sExternalDisplay, sInternalDisplay);
+
+ mRouteInfo.updatePresentationDisplay();
+
+ assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sExternalDisplay);
+ }
+
+ @Test
+ public void testGetPresentationDisplay_containsAllDisplays_returnWifiDisplay() {
+ mRouteInfo.setPresentationDisplays(sExternalDisplay, sInternalDisplay, sWifiDisplay);
+
+ mRouteInfo.updatePresentationDisplay();
+
+ assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay);
+ }
+
+ private static class TestableRouteInfo extends MediaRouter.RouteInfo {
+ private Display[] mDisplays = new Display[0];
+ private int mSupportedType = MediaRouter.ROUTE_TYPE_LIVE_VIDEO;
+ private String mDeviceAddress = FAKE_MAC_ADDRESS;
+ private MediaRouter.RouteInfo mDefaultRouteInfo = null;
+
+ private TestableRouteInfo() {
+ super(null);
+ }
+
+ private void setPresentationDisplays(Display ...displays) {
+ mDisplays = new Display[displays.length];
+ System.arraycopy(displays, 0, mDisplays, 0, displays.length);
+ }
+
+ @Override
+ public Display[] getAllPresentationDisplays() {
+ return mDisplays;
+ }
+
+ @Override
+ public int getSupportedTypes() {
+ return mSupportedType;
+ }
+
+ @Override
+ public String getDeviceAddress() {
+ return mDeviceAddress;
+ }
+
+ @Override
+ public MediaRouter.RouteInfo getDefaultAudioVideo() {
+ return mDefaultRouteInfo;
+ }
+ }
+}
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index 56f3906..ac4c16a 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -346,3 +346,25 @@
void AImageDecoder_delete(AImageDecoder* decoder) {
delete toDecoder(decoder);
}
+
+bool AImageDecoder_isAnimated(AImageDecoder* decoder) {
+ if (!decoder) return false;
+
+ ImageDecoder* imageDecoder = toDecoder(decoder);
+ return imageDecoder->mCodec->codec()->getFrameCount() > 1;
+}
+
+int32_t AImageDecoder_getRepeatCount(AImageDecoder* decoder) {
+ if (!decoder) return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
+
+ ImageDecoder* imageDecoder = toDecoder(decoder);
+ const int count = imageDecoder->mCodec->codec()->getRepetitionCount();
+
+ // Skia should not report anything out of range, but defensively treat
+ // negative and too big as INFINITE.
+ if (count == SkCodec::kRepetitionCountInfinite || count < 0
+ || count > std::numeric_limits<int32_t>::max()) {
+ return ANDROID_IMAGE_DECODER_INFINITE;
+ }
+ return count;
+}
diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt
index 01c1477..a184ab9 100644
--- a/native/graphics/jni/libjnigraphics.map.txt
+++ b/native/graphics/jni/libjnigraphics.map.txt
@@ -13,6 +13,8 @@
AImageDecoder_setTargetSize; # introduced=30
AImageDecoder_computeSampledSize; # introduced=30
AImageDecoder_setCrop; # introduced=30
+ AImageDecoder_isAnimated; # introduced=31
+ AImageDecoder_getRepeatCount; # introduced=31
AImageDecoderHeaderInfo_getWidth; # introduced=30
AImageDecoderHeaderInfo_getHeight; # introduced=30
AImageDecoderHeaderInfo_getMimeType; # introduced=30
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index a0aa0e0..45d2214 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -1613,6 +1613,7 @@
field public static final int windowHideAnimation = 16842935; // 0x10100b7
field public static final int windowIsFloating = 16842839; // 0x1010057
field public static final int windowIsTranslucent = 16842840; // 0x1010058
+ field public static final int windowLayoutAffinity = 16844313; // 0x1010619
field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586
field public static final int windowLightNavigationBar = 16844140; // 0x101056c
field public static final int windowLightStatusBar = 16844000; // 0x10104e0
@@ -2889,6 +2890,7 @@
field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28
field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
+ field public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43; // 0x2b
field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c
@@ -2897,11 +2899,13 @@
field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29
field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
+ field public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44; // 0x2c
field public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; // 0x1e
field public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; // 0x1f
field public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32; // 0x20
field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d
field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18
+ field public static final int GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD = 45; // 0x2d
field public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; // 0x26
field public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42; // 0x2a
field public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; // 0x25
@@ -4898,7 +4902,7 @@
@Deprecated public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
ctor @Deprecated public Fragment();
method @Deprecated public void dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]);
- method @Deprecated public final boolean equals(Object);
+ method @Deprecated public final boolean equals(@Nullable Object);
method @Deprecated public final android.app.Activity getActivity();
method @Deprecated public boolean getAllowEnterTransitionOverlap();
method @Deprecated public boolean getAllowReturnTransitionOverlap();
@@ -13063,38 +13067,38 @@
public interface Cursor extends java.io.Closeable {
method public void close();
- method public void copyStringToBuffer(int, android.database.CharArrayBuffer);
+ method public void copyStringToBuffer(@IntRange(from=0) int, android.database.CharArrayBuffer);
method @Deprecated public void deactivate();
- method public byte[] getBlob(int);
- method public int getColumnCount();
- method public int getColumnIndex(String);
- method public int getColumnIndexOrThrow(String) throws java.lang.IllegalArgumentException;
- method public String getColumnName(int);
+ method public byte[] getBlob(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getColumnCount();
+ method @IntRange(from=0xffffffff) public int getColumnIndex(String);
+ method @IntRange(from=0) public int getColumnIndexOrThrow(String) throws java.lang.IllegalArgumentException;
+ method public String getColumnName(@IntRange(from=0) int);
method public String[] getColumnNames();
- method public int getCount();
- method public double getDouble(int);
+ method @IntRange(from=0) public int getCount();
+ method public double getDouble(@IntRange(from=0) int);
method public android.os.Bundle getExtras();
- method public float getFloat(int);
- method public int getInt(int);
- method public long getLong(int);
+ method public float getFloat(@IntRange(from=0) int);
+ method public int getInt(@IntRange(from=0) int);
+ method public long getLong(@IntRange(from=0) int);
method public android.net.Uri getNotificationUri();
method @Nullable public default java.util.List<android.net.Uri> getNotificationUris();
- method public int getPosition();
- method public short getShort(int);
- method public String getString(int);
- method public int getType(int);
+ method @IntRange(from=0xffffffff) public int getPosition();
+ method public short getShort(@IntRange(from=0) int);
+ method public String getString(@IntRange(from=0) int);
+ method public int getType(@IntRange(from=0) int);
method public boolean getWantsAllOnMoveCalls();
method public boolean isAfterLast();
method public boolean isBeforeFirst();
method public boolean isClosed();
method public boolean isFirst();
method public boolean isLast();
- method public boolean isNull(int);
+ method public boolean isNull(@IntRange(from=0) int);
method public boolean move(int);
method public boolean moveToFirst();
method public boolean moveToLast();
method public boolean moveToNext();
- method public boolean moveToPosition(int);
+ method public boolean moveToPosition(@IntRange(from=0xffffffff) int);
method public boolean moveToPrevious();
method public void registerContentObserver(android.database.ContentObserver);
method public void registerDataSetObserver(android.database.DataSetObserver);
@@ -13136,33 +13140,33 @@
ctor @Deprecated public CursorWindow(boolean);
method public boolean allocRow();
method public void clear();
- method public void copyStringToBuffer(int, int, android.database.CharArrayBuffer);
+ method public void copyStringToBuffer(@IntRange(from=0) int, @IntRange(from=0) int, android.database.CharArrayBuffer);
method public int describeContents();
method public void freeLastRow();
- method public byte[] getBlob(int, int);
- method public double getDouble(int, int);
- method public float getFloat(int, int);
- method public int getInt(int, int);
- method public long getLong(int, int);
- method public int getNumRows();
- method public short getShort(int, int);
- method public int getStartPosition();
- method public String getString(int, int);
- method public int getType(int, int);
- method @Deprecated public boolean isBlob(int, int);
- method @Deprecated public boolean isFloat(int, int);
- method @Deprecated public boolean isLong(int, int);
- method @Deprecated public boolean isNull(int, int);
- method @Deprecated public boolean isString(int, int);
+ method public byte[] getBlob(@IntRange(from=0) int, @IntRange(from=0) int);
+ method public double getDouble(@IntRange(from=0) int, @IntRange(from=0) int);
+ method public float getFloat(@IntRange(from=0) int, @IntRange(from=0) int);
+ method public int getInt(@IntRange(from=0) int, @IntRange(from=0) int);
+ method public long getLong(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @IntRange(from=0) public int getNumRows();
+ method public short getShort(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @IntRange(from=0) public int getStartPosition();
+ method public String getString(@IntRange(from=0) int, @IntRange(from=0) int);
+ method public int getType(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @Deprecated public boolean isBlob(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @Deprecated public boolean isFloat(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @Deprecated public boolean isLong(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @Deprecated public boolean isNull(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @Deprecated public boolean isString(@IntRange(from=0) int, @IntRange(from=0) int);
method public static android.database.CursorWindow newFromParcel(android.os.Parcel);
method protected void onAllReferencesReleased();
- method public boolean putBlob(byte[], int, int);
- method public boolean putDouble(double, int, int);
- method public boolean putLong(long, int, int);
- method public boolean putNull(int, int);
- method public boolean putString(String, int, int);
- method public boolean setNumColumns(int);
- method public void setStartPosition(int);
+ method public boolean putBlob(byte[], @IntRange(from=0) int, @IntRange(from=0) int);
+ method public boolean putDouble(double, @IntRange(from=0) int, @IntRange(from=0) int);
+ method public boolean putLong(long, @IntRange(from=0) int, @IntRange(from=0) int);
+ method public boolean putNull(@IntRange(from=0) int, @IntRange(from=0) int);
+ method public boolean putString(String, @IntRange(from=0) int, @IntRange(from=0) int);
+ method public boolean setNumColumns(@IntRange(from=0) int);
+ method public void setStartPosition(@IntRange(from=0) int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.database.CursorWindow> CREATOR;
}
@@ -16504,23 +16508,6 @@
package android.graphics.text {
- public class GlyphStyle {
- ctor public GlyphStyle(@ColorInt int, @FloatRange(from=0) float, @FloatRange(from=0) float, @FloatRange(from=0) float, int);
- ctor public GlyphStyle(@NonNull android.graphics.Paint);
- method public void applyToPaint(@NonNull android.graphics.Paint);
- method @ColorInt public int getColor();
- method public int getFlags();
- method @FloatRange(from=0) public float getFontSize();
- method @FloatRange(from=0) public float getScaleX();
- method @FloatRange(from=0) public float getSkewX();
- method public void setColor(@ColorInt int);
- method public void setFlags(int);
- method public void setFontSize(@FloatRange(from=0) float);
- method public void setFromPaint(@NonNull android.graphics.Paint);
- method public void setScaleX(@FloatRange(from=0) float);
- method public void setSkewX(@FloatRange(from=0) float);
- }
-
public class LineBreaker {
method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int);
field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -16582,20 +16569,19 @@
}
public final class PositionedGlyphs {
+ method public float getAdvance();
method public float getAscent();
method public float getDescent();
method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int);
method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int);
- method public float getOriginX();
- method public float getOriginY();
- method public float getPositionX(@IntRange(from=0) int);
- method public float getPositionY(@IntRange(from=0) int);
- method @NonNull public android.graphics.text.GlyphStyle getStyle();
- method public float getTotalAdvance();
+ method public float getGlyphX(@IntRange(from=0) int);
+ method public float getGlyphY(@IntRange(from=0) int);
+ method public float getOffsetX();
+ method public float getOffsetY();
method @IntRange(from=0) public int glyphCount();
}
- public class TextShaper {
+ public class TextRunShaper {
method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull char[], int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull CharSequence, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
}
@@ -24071,9 +24057,13 @@
method @IntRange(from=1, to=java.lang.Integer.MAX_VALUE) public int getMaxUpdates();
method @FloatRange(from=0, to=java.lang.Float.MAX_VALUE) public float getMinUpdateDistanceMeters();
method @IntRange(from=0) public long getMinUpdateIntervalMillis();
+ method public int getQuality();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR;
field public static final long PASSIVE_INTERVAL = 9223372036854775807L; // 0x7fffffffffffffffL
+ field public static final int QUALITY_BALANCED_POWER_ACCURACY = 102; // 0x66
+ field public static final int QUALITY_HIGH_ACCURACY = 100; // 0x64
+ field public static final int QUALITY_LOW_POWER = 104; // 0x68
}
public static final class LocationRequest.Builder {
@@ -24086,6 +24076,7 @@
method @NonNull public android.location.LocationRequest.Builder setMaxUpdates(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int);
method @NonNull public android.location.LocationRequest.Builder setMinUpdateDistanceMeters(@FloatRange(from=0, to=java.lang.Float.MAX_VALUE) float);
method @NonNull public android.location.LocationRequest.Builder setMinUpdateIntervalMillis(@IntRange(from=0) long);
+ method @NonNull public android.location.LocationRequest.Builder setQuality(int);
}
public interface OnNmeaMessageListener {
@@ -25438,6 +25429,7 @@
public static final class MediaCodec.CryptoInfo {
ctor public MediaCodec.CryptoInfo();
+ method @NonNull public android.media.MediaCodec.CryptoInfo.Pattern getPattern();
method public void set(int, @NonNull int[], @NonNull int[], @NonNull byte[], @NonNull byte[], int);
method public void setPattern(android.media.MediaCodec.CryptoInfo.Pattern);
field public byte[] iv;
@@ -26366,7 +26358,7 @@
method public boolean containsKey(String);
method public int describeContents();
method public android.graphics.Bitmap getBitmap(String);
- method @IntRange(from=0) public int getBitmapDimensionLimit();
+ method @IntRange(from=1) public int getBitmapDimensionLimit();
method @NonNull public android.media.MediaDescription getDescription();
method public long getLong(String);
method public android.media.Rating getRating(String);
@@ -26416,7 +26408,7 @@
method public android.media.MediaMetadata.Builder putRating(String, android.media.Rating);
method public android.media.MediaMetadata.Builder putString(String, String);
method public android.media.MediaMetadata.Builder putText(String, CharSequence);
- method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(int);
+ method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(@IntRange(from=1) int);
}
@Deprecated public abstract class MediaMetadataEditor {
@@ -30014,9 +30006,12 @@
method public int describeContents();
method @NonNull public byte[] getKey();
method @NonNull public String getName();
+ method @NonNull public static java.util.Set<java.lang.String> getSupportedAlgorithms();
method public int getTruncationLengthBits();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final String AUTH_AES_XCBC = "xcbc(aes)";
field public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
+ field public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)";
field public static final String AUTH_HMAC_MD5 = "hmac(md5)";
field public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
field public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
@@ -30024,6 +30019,7 @@
field public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
field @NonNull public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
field public static final String CRYPT_AES_CBC = "cbc(aes)";
+ field public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))";
}
public final class IpSecManager {
@@ -41429,8 +41425,16 @@
method @NonNull public java.util.Map<android.view.autofill.AutofillId,android.service.autofill.FieldClassification> getFieldsClassification();
method @NonNull public java.util.Set<java.lang.String> getIgnoredDatasetIds();
method @NonNull public java.util.Map<android.view.autofill.AutofillId,java.util.Set<java.lang.String>> getManuallyEnteredField();
+ method public int getNoSaveReason();
method @NonNull public java.util.Set<java.lang.String> getSelectedDatasetIds();
method public int getType();
+ field public static final int NO_SAVE_REASON_DATASET_MATCH = 6; // 0x6
+ field public static final int NO_SAVE_REASON_FIELD_VALIDATION_FAILED = 5; // 0x5
+ field public static final int NO_SAVE_REASON_HAS_EMPTY_REQUIRED = 3; // 0x3
+ field public static final int NO_SAVE_REASON_NONE = 0; // 0x0
+ field public static final int NO_SAVE_REASON_NO_SAVE_INFO = 1; // 0x1
+ field public static final int NO_SAVE_REASON_NO_VALUE_CHANGED = 4; // 0x4
+ field public static final int NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG = 2; // 0x2
field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
field public static final int TYPE_CONTEXT_COMMITTED = 4; // 0x4
field public static final int TYPE_DATASETS_SHOWN = 5; // 0x5
@@ -42234,11 +42238,13 @@
method public long getLastAudiblyAlertedMillis();
method public String getOverrideGroupKey();
method public int getRank();
+ method @Nullable public android.content.pm.ShortcutInfo getShortcutInfo();
method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions();
method @NonNull public java.util.List<java.lang.CharSequence> getSmartReplies();
method public int getSuppressedVisualEffects();
method public int getUserSentiment();
method public boolean isAmbient();
+ method public boolean isConversation();
method public boolean isSuspended();
method public boolean matchesInterruptionFilter();
field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
@@ -46574,6 +46580,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getDeviceSoftwareVersion();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getEmergencyNumberList();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getEmergencyNumberList(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<java.lang.String> getEquivalentHomePlmns();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String[] getForbiddenPlmns();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getGroupIdLevel1();
method public String getIccAuthentication(int, int, String);
@@ -46598,7 +46605,7 @@
method @Deprecated public int getPhoneCount();
method public int getPhoneType();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
- method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
+ method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
method @Nullable public android.telephony.SignalStrength getSignalStrength();
method public int getSimCarrierId();
method @Nullable public CharSequence getSimCarrierIdName();
@@ -46621,7 +46628,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailAlphaTag();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getVoiceNetworkType();
- method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
+ method @Nullable public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
method public boolean hasCarrierPrivileges();
method public boolean hasIccCard();
method @Deprecated public boolean iccCloseLogicalChannel(int);
@@ -48186,10 +48193,6 @@
method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean);
}
- public class StyledTextShaper {
- method @NonNull public static java.util.List<android.graphics.text.PositionedGlyphs> shapeText(@NonNull CharSequence, int, int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint);
- }
-
public interface TextDirectionHeuristic {
method public boolean isRtl(char[], int, int);
method public boolean isRtl(CharSequence, int, int);
@@ -48219,6 +48222,14 @@
field @Px public float underlineThickness;
}
+ public class TextShaper {
+ method public static void shapeText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint, @NonNull android.text.TextShaper.GlyphsConsumer);
+ }
+
+ public static interface TextShaper.GlyphsConsumer {
+ method public void accept(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.text.PositionedGlyphs, @NonNull android.text.TextPaint);
+ }
+
public class TextUtils {
method @Deprecated public static CharSequence commaEllipsize(CharSequence, android.text.TextPaint, float, String, String);
method public static CharSequence concat(java.lang.CharSequence...);
@@ -51848,11 +51859,12 @@
method @Nullable public android.net.Uri getLinkUri();
method public int getSource();
field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
- field public static final int SOURCE_AUTOFILL = 3; // 0x3
- field public static final int SOURCE_CLIPBOARD = 0; // 0x0
- field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2
- field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
- field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4
+ field public static final int SOURCE_APP = 0; // 0x0
+ field public static final int SOURCE_AUTOFILL = 4; // 0x4
+ field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+ field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+ field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+ field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5
}
public static final class OnReceiveContentCallback.Payload.Builder {
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index 6e3cec6..3c1d19f 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -1,10 +1,20 @@
// Signature format: 2.0
package android.app {
+ public class ActivityManager {
+ method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
+ method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
+ }
+
public class AppOpsManager {
field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
}
+ public abstract class HomeVisibilityListener {
+ ctor public HomeVisibilityListener();
+ method public abstract void onHomeVisibilityChanged(boolean);
+ }
+
public class NotificationManager {
method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED";
@@ -53,6 +63,10 @@
field public static final int FLAG_FROM_KEY = 4096; // 0x1000
}
+ public class MediaMetadataRetriever implements java.lang.AutoCloseable {
+ field public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40; // 0x28
+ }
+
}
package android.media.session {
@@ -119,6 +133,18 @@
}
+package android.telephony {
+
+ public abstract class CellSignalStrength {
+ method public static int getNumSignalStrengthLevels();
+ }
+
+ public class TelephonyManager {
+ method @NonNull public static int[] getAllNetworkTypes();
+ }
+
+}
+
package android.util {
public class AtomicFile {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 0c33ed8..f558b48 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -122,6 +122,7 @@
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
+ field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS";
field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
@@ -674,8 +675,10 @@
public class NotificationManager {
method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments();
method @Nullable public android.content.ComponentName getAllowedNotificationAssistant();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public java.util.List<android.content.ComponentName> getEnabledNotificationListeners();
method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName);
method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean);
field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL";
field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL";
field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL";
@@ -1388,20 +1391,20 @@
package android.app.usage {
public final class CacheQuotaHint implements android.os.Parcelable {
- ctor public CacheQuotaHint(android.app.usage.CacheQuotaHint.Builder);
+ ctor public CacheQuotaHint(@NonNull android.app.usage.CacheQuotaHint.Builder);
method public int describeContents();
method public long getQuota();
method public int getUid();
- method public android.app.usage.UsageStats getUsageStats();
- method public String getVolumeUuid();
- method public void writeToParcel(android.os.Parcel, int);
+ method @Nullable public android.app.usage.UsageStats getUsageStats();
+ method @Nullable public String getVolumeUuid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.CacheQuotaHint> CREATOR;
field public static final long QUOTA_NOT_SET = -1L; // 0xffffffffffffffffL
}
public static final class CacheQuotaHint.Builder {
ctor public CacheQuotaHint.Builder();
- ctor public CacheQuotaHint.Builder(android.app.usage.CacheQuotaHint);
+ ctor public CacheQuotaHint.Builder(@NonNull android.app.usage.CacheQuotaHint);
method @NonNull public android.app.usage.CacheQuotaHint build();
method @NonNull public android.app.usage.CacheQuotaHint.Builder setQuota(long);
method @NonNull public android.app.usage.CacheQuotaHint.Builder setUid(int);
@@ -4111,7 +4114,6 @@
method @Deprecated public long getInterval();
method @Deprecated public int getNumUpdates();
method @Deprecated @NonNull public String getProvider();
- method public int getQuality();
method @Deprecated public float getSmallestDisplacement();
method @NonNull public android.os.WorkSource getWorkSource();
method public boolean isHiddenFromAppOps();
@@ -4130,11 +4132,11 @@
method @Deprecated @NonNull public android.location.LocationRequest setQuality(int);
method @Deprecated @NonNull public android.location.LocationRequest setSmallestDisplacement(float);
method @Deprecated public void setWorkSource(@Nullable android.os.WorkSource);
- field public static final int ACCURACY_BLOCK = 102; // 0x66
- field public static final int ACCURACY_CITY = 104; // 0x68
- field public static final int ACCURACY_FINE = 100; // 0x64
- field public static final int POWER_HIGH = 203; // 0xcb
- field public static final int POWER_LOW = 201; // 0xc9
+ field @Deprecated public static final int ACCURACY_BLOCK = 102; // 0x66
+ field @Deprecated public static final int ACCURACY_CITY = 104; // 0x68
+ field @Deprecated public static final int ACCURACY_FINE = 100; // 0x64
+ field @Deprecated public static final int POWER_HIGH = 203; // 0xcb
+ field @Deprecated public static final int POWER_LOW = 201; // 0xc9
field @Deprecated public static final int POWER_NONE = 200; // 0xc8
}
@@ -4142,7 +4144,6 @@
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean);
- method @NonNull public android.location.LocationRequest.Builder setQuality(int);
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
}
@@ -4709,6 +4710,22 @@
field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.DvbDeviceInfo> CREATOR;
}
+ public final class TunedInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getAppTag();
+ method public int getAppType();
+ method @Nullable public android.net.Uri getChannelUri();
+ method @NonNull public String getInputId();
+ method public boolean isForeground();
+ method public boolean isRecordingSession();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int APP_TAG_SELF = 0; // 0x0
+ field public static final int APP_TYPE_NON_SYSTEM = 3; // 0x3
+ field public static final int APP_TYPE_SELF = 1; // 0x1
+ field public static final int APP_TYPE_SYSTEM = 2; // 0x2
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TunedInfo> CREATOR;
+ }
+
public final class TvContentRatingSystemInfo implements android.os.Parcelable {
method public static android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo);
method public int describeContents();
@@ -4824,6 +4841,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating);
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig);
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String);
+ method @NonNull @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS") public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos();
method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList();
method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList();
@@ -4849,6 +4867,10 @@
method public abstract void onStreamConfigChanged(android.media.tv.TvStreamConfig[]);
}
+ public abstract static class TvInputManager.TvInputCallback {
+ method public void onCurrentTunedInfosUpdated(@NonNull java.util.List<android.media.tv.TunedInfo>);
+ }
+
public abstract class TvInputService extends android.app.Service {
method @Nullable public android.media.tv.TvInputInfo onHardwareAdded(android.media.tv.TvInputHardwareInfo);
method @Nullable public String onHardwareRemoved(android.media.tv.TvInputHardwareInfo);
@@ -4976,6 +4998,7 @@
method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
+ method public int linkFrontendToCiCam(int);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
method @Nullable public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
@@ -4989,9 +5012,14 @@
method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
+ method public int unlinkFrontendToCiCam(int);
method public void updateResourcePriority(int, int);
field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
+ field public static final long INVALID_FILTER_ID_64BIT = -1L; // 0xffffffffffffffffL
+ field public static final int INVALID_FRONTEND_SETTING_FREQUENCY = -1; // 0xffffffff
+ field public static final int INVALID_LTS_ID = -1; // 0xffffffff
+ field public static final int INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM = -1; // 0xffffffff
field public static final int INVALID_STREAM_ID = 65535; // 0xffff
field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL
field public static final int INVALID_TS_PID = 65535; // 0xffff
@@ -5011,15 +5039,22 @@
method public void onResourceLost(@NonNull android.media.tv.tuner.Tuner);
}
+ public final class TunerVersionChecker {
+ method public static int getTunerVersion();
+ field public static final int TUNER_VERSION_1_0 = 65536; // 0x10000
+ field public static final int TUNER_VERSION_1_1 = 65537; // 0x10001
+ field public static final int TUNER_VERSION_UNKNOWN = 0; // 0x0
+ }
+
}
package android.media.tv.tuner.dvr {
public class DvrPlayback implements java.lang.AutoCloseable {
- method public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter);
+ method @Deprecated public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter);
method public void close();
method public int configure(@NonNull android.media.tv.tuner.dvr.DvrSettings);
- method public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter);
+ method @Deprecated public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter);
method public int flush();
method public long read(long);
method public long read(@NonNull byte[], long, long);
@@ -5144,6 +5179,7 @@
method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
method public int flush();
method public int getId();
+ method public long getId64Bit();
method public int read(@NonNull byte[], long, long);
method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter);
method public int start();
@@ -5195,16 +5231,19 @@
method @NonNull public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder();
method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress();
method public int getDstPort();
+ method public int getIpFilterContextId();
method @NonNull @Size(min=4, max=16) public byte[] getSrcIpAddress();
method public int getSrcPort();
method public int getType();
method public boolean isPassthrough();
+ field public static final int INVALID_IP_FILTER_CONTEXT_ID = -1; // 0xffffffff
}
public static final class IpFilterConfiguration.Builder {
method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration build();
method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstIpAddress(@NonNull byte[]);
method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstPort(int);
+ method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setIpFilterContextId(int);
method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setPassthrough(boolean);
method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings);
method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSrcIpAddress(@NonNull byte[]);
@@ -5244,6 +5283,8 @@
public class MmtpRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
method public long getDataLength();
+ method public int getMpuSequenceNumber();
+ method public long getPts();
method public int getScHevcIndexMask();
}
@@ -5406,6 +5447,7 @@
public class TsRecordEvent extends android.media.tv.tuner.filter.FilterEvent {
method public long getDataLength();
method public int getPacketId();
+ method public long getPts();
method public int getScIndexMask();
method public int getTsIndexMask();
}
@@ -5421,9 +5463,13 @@
public class AnalogFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @NonNull public static android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder builder();
+ method public int getAftFlag();
method public int getSifStandard();
method public int getSignalType();
method public int getType();
+ field public static final int AFT_FLAG_FALSE = 2; // 0x2
+ field public static final int AFT_FLAG_TRUE = 1; // 0x1
+ field public static final int AFT_FLAG_UNDEFINED = 0; // 0x0
field public static final int SIF_AUTO = 1; // 0x1
field public static final int SIF_BG = 2; // 0x2
field public static final int SIF_BG_A2 = 4; // 0x4
@@ -5456,6 +5502,7 @@
public static class AnalogFrontendSettings.Builder {
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings build();
+ method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setAftFlag(int);
method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setFrequency(int);
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSifStandard(int);
method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSignalType(int);
@@ -5571,6 +5618,69 @@
method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int);
}
+ public class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
+ method public int getBandwidthCapability();
+ method public int getCodeRateCapability();
+ method public int getGuardIntervalCapability();
+ method public int getModulationCapability();
+ method public int getTimeInterleaveModeCapability();
+ method public int getTransmissionModeCapability();
+ }
+
+ public class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
+ method @NonNull public static android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder builder();
+ method public int getBandwidth();
+ method public int getCodeRate();
+ method public int getGuardInterval();
+ method public int getModulation();
+ method public int getTimeInterleaveMode();
+ method public int getTransmissionMode();
+ method public int getType();
+ field public static final int BANDWIDTH_6MHZ = 4; // 0x4
+ field public static final int BANDWIDTH_8MHZ = 2; // 0x2
+ field public static final int BANDWIDTH_AUTO = 1; // 0x1
+ field public static final int BANDWIDTH_UNDEFINED = 0; // 0x0
+ field public static final int CODERATE_2_5 = 2; // 0x2
+ field public static final int CODERATE_3_5 = 4; // 0x4
+ field public static final int CODERATE_4_5 = 8; // 0x8
+ field public static final int CODERATE_AUTO = 1; // 0x1
+ field public static final int CODERATE_UNDEFINED = 0; // 0x0
+ field public static final int GUARD_INTERVAL_AUTO = 1; // 0x1
+ field public static final int GUARD_INTERVAL_PN_420_CONST = 16; // 0x10
+ field public static final int GUARD_INTERVAL_PN_420_VARIOUS = 2; // 0x2
+ field public static final int GUARD_INTERVAL_PN_595_CONST = 4; // 0x4
+ field public static final int GUARD_INTERVAL_PN_945_CONST = 32; // 0x20
+ field public static final int GUARD_INTERVAL_PN_945_VARIOUS = 8; // 0x8
+ field public static final int GUARD_INTERVAL_PN_RESERVED = 64; // 0x40
+ field public static final int GUARD_INTERVAL_UNDEFINED = 0; // 0x0
+ field public static final int MODULATION_CONSTELLATION_16QAM = 8; // 0x8
+ field public static final int MODULATION_CONSTELLATION_32QAM = 16; // 0x10
+ field public static final int MODULATION_CONSTELLATION_4QAM = 2; // 0x2
+ field public static final int MODULATION_CONSTELLATION_4QAM_NR = 4; // 0x4
+ field public static final int MODULATION_CONSTELLATION_64QAM = 32; // 0x20
+ field public static final int MODULATION_CONSTELLATION_AUTO = 1; // 0x1
+ field public static final int MODULATION_CONSTELLATION_UNDEFINED = 0; // 0x0
+ field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1
+ field public static final int TIME_INTERLEAVE_MODE_TIMER_INT_240 = 2; // 0x2
+ field public static final int TIME_INTERLEAVE_MODE_TIMER_INT_720 = 4; // 0x4
+ field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0
+ field public static final int TRANSMISSION_MODE_AUTO = 1; // 0x1
+ field public static final int TRANSMISSION_MODE_C1 = 2; // 0x2
+ field public static final int TRANSMISSION_MODE_C3780 = 4; // 0x4
+ field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0
+ }
+
+ public static final class DtmbFrontendSettings.Builder {
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings build();
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setBandwidth(int);
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setCodeRate(int);
+ method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setFrequency(int);
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setGuardInterval(int);
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setModulation(int);
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setTimeInterleaveMode(int);
+ method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setTransmissionMode(int);
+ }
+
public class DvbcFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
method public int getAnnexCapability();
method public int getFecCapability();
@@ -5585,6 +5695,7 @@
method public int getOuterFec();
method public int getSpectralInversion();
method public int getSymbolRate();
+ method public int getTimeInterleaveMode();
method public int getType();
field public static final int ANNEX_A = 1; // 0x1
field public static final int ANNEX_B = 2; // 0x2
@@ -5600,9 +5711,20 @@
field public static final int OUTER_FEC_OUTER_FEC_NONE = 1; // 0x1
field public static final int OUTER_FEC_OUTER_FEC_RS = 2; // 0x2
field public static final int OUTER_FEC_UNDEFINED = 0; // 0x0
- field public static final int SPECTRAL_INVERSION_INVERTED = 2; // 0x2
- field public static final int SPECTRAL_INVERSION_NORMAL = 1; // 0x1
- field public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
+ field @Deprecated public static final int SPECTRAL_INVERSION_INVERTED = 2; // 0x2
+ field @Deprecated public static final int SPECTRAL_INVERSION_NORMAL = 1; // 0x1
+ field @Deprecated public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
+ field public static final int TIME_INTERLEAVE_MODE_128_1_0 = 2; // 0x2
+ field public static final int TIME_INTERLEAVE_MODE_128_1_1 = 4; // 0x4
+ field public static final int TIME_INTERLEAVE_MODE_128_2 = 128; // 0x80
+ field public static final int TIME_INTERLEAVE_MODE_128_3 = 256; // 0x100
+ field public static final int TIME_INTERLEAVE_MODE_128_4 = 512; // 0x200
+ field public static final int TIME_INTERLEAVE_MODE_16_8 = 32; // 0x20
+ field public static final int TIME_INTERLEAVE_MODE_32_4 = 16; // 0x10
+ field public static final int TIME_INTERLEAVE_MODE_64_2 = 8; // 0x8
+ field public static final int TIME_INTERLEAVE_MODE_8_16 = 64; // 0x40
+ field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1
+ field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0
}
public static class DvbcFrontendSettings.Builder {
@@ -5614,6 +5736,7 @@
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setOuterFec(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSpectralInversion(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSymbolRate(int);
+ method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setTimeInterleaveMode(int);
}
public class DvbsCodeRate {
@@ -5645,6 +5768,7 @@
method public int getModulation();
method public int getPilot();
method public int getRolloff();
+ method public int getScanType();
method public int getStandard();
method public int getSymbolRate();
method public int getType();
@@ -5675,6 +5799,11 @@
field public static final int ROLLOFF_0_35 = 1; // 0x1
field public static final int ROLLOFF_0_5 = 6; // 0x6
field public static final int ROLLOFF_UNDEFINED = 0; // 0x0
+ field public static final int SCAN_TYPE_DIRECT = 1; // 0x1
+ field public static final int SCAN_TYPE_DISEQC = 2; // 0x2
+ field public static final int SCAN_TYPE_JESS = 4; // 0x4
+ field public static final int SCAN_TYPE_UNDEFINED = 0; // 0x0
+ field public static final int SCAN_TYPE_UNICABLE = 3; // 0x3
field public static final int STANDARD_AUTO = 1; // 0x1
field public static final int STANDARD_S = 2; // 0x2
field public static final int STANDARD_S2 = 4; // 0x4
@@ -5692,6 +5821,7 @@
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setPilot(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setRolloff(int);
+ method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setScanType(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setStandard(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setSymbolRate(int);
method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setVcmMode(int);
@@ -5744,10 +5874,14 @@
field public static final int CODERATE_AUTO = 1; // 0x1
field public static final int CODERATE_UNDEFINED = 0; // 0x0
field public static final int CONSTELLATION_16QAM = 4; // 0x4
+ field public static final int CONSTELLATION_16QAM_R = 64; // 0x40
field public static final int CONSTELLATION_256QAM = 16; // 0x10
+ field public static final int CONSTELLATION_256QAM_R = 256; // 0x100
field public static final int CONSTELLATION_64QAM = 8; // 0x8
+ field public static final int CONSTELLATION_64QAM_R = 128; // 0x80
field public static final int CONSTELLATION_AUTO = 1; // 0x1
field public static final int CONSTELLATION_QPSK = 2; // 0x2
+ field public static final int CONSTELLATION_QPSK_R = 32; // 0x20
field public static final int CONSTELLATION_UNDEFINED = 0; // 0x0
field public static final int GUARD_INTERVAL_19_128 = 64; // 0x40
field public static final int GUARD_INTERVAL_19_256 = 128; // 0x80
@@ -5781,6 +5915,9 @@
field public static final int TRANSMISSION_MODE_4K = 8; // 0x8
field public static final int TRANSMISSION_MODE_8K = 4; // 0x4
field public static final int TRANSMISSION_MODE_AUTO = 1; // 0x1
+ field public static final int TRANSMISSION_MODE_EXTENDED_16K = 256; // 0x100
+ field public static final int TRANSMISSION_MODE_EXTENDED_32K = 512; // 0x200
+ field public static final int TRANSMISSION_MODE_EXTENDED_8K = 128; // 0x80
field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0
}
@@ -5818,8 +5955,12 @@
}
public abstract class FrontendSettings {
+ method public int getEndFrequency();
method public int getFrequency();
+ method public int getFrontendSpectralInversion();
method public abstract int getType();
+ method @IntRange(from=1) public void setEndFrequency(int);
+ method public void setSpectralInversion(int);
field public static final long FEC_11_15 = 4194304L; // 0x400000L
field public static final long FEC_11_20 = 8388608L; // 0x800000L
field public static final long FEC_11_45 = 16777216L; // 0x1000000L
@@ -5857,9 +5998,13 @@
field public static final long FEC_9_20 = 2097152L; // 0x200000L
field public static final long FEC_AUTO = 1L; // 0x1L
field public static final long FEC_UNDEFINED = 0L; // 0x0L
+ field public static final int FRONTEND_SPECTRAL_INVERSION_INVERTED = 2; // 0x2
+ field public static final int FRONTEND_SPECTRAL_INVERSION_NORMAL = 1; // 0x1
+ field public static final int FRONTEND_SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
field public static final int TYPE_ANALOG = 1; // 0x1
field public static final int TYPE_ATSC = 2; // 0x2
field public static final int TYPE_ATSC3 = 3; // 0x3
+ field public static final int TYPE_DTMB = 10; // 0xa
field public static final int TYPE_DVBC = 4; // 0x4
field public static final int TYPE_DVBS = 5; // 0x5
field public static final int TYPE_DVBT = 6; // 0x6
@@ -9886,6 +10031,25 @@
field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
}
+ public final class ModemActivityInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo);
+ method public long getIdleTimeMillis();
+ method public static int getNumTxPowerLevels();
+ method public long getReceiveTimeMillis();
+ method public long getSleepTimeMillis();
+ method public long getTimestampMillis();
+ method public long getTransmitDurationMillisAtPowerLevel(int);
+ method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
+ field public static final int TX_POWER_LEVEL_0 = 0; // 0x0
+ field public static final int TX_POWER_LEVEL_1 = 1; // 0x1
+ field public static final int TX_POWER_LEVEL_2 = 2; // 0x2
+ field public static final int TX_POWER_LEVEL_3 = 3; // 0x3
+ field public static final int TX_POWER_LEVEL_4 = 4; // 0x4
+ }
+
public final class NetworkRegistrationInfo implements android.os.Parcelable {
method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
method public int getRegistrationState();
@@ -10379,7 +10543,6 @@
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<java.lang.String> getEquivalentHomePlmns();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
@@ -10413,6 +10576,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
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);
@@ -10443,6 +10607,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -10493,6 +10658,8 @@
field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
field public static final int KEY_TYPE_EPDG = 1; // 0x1
field public static final int KEY_TYPE_WLAN = 2; // 0x2
+ field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1
+ field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2
field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
@@ -11910,7 +12077,7 @@
method @Nullable public String findProxyForUrl(@NonNull String);
method @NonNull public static android.webkit.PacProcessor getInstance();
method @Nullable public default android.net.Network getNetwork();
- method public default void releasePacProcessor();
+ method public default void release();
method public default void setNetwork(@Nullable android.net.Network);
method public boolean setProxyScript(@NonNull String);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
index 023b5b4..6d63e31 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
@@ -567,6 +567,9 @@
Log.e(TAG, "Failed to switch to new user: " + user.id);
}
}
+ if (mAddUserView != null) {
+ mAddUserView.setEnabled(true);
+ }
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
index 6d31a8d..3bfe410 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
@@ -26,19 +26,24 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.pip.Pip;
+import dagger.BindsOptionalOf;
import dagger.Module;
import dagger.Provides;
/** Provides dependencies from {@link com.android.wm.shell} for CarSystemUI. */
@Module(includes = WMShellBaseModule.class)
-public class CarWMShellModule {
+public abstract class CarWMShellModule {
@SysUISingleton
@Provides
- DisplayImeController provideDisplayImeController(Context context,
+ static DisplayImeController provideDisplayImeController(Context context,
IWindowManager wmService, DisplayController displayController,
@Main Handler mainHandler, TransactionPool transactionPool) {
return new DisplaySystemBarsController(context, wmService, displayController,
mainHandler, transactionPool);
}
+
+ @BindsOptionalOf
+ abstract Pip optionalPip();
}
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 900e68d..6827d6e 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -19,6 +19,9 @@
import static android.content.Intent.ACTION_USER_SWITCHED;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.NETWORK_PROVIDER;
+import static android.location.LocationRequest.QUALITY_LOW_POWER;
+
+import static com.android.location.provider.ProviderRequestUnbundled.INTERVAL_DISABLED;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -35,7 +38,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.ProviderRequest;
import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.LocationRequestUnbundled;
import com.android.location.provider.ProviderPropertiesUnbundled;
import com.android.location.provider.ProviderRequestUnbundled;
@@ -147,8 +149,8 @@
mRequest = new ProviderRequestUnbundled(ProviderRequest.EMPTY_REQUEST);
mWorkSource = new WorkSource();
- mGpsInterval = Long.MAX_VALUE;
- mNetworkInterval = Long.MAX_VALUE;
+ mGpsInterval = INTERVAL_DISABLED;
+ mNetworkInterval = INTERVAL_DISABLED;
}
void start() {
@@ -175,30 +177,9 @@
@GuardedBy("mLock")
private void updateRequirementsLocked() {
- long gpsInterval = Long.MAX_VALUE;
- long networkInterval = Long.MAX_VALUE;
- if (mRequest.getReportLocation()) {
- for (LocationRequestUnbundled request : mRequest.getLocationRequests()) {
- switch (request.getQuality()) {
- case LocationRequestUnbundled.ACCURACY_FINE:
- case LocationRequestUnbundled.ACCURACY_BLOCK:
- case LocationRequestUnbundled.POWER_HIGH:
- if (request.getInterval() < gpsInterval) {
- gpsInterval = request.getInterval();
- }
- if (request.getInterval() < networkInterval) {
- networkInterval = request.getInterval();
- }
- break;
- case LocationRequestUnbundled.ACCURACY_CITY:
- case LocationRequestUnbundled.POWER_LOW:
- if (request.getInterval() < networkInterval) {
- networkInterval = request.getInterval();
- }
- break;
- }
- }
- }
+ long gpsInterval = mRequest.getQuality() < QUALITY_LOW_POWER ? mRequest.getInterval()
+ : INTERVAL_DISABLED;
+ long networkInterval = mRequest.getInterval();
if (gpsInterval != mGpsInterval) {
resetProviderRequestLocked(GPS_PROVIDER, mGpsInterval, gpsInterval, mGpsListener);
@@ -214,11 +195,12 @@
@GuardedBy("mLock")
private void resetProviderRequestLocked(String provider, long oldInterval, long newInterval,
LocationListener listener) {
- if (oldInterval != Long.MAX_VALUE) {
+ if (oldInterval != INTERVAL_DISABLED && newInterval == INTERVAL_DISABLED) {
mLocationManager.removeUpdates(listener);
}
- if (newInterval != Long.MAX_VALUE) {
+ if (newInterval != INTERVAL_DISABLED) {
LocationRequest request = new LocationRequest.Builder(newInterval)
+ .setQuality(mRequest.getQuality())
.setLocationSettingsIgnored(mRequest.isLocationSettingsIgnored())
.setWorkSource(mWorkSource)
.build();
@@ -254,10 +236,10 @@
void dump(PrintWriter writer) {
synchronized (mLock) {
writer.println("request: " + mRequest);
- if (mGpsInterval != Long.MAX_VALUE) {
+ if (mGpsInterval != INTERVAL_DISABLED) {
writer.println(" gps interval: " + mGpsInterval);
}
- if (mNetworkInterval != Long.MAX_VALUE) {
+ if (mNetworkInterval != INTERVAL_DISABLED) {
writer.println(" network interval: " + mNetworkInterval);
}
if (mGpsLocation != null) {
diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
index d3aa977..61349d9 100644
--- a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
+++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
@@ -48,7 +48,6 @@
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
-import java.util.Collections;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -60,7 +59,6 @@
private static final long TIMEOUT_MS = 5000;
- private Context mContext;
private Random mRandom;
private LocationManager mLocationManager;
@@ -72,15 +70,15 @@
long seed = System.currentTimeMillis();
Log.i(TAG, "location seed: " + seed);
- mContext = InstrumentationRegistry.getTargetContext();
+ Context context = InstrumentationRegistry.getTargetContext();
mRandom = new Random(seed);
- mLocationManager = mContext.getSystemService(LocationManager.class);
+ mLocationManager = context.getSystemService(LocationManager.class);
setMockLocation(true);
mManager = new LocationProviderManagerCapture();
mProvider = ILocationProvider.Stub.asInterface(
- new FusedLocationProvider(mContext).getBinder());
+ new FusedLocationProvider(context).getBinder());
mProvider.setLocationProviderManager(mManager);
mLocationManager.addTestProvider(NETWORK_PROVIDER,
@@ -118,12 +116,9 @@
@Test
public void testNetworkRequest() throws Exception {
- LocationRequest request = new LocationRequest.Builder(1000).build();
-
mProvider.setRequest(
new ProviderRequest.Builder()
.setIntervalMillis(1000)
- .setLocationRequests(Collections.singletonList(request))
.build(),
new WorkSource());
@@ -135,14 +130,10 @@
@Test
public void testGpsRequest() throws Exception {
- LocationRequest request = new LocationRequest.Builder(1000)
- .setQuality(LocationRequest.POWER_HIGH)
- .build();
-
mProvider.setRequest(
new ProviderRequest.Builder()
+ .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
.setIntervalMillis(1000)
- .setLocationRequests(Collections.singletonList(request))
.build(),
new WorkSource());
diff --git a/packages/InputDevices/res/raw/keyboard_layout_turkish_f.kcm b/packages/InputDevices/res/raw/keyboard_layout_turkish_f.kcm
new file mode 100644
index 0000000..5b96da0
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_turkish_f.kcm
@@ -0,0 +1,366 @@
+# 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.
+
+#
+# Turkish F keyboard layout.
+#
+
+type OVERLAY
+
+map key 12 SLASH
+map key 13 MINUS
+map key 43 COMMA
+map key 51 EQUALS
+map key 52 BACKSLASH
+map key 53 PERIOD
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+ label: '+'
+ base: '+'
+ shift: '*'
+ ralt: '\u00ac'
+}
+
+key 1 {
+ label: '1'
+ base: '1'
+ shift: '!'
+ ralt: '\u00b9'
+}
+
+key 2 {
+ label: '2'
+ base: '2'
+ shift: '"'
+ ralt: '\u00b2'
+}
+
+key 3 {
+ label: '3'
+ base: '3'
+ shift: '^'
+ ralt: '#'
+}
+
+key 4 {
+ label: '4'
+ base: '4'
+ shift: '$'
+ ralt: '\u00bc'
+}
+
+key 5 {
+ label: '5'
+ base: '5'
+ shift: '%'
+ ralt: '\u00bd'
+}
+
+key 6 {
+ label: '6'
+ base: '6'
+ shift: '&'
+ ralt: '\u00be'
+}
+
+key 7 {
+ label: '7'
+ base: '7'
+ shift: '\''
+ ralt: '{'
+}
+
+key 8 {
+ label: '8'
+ base: '8'
+ shift: '('
+ ralt: '['
+}
+
+key 9 {
+ label: '9'
+ base: '9'
+ shift: ')'
+ ralt: ']'
+}
+
+key 0 {
+ label: '0'
+ base: '0'
+ shift: '='
+ ralt: '}'
+}
+
+key SLASH {
+ label: '/'
+ base: '/'
+ shift: '?'
+ ralt: '\\'
+}
+
+key MINUS {
+ label: '-'
+ base: '-'
+ shift: '_'
+ ralt: '|'
+}
+
+### ROW 2
+
+key Q {
+ label: 'F'
+ base: 'f'
+ shift, capslock: 'F'
+ ralt: '@'
+}
+
+key W {
+ label: 'G'
+ base: 'g'
+ shift, capslock: 'G'
+}
+
+key E {
+ label: '\u011f'
+ base: '\u011f'
+ shift, capslock: '\u011e'
+}
+
+key R {
+ label: '\u0131'
+ base: '\u0131'
+ shift, capslock: 'I'
+ ralt: '\u00b6'
+ ralt+shift, ralt+capslock: '\u00ae'
+}
+
+key T {
+ label: 'O'
+ base: 'o'
+ shift, capslock: 'O'
+}
+
+key Y {
+ label: 'D'
+ base: 'd'
+ shift, capslock: 'D'
+ ralt: '\u00a5'
+}
+
+key U {
+ label: 'R'
+ base: 'r'
+ shift, capslock: 'R'
+}
+
+key I {
+ label: 'N'
+ base: 'n'
+ shift, capslock: 'N'
+}
+
+key O {
+ label: 'H'
+ base: 'h'
+ shift, capslock: 'H'
+ ralt: '\u00f8'
+ ralt+shift, ralt+capslock: '\u00d8'
+}
+
+key P {
+ label: 'P'
+ base: 'p'
+ shift, capslock: 'P'
+ ralt: '\u00a3'
+}
+
+key LEFT_BRACKET {
+ label: 'Q'
+ base: 'q'
+ shift, capslock: 'Q'
+ ralt: '"'
+}
+
+key RIGHT_BRACKET {
+ label: 'W'
+ base: 'w'
+ shift, capslock: 'W'
+ ralt: '~'
+}
+
+### ROW 3
+
+key A {
+ label: '\u0075'
+ base: '\u0075'
+ shift, capslock: '\u0055'
+ ralt: '\u00e6'
+ ralt+shift, ralt+capslock: '\u00c6'
+}
+
+key S {
+ label: 'i'
+ base: 'i'
+ shift, capslock: '\u0130'
+ ralt: '\u00df'
+ ralt+shift, ralt+capslock: '\u00a7'
+}
+
+key D {
+ label: 'E'
+ base: 'e'
+ shift, capslock: 'E'
+ ralt: '\u20ac'
+}
+
+key F {
+ label: 'A'
+ base: 'a'
+ shift, capslock: 'A'
+ ralt: '\u00aa'
+}
+
+key G {
+ label: '\u00fc'
+ base: '\u00fc'
+ shift, capslock: '\u00dc'
+}
+
+key H {
+ label: 'T'
+ base: 't'
+ shift, capslock: 'T'
+ ralt: '\u20ba'
+}
+
+key J {
+ label: 'K'
+ base: 'k'
+ shift, capslock: 'K'
+}
+
+key K {
+ label: 'M'
+ base: 'm'
+ shift, capslock: 'M'
+}
+
+key L {
+ label: 'L'
+ base: 'l'
+ shift, capslock: 'L'
+}
+
+key SEMICOLON {
+ label: 'Y'
+ base: 'y'
+ shift, capslock: 'Y'
+ ralt: '\u00b4'
+}
+
+key APOSTROPHE {
+ label: '\u015f'
+ base: '\u015f'
+ shift, capslock: '\u015e'
+}
+
+key COMMA {
+ label: 'X'
+ base: 'x'
+ shift: 'X'
+ ralt: '\u0060'
+}
+
+### ROW 4
+
+key PLUS {
+ label: '<'
+ base: '<'
+ shift: '>'
+ ralt: '|'
+ ralt+shift, ralt+capslock: '\u00a6'
+}
+
+key Z {
+ label: 'J'
+ base: 'j'
+ shift, capslock: 'J'
+ ralt: '\u00ab'
+ ralt+shift, ralt+capslock: '<'
+}
+
+key X {
+ label: '\u00f6'
+ base: '\u00f6'
+ shift, capslock: '\u00d6'
+ ralt: '\u00bb'
+ ralt+shift, ralt+capslock: '>'
+}
+
+key C {
+ label: 'V'
+ base: 'v'
+ shift, capslock: 'V'
+ ralt: '\u00a2'
+ ralt+shift, ralt+capslock: '\u00a9'
+}
+
+key V {
+ label: 'C'
+ base: 'c'
+ shift, capslock: 'C'
+}
+
+key B {
+ label: '\u00e7'
+ base: '\u00e7'
+ shift, capslock: '\u00c7'
+}
+
+key N {
+ label: 'Z'
+ base: 'z'
+ shift, capslock: 'Z'
+}
+
+key M {
+ label: 'S'
+ base: 's'
+ shift, capslock: 'S'
+ ralt: '\u00b5'
+ ralt+shift, ralt+capslock: '\u00ba'
+}
+
+key EQUALS {
+ label: 'B'
+ base: 'b'
+ shift, capslock: 'B'
+ ralt: '\u00d7'
+}
+
+key BACKSLASH {
+ label: '.'
+ base: '.'
+ shift, capslock: ':'
+ ralt: '\u00f7'
+}
+
+key PERIOD {
+ label: ','
+ base: ','
+ shift: ';'
+}
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index 462a6a9..7208894 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowaaks"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloweens"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turks"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turks-F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Oekraïens"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabies"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Grieks"</string>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index 1559fa8..ff9f652 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"ስሎቫክ"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ስሎቫኒያ"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"ቱርክኛ"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ቱርክኛ F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ዩክሬን"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"አረብኛ"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ግሪክኛ"</string>
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index bf508b2..f27350a 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"السلوفاكية"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"السلوفينية"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"التركية"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"التركية-ف"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"الأوكرانية"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"العربية"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"اليونانية"</string>
diff --git a/packages/InputDevices/res/values-as/strings.xml b/packages/InputDevices/res/values-as/strings.xml
index 49fbef9..9744a7d 100644
--- a/packages/InputDevices/res/values-as/strings.xml
+++ b/packages/InputDevices/res/values-as/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"শ্ল\'ভাক"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"শ্ল\'ভেনিয়া"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"তুৰ্কী"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"টুৰ্কিছ F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ইউক্ৰেনিয়ান"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"আৰবী"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"গ্ৰীক"</string>
diff --git a/packages/InputDevices/res/values-az/strings.xml b/packages/InputDevices/res/values-az/strings.xml
index c5a1e1e..ee3a337 100644
--- a/packages/InputDevices/res/values-az/strings.xml
+++ b/packages/InputDevices/res/values-az/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloven"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türk"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türkcə F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrayna"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Ərəb"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Yunan"</string>
diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
index 16f1cb2..1fc84f3 100644
--- a/packages/InputDevices/res/values-b+sr+Latn/strings.xml
+++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovačka"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenačka"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"turska"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turska F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string>
diff --git a/packages/InputDevices/res/values-be/strings.xml b/packages/InputDevices/res/values-be/strings.xml
index 6b0523f..50c5910 100644
--- a/packages/InputDevices/res/values-be/strings.xml
+++ b/packages/InputDevices/res/values-be/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Славацкая"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Славенская"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Турэцкая"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турэцкая-F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украінская"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабская"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Грэчаская"</string>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index a7088c9..5c25f97 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"словашки"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенски"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"турски"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турски (тип F)"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"украински"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабска клавиатурна подредба"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Гръцка клавиатурна подредба"</string>
diff --git a/packages/InputDevices/res/values-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml
index f387414..a996538 100644
--- a/packages/InputDevices/res/values-bn/strings.xml
+++ b/packages/InputDevices/res/values-bn/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"স্লোভাক"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"স্লোভেনিয়ান"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"তুর্কি"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ইউক্রেনীয়"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"আরবি"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"গ্রীক"</string>
diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml
index b92ac8c..df58464 100644
--- a/packages/InputDevices/res/values-bs/strings.xml
+++ b/packages/InputDevices/res/values-bs/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovački"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenački"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"turski"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turski F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinski"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index a5b5e10..761c248 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovac"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Eslovè"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turc"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turc F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraïnès"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Àrab"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Grec"</string>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index 6b4f7ebf..3f1b3d0 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovenské"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovinské"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"turecké"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turečtina F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinské"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabština"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"řečtina"</string>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index cf2aecf..b160341 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakisk"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovensk"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tyrkisk"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tyrkisk F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainsk"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisk"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Græsk"</string>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index 1e78685..95bd806 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowakisch"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slowenisch"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türkisch"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türkisch F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainisch"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisch"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Griechisch"</string>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index eb2cc9b..1b9b42e 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Σλοβακικά"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Σλοβενικά"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Τουρκικά"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Τουρκικά F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ουκρανικά"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Αραβικά"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Ελληνικά"</string>
diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml
index f79c3da..ab48729 100644
--- a/packages/InputDevices/res/values-en-rAU/strings.xml
+++ b/packages/InputDevices/res/values-en-rAU/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml
index f79c3da..ab48729 100644
--- a/packages/InputDevices/res/values-en-rCA/strings.xml
+++ b/packages/InputDevices/res/values-en-rCA/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml
index f79c3da..ab48729 100644
--- a/packages/InputDevices/res/values-en-rGB/strings.xml
+++ b/packages/InputDevices/res/values-en-rGB/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml
index f79c3da..ab48729 100644
--- a/packages/InputDevices/res/values-en-rIN/strings.xml
+++ b/packages/InputDevices/res/values-en-rIN/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml
index d088186..92c5a5c 100644
--- a/packages/InputDevices/res/values-en-rXC/strings.xml
+++ b/packages/InputDevices/res/values-en-rXC/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index e8d6597..b9fe046 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Griego"</string>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index 9396e46..77b896b 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Griego"</string>
diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml
index cf28e9f..c835522 100644
--- a/packages/InputDevices/res/values-et/strings.xml
+++ b/packages/InputDevices/res/values-et/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovaki"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloveenia"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türgi"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türgi F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraina"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Araabia"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Kreeka"</string>
diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index 1e080fc..77d252d 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovakiarra"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveniarra"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkiarra"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkiarra (F teklatua)"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainarra"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabiarra"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Greziarra"</string>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index 5cb237e..a086060 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"اسلوواکی"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"اسلوونیایی"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"ترکی"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"اوکراینی"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"عربی"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"یونانی"</string>
diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml
index da72106..a20416f 100644
--- a/packages/InputDevices/res/values-fi/strings.xml
+++ b/packages/InputDevices/res/values-fi/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovakki"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"sloveeni"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"turkki"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turkki (F)"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukraina"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabia"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"kreikka"</string>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index 45aca35..63635824 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovaque"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovène"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turc"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turc F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainien"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabe"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Grec"</string>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index b55a3c9..0e41a2e 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovaque"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovène"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turc"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Clavier turc en F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainien"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabe"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Grec"</string>
diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml
index 40ede04..0f979d0 100644
--- a/packages/InputDevices/res/values-gl/strings.xml
+++ b/packages/InputDevices/res/values-gl/strings.xml
@@ -36,6 +36,8 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+ <!-- no translation found for keyboard_layout_turkish_f (9130320856010776018) -->
+ <skip />
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraíno"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string>
diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml
index 631fc14..99e5a45 100644
--- a/packages/InputDevices/res/values-gu/strings.xml
+++ b/packages/InputDevices/res/values-gu/strings.xml
@@ -36,6 +36,8 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"સ્લોવૅક"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"સ્લોવેનિયન"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"ટર્કીશ"</string>
+ <!-- no translation found for keyboard_layout_turkish_f (9130320856010776018) -->
+ <skip />
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"યુક્રેનિયન"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"અરબી"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ગ્રીક"</string>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 7d1e2f8..55fc5bf 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"स्लोवाक"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"स्लोवेनियाई"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"तुर्की"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"यूक्रेनियाई"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"अरबी"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ग्रीक"</string>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index aff2a37..6832437 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovačka"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenska"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"turska"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turski F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index 50d667b4..f2ac8a2 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"szlovák"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"szlovén"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"török"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"török F-billentyűzet"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrán"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"arab"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"görög"</string>
diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml
index 4a6fe2b..a6676fe 100644
--- a/packages/InputDevices/res/values-hy/strings.xml
+++ b/packages/InputDevices/res/values-hy/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Սլովակերեն"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Սլովեներեն"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Թուրքերեն"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"թուրքերեն F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ուկրաիներեն"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Արաբերեն"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Հունարեն"</string>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index 90ba97d..41bf2de 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakia"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenia"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turki"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turki F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraina"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arab"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Yunani"</string>
diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml
index 0889b21..b761a7e 100644
--- a/packages/InputDevices/res/values-is/strings.xml
+++ b/packages/InputDevices/res/values-is/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slóvaskt"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slóvenskt"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tyrkneskt"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tyrkneskt F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Úkranískt"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabískt"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Grískt"</string>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index 77f78c6..ed1ec55 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovacco"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloveno"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraino"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabo"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Greco"</string>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index 52641b2..de9b276 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"סלובקית"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"סלובנית"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"טורקית"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"טורקית F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"אוקראינית"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"ערבית"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"יוונית"</string>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index 2961548..6be0b4c 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"スロバキア語"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"スロベニア語"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"トルコ語"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"トルコ語 F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ウクライナ語"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"アラビア語"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ギリシャ語"</string>
diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml
index 2ccfeb2..92d9470 100644
--- a/packages/InputDevices/res/values-ka/strings.xml
+++ b/packages/InputDevices/res/values-ka/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"სლოვაკური"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"სლოვენური"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"თურქული"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"თურქული F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"უკრაინული"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"არაბული"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ბერძნული"</string>
diff --git a/packages/InputDevices/res/values-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml
index dfe8c56..c8ab796 100644
--- a/packages/InputDevices/res/values-kk/strings.xml
+++ b/packages/InputDevices/res/values-kk/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словак"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словен"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Түрік"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Түрік тілі, F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украин"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Араб"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Грек"</string>
diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml
index 3bd7f20..4b12321 100644
--- a/packages/InputDevices/res/values-km/strings.xml
+++ b/packages/InputDevices/res/values-km/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"ស្លូវ៉ាគី"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ស្លូវ៉ានី"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"ទួរគី"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"តួកគី F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"អ៊ុយក្រែន"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"អារ៉ាប់"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ក្រិក"</string>
diff --git a/packages/InputDevices/res/values-kn/strings.xml b/packages/InputDevices/res/values-kn/strings.xml
index 1e3c693..c305f09 100644
--- a/packages/InputDevices/res/values-kn/strings.xml
+++ b/packages/InputDevices/res/values-kn/strings.xml
@@ -36,6 +36,8 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"ಸ್ಲೋವಾಕ್"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ಸ್ಲೋವೇನಿಯನ್"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"ಟರ್ಕಿಶ್"</string>
+ <!-- no translation found for keyboard_layout_turkish_f (9130320856010776018) -->
+ <skip />
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ಉಕ್ರೇನಿಯನ್"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"ಅರೇಬಿಕ್"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ಗ್ರೀಕ್"</string>
diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml
index 1470504..2a1cbb0 100644
--- a/packages/InputDevices/res/values-ko/strings.xml
+++ b/packages/InputDevices/res/values-ko/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"슬로바키아어"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"슬로베니아어"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"터키어"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"터키어 F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"우크라이나어"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"아랍어"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"그리스어"</string>
diff --git a/packages/InputDevices/res/values-ky/strings.xml b/packages/InputDevices/res/values-ky/strings.xml
index cb9dbb2..70295e1 100644
--- a/packages/InputDevices/res/values-ky/strings.xml
+++ b/packages/InputDevices/res/values-ky/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словак"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словен"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"түркчө"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Түркчө F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украин"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабча"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Грекче"</string>
diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml
index 4ae4b7d..2b8946b 100644
--- a/packages/InputDevices/res/values-lo/strings.xml
+++ b/packages/InputDevices/res/values-lo/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"ສະໂລແວັກ"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ສະໂລເວນຽນ"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"ເຕີກິສ"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ເທືຄິຊ-F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ຢູເຄຣນຽນ"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"ອາຣັບ"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ກຣີກ"</string>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index d2aef7f..5fca46b 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakų k."</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovėnų k."</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkų k."</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkų F k."</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainiečių k."</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabų"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Graikų"</string>
diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml
index 8f3ff0a..d225329 100644
--- a/packages/InputDevices/res/values-lv/strings.xml
+++ b/packages/InputDevices/res/values-lv/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovāku"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovēņu"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turku"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turku F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraiņu"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arābu"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Grieķu"</string>
diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml
index 9584e15..d98b58b 100644
--- a/packages/InputDevices/res/values-mk/strings.xml
+++ b/packages/InputDevices/res/values-mk/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словачки"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словенечки"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Турски"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турски F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украински"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"арапски"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"грчки"</string>
diff --git a/packages/InputDevices/res/values-ml/strings.xml b/packages/InputDevices/res/values-ml/strings.xml
index 9e53443..f346881 100644
--- a/packages/InputDevices/res/values-ml/strings.xml
+++ b/packages/InputDevices/res/values-ml/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"സ്ലോവാക്"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"സ്ലോവേനിയൻ"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"ടർക്കിഷ്"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ടർക്കിഷ്-എഫ്"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ഉക്രേനിയന്"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"അറബിക്"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ഗ്രീക്ക്"</string>
diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml
index 18c2faf..fcaf321 100644
--- a/packages/InputDevices/res/values-mn/strings.xml
+++ b/packages/InputDevices/res/values-mn/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словак"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словени"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Турк"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турк F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украйн"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Араб"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Грек"</string>
diff --git a/packages/InputDevices/res/values-mr/strings.xml b/packages/InputDevices/res/values-mr/strings.xml
index d8788c9..39b2adb 100644
--- a/packages/InputDevices/res/values-mr/strings.xml
+++ b/packages/InputDevices/res/values-mr/strings.xml
@@ -36,6 +36,8 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"स्लोव्हाक"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"स्लोव्हेनियन"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"तुर्की"</string>
+ <!-- no translation found for keyboard_layout_turkish_f (9130320856010776018) -->
+ <skip />
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"यूक्रेनियन"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"अरबी"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ग्रीक"</string>
diff --git a/packages/InputDevices/res/values-ms/strings.xml b/packages/InputDevices/res/values-ms/strings.xml
index 8bc9b2a..9bff171 100644
--- a/packages/InputDevices/res/values-ms/strings.xml
+++ b/packages/InputDevices/res/values-ms/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Bahasa Slovakia"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Bahasa Slovenia"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Bahasa Turki"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turki F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Bahasa Ukraine"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Bahasa Arab"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Bahasa Greek"</string>
diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml
index 2672057..01b5507 100644
--- a/packages/InputDevices/res/values-my/strings.xml
+++ b/packages/InputDevices/res/values-my/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"စလိုဗက်"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"စလိုဗေးနီးယန်း"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"တူရကီ"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"တူရကီ F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ယူကရိန်း"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"အာရပ်"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ဂရိ"</string>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index 83b87e5..60cac3d 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakisk"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovensk"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tyrkisk"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tyrkisk F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainsk"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisk"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Gresk"</string>
diff --git a/packages/InputDevices/res/values-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml
index 4801f75..dd6366b 100644
--- a/packages/InputDevices/res/values-ne/strings.xml
+++ b/packages/InputDevices/res/values-ne/strings.xml
@@ -36,6 +36,8 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"स्लोवाक"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"स्लोवेनियाई"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"टर्किश"</string>
+ <!-- no translation found for keyboard_layout_turkish_f (9130320856010776018) -->
+ <skip />
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"युक्रेनी"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"अरबी"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ग्रीक"</string>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index 6e58490..f3a5814 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowaaks"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloveens"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turks"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turks F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Oekraïens"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisch"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Grieks"</string>
diff --git a/packages/InputDevices/res/values-or/strings.xml b/packages/InputDevices/res/values-or/strings.xml
index aa16151..52556ef 100644
--- a/packages/InputDevices/res/values-or/strings.xml
+++ b/packages/InputDevices/res/values-or/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"ସ୍ଲୋଭାକ୍"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ସ୍ଲୋଭେନିଆନ୍"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"ତୁର୍କିସ୍"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ତୁର୍କିଶ୍ F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ୟୁକ୍ରାନିଆନ୍"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"ଆରବିକ୍"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ଗ୍ରୀକ୍"</string>
diff --git a/packages/InputDevices/res/values-pa/strings.xml b/packages/InputDevices/res/values-pa/strings.xml
index 7e5a03c..916ee54 100644
--- a/packages/InputDevices/res/values-pa/strings.xml
+++ b/packages/InputDevices/res/values-pa/strings.xml
@@ -36,6 +36,8 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"ਸਲੋਵਾਕ"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ਸਲੋਵੀਅਨ"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"ਤੁਰਕੀ"</string>
+ <!-- no translation found for keyboard_layout_turkish_f (9130320856010776018) -->
+ <skip />
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ਯੂਕਰੇਨੀਅਨ"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"ਅਰਬੀ"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ਯੂਨਾਨੀ"</string>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index 51755e2..51cd29bf 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Słowacki"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Słoweński"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turecki"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turecki F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraiński"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabski"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"grecki"</string>
diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml
index 1288688..e44f4ae 100644
--- a/packages/InputDevices/res/values-pt-rBR/strings.xml
+++ b/packages/InputDevices/res/values-pt-rBR/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index 89bb3e3..3ad3e63 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index 1288688..e44f4ae 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index f7ff250..3867e1c 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovacă"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenă"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turcă"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turcă F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraineană"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabă"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Greacă"</string>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 7066813..7c5c95a 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"словацкий"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенский"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"турецкий"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"турецкий (тип F)"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"украинский"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"арабский"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"греческий"</string>
diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml
index eb3c446..f4147f3 100644
--- a/packages/InputDevices/res/values-si/strings.xml
+++ b/packages/InputDevices/res/values-si/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"ස්ලෝවැක්"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ස්ලෝවේනියානු"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"තුර්කි"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"තුර්කි F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"යුක්රේනියානු"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"අරාබි"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"ග්රීක"</string>
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index e7e15b0..301c800 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovenské"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovinské"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"turecké"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turečtina F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinské"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabčina"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Gréčtina"</string>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index f4d1e57..09b3c31 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovaška"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenska"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"turška"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turščina F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabščina"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"grščina"</string>
diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml
index 9b4fe9e..0863138 100644
--- a/packages/InputDevices/res/values-sq/strings.xml
+++ b/packages/InputDevices/res/values-sq/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"sllovakisht"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"sllovenisht"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"turqisht"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turqisht me F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrainisht"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabisht"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"greqisht"</string>
diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml
index e3a2043..973b833 100644
--- a/packages/InputDevices/res/values-sr/strings.xml
+++ b/packages/InputDevices/res/values-sr/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"словачка"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словеначка"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"турска"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"турска F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"украјинска"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"арапски"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"грчки"</string>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index 097ada4..a08a74b 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakiskt"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenskt"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkiskt"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turkiska, F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainskt"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabiska"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Grekiska"</string>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index 3257962..0f4c846 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Kislovakia"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Kislovenia"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Kituruki"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Kituruki F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Kiukrania"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Kiarabu"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Kigiriki"</string>
diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml
index d3c6000..f596b40 100644
--- a/packages/InputDevices/res/values-ta/strings.xml
+++ b/packages/InputDevices/res/values-ta/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"ஸ்லோவாக்"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ஸ்லோவேனியன்"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"டர்கிஷ்"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"டர்கிஷ் F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"உக்ரைனியன்"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"அரபிக்"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"கிரேக்கம்"</string>
diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml
index c0253e5..5a10f6e 100644
--- a/packages/InputDevices/res/values-te/strings.xml
+++ b/packages/InputDevices/res/values-te/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"స్లోవక్"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"స్లోవేనియన్"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"టర్కిష్"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"టర్కిష్ ఎఫ్"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ఉక్రెయినియన్"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"అరబిక్"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"గ్రీక్"</string>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index c8e0e62..5f60c3d 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"สโลวัก"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"สโลวีเนีย"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"ตุรกี"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ภาษาตุรกี F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ยูเครน"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"ภาษาอารบิค"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"กรีก"</string>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index b9aee76..c42adbc 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index f093abb..2877cb7 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakça"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovence"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türkçe"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türkçe F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraynaca"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arapça"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Yunanca"</string>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index d9b58d2..3b0de34 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"словацька"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенська"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"турецька"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турецька-F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"українська"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабська"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Грецька"</string>
diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml
index 2bff7ed..0cc9b61 100644
--- a/packages/InputDevices/res/values-ur/strings.xml
+++ b/packages/InputDevices/res/values-ur/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"سلوووک"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"سلووینیائی"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"ترکش"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ترکی-F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"يُوکرينی"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"عربی"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"يونانی"</string>
diff --git a/packages/InputDevices/res/values-uz/strings.xml b/packages/InputDevices/res/values-uz/strings.xml
index 9245aeb..161bd0d 100644
--- a/packages/InputDevices/res/values-uz/strings.xml
+++ b/packages/InputDevices/res/values-uz/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakcha"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovyancha"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkcha"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkcha F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraincha"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arab"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Grek"</string>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index 76fd0bf..0c638fa 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Tiếng Slovak"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Tiếng Sloven"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tiếng Thổ Nhĩ Kỳ"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tiếng Thổ Nhĩ Kỳ F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Tiếng Ukraina"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Tiếng Ả rập"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Tiếng Hy Lạp"</string>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index aa75605..b249779 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"斯洛伐克语"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"斯洛文尼亚语"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"土耳其语"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"土耳其语 F 型键盘"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"乌克兰语"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"阿拉伯语"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"希腊语"</string>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index dc824b9..60a52e9 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"斯洛伐克文"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"斯洛文尼亞文"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"土耳其文"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"土耳其文 F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"烏克蘭文"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"阿拉伯文"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"希臘文"</string>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index c2714da..c3217e3 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"斯洛伐克文"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"斯洛維尼亞文"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"土耳其文"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"土耳其文 F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"烏克蘭文"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"阿拉伯文"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"希臘文"</string>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index 3af1da1..2c53626 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -36,6 +36,7 @@
<string name="keyboard_layout_slovak" msgid="2469379934672837296">"Isi-Slovak"</string>
<string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Isi-Slovenian"</string>
<string name="keyboard_layout_turkish" msgid="7736163250907964898">"Isi-Turkish"</string>
+ <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"I-Turkish-F"</string>
<string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Isi-Ukrainian"</string>
<string name="keyboard_layout_arabic" msgid="5671970465174968712">"Isi-Arabic"</string>
<string name="keyboard_layout_greek" msgid="7289253560162386040">"Isi-Greek"</string>
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 9d068fe..1e13940 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -105,6 +105,9 @@
<!-- Turkish keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_turkish">Turkish</string>
+ <!-- Turkish keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_turkish_f">Turkish F</string>
+
<!-- Ukrainian keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_ukrainian">Ukrainian</string>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 6b78b68..976a279 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -132,6 +132,10 @@
android:label="@string/keyboard_layout_turkish"
android:keyboardLayout="@raw/keyboard_layout_turkish" />
+ <keyboard-layout android:name="keyboard_layout_turkish_f"
+ android:label="@string/keyboard_layout_turkish_f"
+ android:keyboardLayout="@raw/keyboard_layout_turkish_f" />
+
<keyboard-layout android:name="keyboard_layout_ukrainian"
android:label="@string/keyboard_layout_ukrainian"
android:keyboardLayout="@raw/keyboard_layout_ukrainian" />
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index 8e140ca..83974af 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -254,7 +254,7 @@
mLocationManager.requestLocationUpdates(
LocationManager.FUSED_PROVIDER,
new LocationRequest.Builder(LOCATION_UPDATE_MS)
- .setQuality(LocationRequest.POWER_LOW)
+ .setQuality(LocationRequest.QUALITY_LOW_POWER)
.build(),
new HandlerExecutor(new Handler(Looper.getMainLooper())),
this);
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 2f97f27..e2b04d4 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Voordat jy \'n beperkte profiel kan skep, moet jy \'n skermslot opstel om jou programme en persoonlike data te beskerm."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Stel slot op"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Skakel oor na <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Skep tans nuwe gebruiker …"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Bynaam"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gas"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Kies foto"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Toestelverstek"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Gedeaktiveer"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Geaktiveer"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index f0d8a40..0e3684e 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"የተገደበ መገለጫ መፍጠር ከመቻልዎ በፊት መተግበሪያዎችዎን እና የግል ውሂብዎን ለመጠበቅ ቁልፍ ማያ ገጽ ማዋቀር አለብዎት።"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"ቁልፍ አዘጋጅ"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"ወደ <xliff:g id="USER_NAME">%s</xliff:g> ቀይር"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"አዲስ ተጠቃሚ በመፍጠር ላይ…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"ቅጽል ስም"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"እንግዳ"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ፎቶ አንሳ"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"ምስል ይምረጡ"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"ፎቶ ይምረጡ"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"የመሣሪያ ነባሪ"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ተሰናክሏል"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ነቅቷል"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 323ba44..7ec46d2 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -549,19 +549,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"قبل أن تتمكن من إنشاء ملف شخصي مقيد، يلزمك إعداد تأمين للشاشة لحماية تطبيقاتك وبياناتك الشخصية."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"تعيين التأمين"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"التبديل إلى <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"جارٍ إنشاء مستخدم جديد…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"اللقب"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"إضافة ضيف"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"إزالة جلسة الضيف"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ضيف"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"التقاط صورة"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"اختيار صورة"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"اختيار صورة"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"الإعداد التلقائي للجهاز"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غير مفعّل"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"مفعّل"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 7679c64..fe04094 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"আপুনি সীমিত প্ৰ\'ফাইল এটা সৃষ্টি কৰাৰ আগেয়ে, আপোনাৰ ব্যক্তিগত ডেটা আৰু এপবিলাকক সুৰক্ষিত কৰিবলৈ স্ক্ৰীণ লক এটা নিৰ্ধাৰণ কৰিব লাগিব।"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"লক ছেট কৰক"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>লৈ সলনি কৰক"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"নতুন ব্যৱহাৰকাৰী সৃষ্টি কৰি থকা হৈছে…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"উপনাম"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ কৰক"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string>
<string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"এখন ফট’ তোলক"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"ফট’ বাছনি কৰক"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ডিভাইচ ডিফ’ল্ট"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"অক্ষম কৰা আছে"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"সক্ষম কৰা আছে"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index d0bf0ed..bd10cfb 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Məhdudlaşdırılmış profil yaratmadan öncə, Siz tətbiqlərinizi və şəxsi datanızı qorumaq üçün ekran kilidi quraşdırmalısınız."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Kilid ayarlayın"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> adlı istifadəçiyə keçin"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yeni istifadəçi yaradılır…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Ləqəb"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Qonaq"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Foto seçin"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Cihaz defoltu"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiv"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 3beb9b8..0deb927 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -546,19 +546,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Da biste mogli da napravite ograničeni profil, treba da podesite zaključavanje ekrana da biste zaštitili aplikacije i lične podatke."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Podesi zaključavanje"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Pređi na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Pravi se novi korisnik…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Izaberite sliku"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Podrazumevano za uređaj"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index af6da4e..9aad825 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -547,19 +547,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Перш чым вы зможаце стварыць профіль з абмежаваннямi, вам трэба наладзіць блакiроўку экрана для абароны сваiх дадаткаў і асабістай інфармацыі."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Усталёўка блакiроўкi"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Пераключыцца на карыстальніка <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ствараецца новы карыстальнік…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Псеўданім"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Дадаць госця"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Выдаліць госця"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Госць"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Зрабіць фота"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбраць відарыс"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Выбраць фота"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандартная прылада"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Выключана"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Уключана"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 0097c9c..8bf13fe 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Преди да можете да създадете потребителски профил с ограничена функционалност, трябва да настроите заключения екран, за да защитите приложенията и личните си данни."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Задаване на заключване"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Превключване към <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Създава се нов потребител…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Правене на снимка"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Избиране на изображение"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Избиране на снимката"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандартна настройка за у-вото"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Деактивирано"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Активирано"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index c351542..8bcf9a6 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"আপনি একটি সীমাবদ্ধযুক্ত প্রোফাইল তৈরি করার আগে, আপনাকে আপনার অ্যাপ্লিকেশন এবং ব্যক্তিগত ডেটা সুরক্ষিত করার জন্য একটি স্ক্রিন লক সেট-আপ করতে হবে।"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"লক সেট করুন"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>-এ পাল্টান"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"নতুন ব্যবহারকারী তৈরি করা হচ্ছে…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"বিশেষ নাম"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string>
<string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ফটো তুলুন"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"একটি ইমেজ বেছে নিন"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"ফটো বেছে নিন"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ডিভাইসের ডিফল্ট"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"বন্ধ করা আছে"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"চালু করা আছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml
index 775cd15..6d2f1f3 100644
--- a/packages/SettingsLib/res/values-bs/arrays.xml
+++ b/packages/SettingsLib/res/values-bs/arrays.xml
@@ -27,7 +27,7 @@
<item msgid="8356618438494652335">"Autentifikacija…"</item>
<item msgid="2837871868181677206">"Dobivanje IP adrese…"</item>
<item msgid="4613015005934755724">"Povezano"</item>
- <item msgid="3763530049995655072">"Suspendirano"</item>
+ <item msgid="3763530049995655072">"Obustavljeno"</item>
<item msgid="7852381437933824454">"Prekidanje veze…"</item>
<item msgid="5046795712175415059">"Isključen"</item>
<item msgid="2473654476624070462">"Neuspješno"</item>
@@ -41,7 +41,7 @@
<item msgid="3028983857109369308">"Autentifikacija s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
<item msgid="4287401332778341890">"Dobivanje IP adrese iz mreže <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
<item msgid="1043944043827424501">"Povezano s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
- <item msgid="7445993821842009653">"Suspendirano"</item>
+ <item msgid="7445993821842009653">"Obustavljeno"</item>
<item msgid="1175040558087735707">"Prekidanje veze s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
<item msgid="699832486578171722">"Isključen"</item>
<item msgid="522383512264986901">"Neuspješno"</item>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 3d358730..32f6aa7 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -546,19 +546,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Prije nego vam se omogući kreiranje ograničenog profila, morate postaviti zaključavanje ekrana da biste zaštitili svoje aplikacije i lične podatke."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Postaviti zaključavanje"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Prebaci na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Kreiranje novog korisnika…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Odabir fotografije"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Zadana postavka uređaja"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index b1edd78..525a187 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Per crear un perfil restringit, has de configurar una pantalla de bloqueig per protegir les aplicacions i les dades personals."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Defineix un bloqueig"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Canvia a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"S\'està creant l\'usuari…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Àlies"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidat"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Selecciona una foto"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Opció predeter. del dispositiu"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivat"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index bdce444c..64ad700 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -547,19 +547,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Před vytvořením omezeného profilu je nutné nejprve nastavit zámek obrazovky k ochraně aplikací a dat."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Nastavit zámek"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Přepnout na uživatele <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Vytváření nového uživatele…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Přezdívka"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Host"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Vybrat fotku"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Výchozí nastavení zařízení"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuto"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuto"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 1dc0e4e..433368b 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Før du kan oprette en begrænset profil, skal du oprette en skærmlås for at beskytte dine apps og personlige data."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Konfigurer låseskærmen"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Skift til <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Opretter ny bruger…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæsten"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Vælg et billede"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Vælg billede"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Enhedens standardindstilling"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiveret"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiveret"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index a9cdf1f..9946eed 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Vor dem Erstellen eines eingeschränkten Profils musst du eine Displaysperre einrichten, um deine Apps und personenbezogenen Daten zu schützen."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Sperre einrichten"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Zu <xliff:g id="USER_NAME">%s</xliff:g> wechseln"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Neuer Nutzer wird erstellt…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Alias"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Foto machen"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Bild auswählen"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Foto auswählen"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Gerätestandard"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiviert"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiviert"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 00b8434..c265dbc 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Προκειμένου να μπορέσετε να δημιουργήσετε ένα περιορισμένο προφίλ, θα πρέπει να δημιουργήσετε ένα κλείδωμα οθόνης για την προστασία των εφαρμογών και των προσωπικών δεδομένων σας."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Ορισμός κλειδώματος"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Εναλλαγή σε <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Δημιουργία νέου χρήστη…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Ψευδώνυμο"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Επισκέπτης"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Λήψη φωτογραφίας"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Επιλογή εικόνας"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Επιλογή φωτογραφίας"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Προεπιλογή συσκευής"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ανενεργή"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ενεργή"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 4bb2d8a..285c615 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 05c12d6..bbd5c76 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 4bb2d8a..285c615 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 4bb2d8a..285c615 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 1dc5f5b..ab421f6 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar un bloqueo de pantalla que proteja tus aplicaciones y datos personales."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Configurar bloqueo"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario nuevo…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Sobrenombre"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Tomar una foto"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Elegir una imagen"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predeterminado del dispositivo"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 977b469..f6157b4 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar una pantalla de bloqueo que proteja tus aplicaciones y datos personales."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Establecer bloqueo"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Apodo"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predeterminado por el dispositivo"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 3c2593a..72766c0 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Enne piiratud profiili loomist peate seadistama lukustusekraani, et oma rakendusi ja isiklikke andmeid kaitsta."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Määra lukk"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Lülita kasutajale <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Uue kasutaja loomine …"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Hüüdnimi"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Külaline"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Valige foto"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Seadme vaikeseade"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Keelatud"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Lubatud"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 33ce3c7..c7462ce 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Profil murriztua sortu aurretik, aplikazioak eta datu pertsonalak babesteko, pantaila blokeatzeko metodo bat konfiguratu beharko duzu."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Ezarri blokeoa"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Aldatu <xliff:g id="USER_NAME">%s</xliff:g> erabiltzailera"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Beste erabiltzaile bat sortzen…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Goitizena"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatua"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gonbidatua"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Hautatu argazki bat"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Gailuaren balio lehenetsia"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desgaituta"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Gaituta"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 0453788..c5a4f2f 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"قبل از ایجاد یک نمایه محدود، باید یک قفل صفحه را برای محافظت از برنامهها و دادههای شخصی خود تنظیم کنید."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"تنظیم قفل"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"رفتن به <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"درحال ایجاد کاربر جدید…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"نام مستعار"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"افزودن مهمان"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"حذف مهمان"</string>
<string name="guest_nickname" msgid="6332276931583337261">"مهمان"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"عکس گرفتن"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"انتخاب تصویر"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"انتخاب عکس"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"پیشفرض دستگاه"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غیرفعال"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 7f85572..dd1c12a 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Ennen kuin voit luoda rajoitetun profiilin, määritä näytön lukitus, joka suojelee sovelluksiasi ja henkilökohtaisia tietojasi."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Aseta lukitus"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Vaihda tähän käyttäjään: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Luodaan uutta käyttäjää…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Lempinimi"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Ota valokuva"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Valitse valokuva"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Laitteen oletusasetus"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ei käytössä"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Käytössä"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 1745e02..f0364c6 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Avant de créer un profil limité, vous devez définir un écran de verrouillage pour protéger vos applications et vos données personnelles."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Définir verrouillage écran"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Passer à <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Créer un utilisateur…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Sélectionner une image"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionnez une photo"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Valeur par défaut de l\'appareil"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 8c753ae..17d13c1 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Avant de créer un profil limité, vous devez définir un écran de verrouillage pour protéger vos applications et vos données personnelles."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Définir verrouillage écran"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Passer à <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Création d\'un nouvel utilisateur…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Choisir une image"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionner une photo"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Paramètre par défaut"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 414564b..e0b21c3 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restrinxido, precisarás configurar un bloqueo da pantalla para protexer as túas aplicacións e datos persoais."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Establecer bloqueo"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario novo…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Alcume"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Funcionamento predeterminado"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivado"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activado"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 634870c..08a2b99 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"તમે પ્રતિબંધિત પ્રોફાઇલ બનાવી શકો તે પહેલાં, તમારે તમારી ઍપ્લિકેશનો અને વ્યક્તિગત ડેટાની સુરક્ષા માટે એક લૉક સ્ક્રીન સેટ કરવાની જરૂર પડશે."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"લૉક સેટ કરો"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> પર સ્વિચ કરો"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"નવા વપરાશકર્તા બનાવી રહ્યાં છીએ…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"ઉપનામ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"અતિથિ ઉમેરો"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"અતિથિને કાઢી નાખો"</string>
<string name="guest_nickname" msgid="6332276931583337261">"અતિથિ"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ફોટો લો"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"છબી પસંદ કરો"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"ફોટો પસંદ કરો"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ડિવાઇસ ડિફૉલ્ટ"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"બંધ છે"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ચાલુ છે"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index bd052c0..8486e34 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"इससे पहले कि आप कोई प्रतिबंधित प्रोफ़ाइल बनाएं, आपको अपने ऐप्लिकेशन और व्यक्तिगत डेटा की सुरक्षा करने के लिए एक स्क्रीन लॉक सेट करना होगा."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"लॉक सेट करें"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> पर जाएं"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नया उपयोगकर्ता बनाया जा रहा है…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"प्रचलित नाम"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान हटाएं"</string>
<string name="guest_nickname" msgid="6332276931583337261">"मेहमान"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"फ़ोटो खींचें"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"कोई इमेज चुनें"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"फ़ोटो चुनें"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"डिवाइस की डिफ़ॉल्ट सेटिंग"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"बंद है"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"चालू है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 3cff480..a4ef323 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -546,19 +546,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Prije izrade ograničenog profila trebate postaviti zaključavanje zaslona radi zaštite svojih aplikacija i osobnih podataka."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Postavi zaključavanje"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Prelazak na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Izrada novog korisnika…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Uklanjanje gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Odabir slike"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Zadana postavka uređaja"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 854265c..480552b 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Mielőtt létrehozhatna egy korlátozott profilt, be kell állítania egy képernyőzárat, hogy megvédje alkalmazásait és személyes adatait."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Képernyőzár beállítása"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Váltás erre: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Új felhasználó létrehozása…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Becenév"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Vendég"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Fotó készítése"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Kép kiválasztása"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Fotó kiválasztása"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Alapértelmezett"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Letiltva"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Engedélyezve"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 35b218e..9fbf998 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Նախքան դուք կկարողանաք ստեղծել սահմանափակ պրոֆիլ, դուք պետք է կարգավորեք էկրանի կողպումը` ձեր ծրագրերը և անձնական տվյալները պաշտպանելու համար:"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Կարգավորել կողպումը"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Անցնել <xliff:g id="USER_NAME">%s</xliff:g> պրոֆիլին"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ստեղծվում է օգտատիրոջ նոր պրոֆիլ…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Կեղծանուն"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ավելացնել հյուր"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Հեռացնել հյուրին"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Հյուր"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Լուսանկարել"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Ընտրել պատկեր"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Ընտրեք լուսանկար"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Կանխադրված տարբերակ"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Անջատված է"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Միացված է"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index cf6455f..b1391ec 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Sebelum dapat membuat profil yang dibatasi, Anda perlu menyiapkan kunci layar untuk melindungi aplikasi dan data pribadi Anda."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Setel kunci"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Beralih ke <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Membuat pengguna baru …"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Tamu"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih gambar"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Default perangkat"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Nonaktif"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktif"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index b4be963..1636c55 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Áður en þú getur búið til takmarkað snið þarftu að setja upp skjálás til að vernda forritin þín og persónuleg gögn."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Velja lás"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Skipta yfir í <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Stofnar nýjan notanda…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Gælunafn"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gestur"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Velja mynd"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Sjálfgefin stilling tækis"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slökkt"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Virkt"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 69b1b5e..b358d65 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Prima di poter creare un profilo con limitazioni, devi impostare un blocco schermo per proteggere le tue app e i tuoi dati personali."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Imposta blocco"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Passa a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creazione nuovo utente…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Ospite"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Seleziona la foto"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Parametro predefinito"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Non attivo"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Attivo"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 158872d..0f2eb4b 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -547,19 +547,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"לפני שתוכל ליצור פרופיל מוגבל, תצטרך להגדיר נעילת מסך כדי להגן על האפליקציות ועל הנתונים האישיים שלך."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"הגדרת נעילה"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"מעבר אל <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"בתהליך יצירה של משתמש חדש…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"כינוי"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"הוספת אורח"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"הסרת אורח"</string>
<string name="guest_nickname" msgid="6332276931583337261">"אורח"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"צילום תמונה"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"לבחירת תמונה"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"בחירת תמונה"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ברירת המחדל של המכשיר"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"מושבת"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"מופעל"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 13fd53f..dba1c95 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"制限付きプロファイルを作成する場合は、アプリや個人データを保護するように画面ロックを設定しておく必要があります。"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"ロックを設定"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> に切り替え"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"新しいユーザーを作成しています…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"ニックネーム"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ゲストを追加"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ゲストを削除"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ゲスト"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"写真を撮る"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"画像を選択"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"写真を選択"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"デバイスのデフォルト"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"無効"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"有効"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index f411d00..ffb5c9a 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Шектелген профайл жасақтауға дейін қолданбалар мен жеке деректерді қорғау үшін экран бекітпесін тағайындау қажет."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Бекітпе тағайындау"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> пайдаланушысына ауысу"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Жаңа пайдаланушы профилі жасалуда…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Қонақты енгізу"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты өшіру"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Фотосурет таңдау"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Құрылғыны әдепкісінше реттеу"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Өшірулі"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Қосулы"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 9b7cc4c..6fbd2d1 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"មុនពេលអ្នកអាចបង្កើតប្រវត្តិរូបបានដាក់កម្រិត អ្នកត្រូវរៀបចំការចាក់សោអេក្រង់ ដើម្បីការពារកម្មវិធី និងទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នក។"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"កំណត់ការចាក់សោ"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"ប្ដូរទៅ <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"កំពុងបង្កើតអ្នកប្រើប្រាស់ថ្មី…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"ឈ្មោះហៅក្រៅ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូលភ្ញៀវ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"លុបភ្ញៀវ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើសរូបភាព"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"ជ្រើសរើសរូបថត"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"លំនាំដើមរបស់ឧបករណ៍"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"បានបិទ"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"បានបើក"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 1f74d9b6..653d8ba 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"ನೀವು ನಿರ್ಬಂಧಿತ ಪ್ರೊಫೈಲ್ ಅನ್ನು ರಚಿಸಬಹುದಾದರ ಮೊದಲು, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು ನೀವು ಪರದೆಯ ಲಾಕ್ ಹೊಂದಿಸುವ ಅಗತ್ಯವಿದೆ."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಹೊಂದಿಸಿ"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> ಗೆ ಬದಲಿಸಿ"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"ಅಡ್ಡ ಹೆಸರು"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ಅತಿಥಿ"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ಫೋಟೋ ತೆಗೆದುಕೊಳ್ಳಿ"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"ಚಿತ್ರವನ್ನು ಆರಿಸಿ"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"ಫೋಟೋ ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ಸಾಧನದ ಡೀಫಾಲ್ಟ್"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index d130834..761f813 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"제한된 프로필을 만들기 전에 화면 잠금을 설정하여 앱과 개인 데이터를 보호해야 합니다."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"잠금 설정"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>(으)로 전환"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"새로운 사용자를 만드는 중…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"닉네임"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"게스트 추가"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"게스트 삭제"</string>
<string name="guest_nickname" msgid="6332276931583337261">"게스트"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"사진 찍기"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"이미지 선택"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"사진 선택"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"기기 기본값"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"사용 중지됨"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"사용 설정됨"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 5672913..c72b93f 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Чектелген профайл түзөөрдөн мурун, сиз өзүңүздүн колдонмолоруңузду жана жеке маалыматтарыңызды коргош үчүн, бөгөттөө көшөгөсүн орнотушуңуз керек болот."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Бөгөт коюу"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> аккаунтуна которулуу"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Жаңы колдонуучу түзүлүүдө…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Ылакап аты"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Конок кошуу"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Конокту өчүрүү"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Конок"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Сүрөткө тартуу"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Сүрөт тандаңыз"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Сүрөт тандаңыз"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Түзмөктүн демейки параметри"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Өчүк"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Күйүк"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index b3b3c81..7883308 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"ກ່ອນທ່ານຈະສ້າງໂປຣໄຟລ໌ທີ່ຖືກຈຳກັດນັ້ນ, ທ່ານຈະຕ້ອງຕັ້ງຄ່າການລັອກໜ້າຈໍ ເພື່ອປ້ອງກັນແອັບຯ ແລະຂໍ້ມູນສ່ວນໂຕຂອງທ່ານກ່ອນ."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"ຕັ້ງການລັອກ"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"ສະຫຼັບໄປ <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ກຳລັງສ້າງຜູ້ໃຊ້ໃໝ່…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"ຊື່ຫຼິ້ນ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ເພີ່ມແຂກ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ລຶບແຂກອອກ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ແຂກ"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ຖ່າຍຮູບ"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"ເລືອກຮູບ"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"ເລືອກຮູບ"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ຄ່າເລີ່ມຕົ້ນອຸປະກອນ"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ປິດການນຳໃຊ້ແລ້ວ"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ເປີດການນຳໃຊ້ແລ້ວ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 2e5385e..e3aa368 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -547,19 +547,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Prieš kuriant apribotą profilį reikės nustatyti ekrano užraktą, kad apsaugotumėte programas ir asmeninius duomenis."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Nustatyti užraktą"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Perjungti į <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Kuriamas naujas naudotojas…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Slapyvardis"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Svečias"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Pasirinkti nuotrauką"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Numatyt. įrenginio nustatymas"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Išjungta"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Įgalinta"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index f4fe8e3..e994974 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -546,19 +546,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Lai varētu izveidot ierobežotu profilu, jums jāiestata ekrāna bloķēšana, kas aizsargās jūsu lietotni un personas datus."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Iestatīt bloķēšanu"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Pārslēgties uz: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Notiek jauna lietotāja izveide…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Segvārds"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Viesis"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Uzņemt fotoattēlu"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Izvēlēties attēlu"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Atlasīt fotoattēlu"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Ierīces noklusējums"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Atspējots"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Iespējots"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 1e933fb..e711747 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Пред да може да создадете ограничен профил, треба да поставите заклучување на екранот за да ги заштити вашите апликации и лични податоци."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Постави заклучување"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Префрли на <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Се создава нов корисник…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Прекар"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Додај гостин"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Отстрани гостин"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гостин"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Фотографирајте"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Одберете слика"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Изберете фотографија"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандардно за уредот"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Оневозможено"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Овозможено"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 970f778..cebd552 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"ഒരു നിയന്ത്രിത പ്രൊഫൈൽ സൃഷ്ടിക്കുന്നതിനുമുമ്പ്, നിങ്ങളുടെ അപ്ലിക്കേഷനുകളും വ്യക്തിഗത ഡാറ്റയും പരിരക്ഷിക്കുന്നതിന് ഒരു സ്ക്രീൻ ലോക്ക് സജ്ജീകരിക്കേണ്ടതുണ്ട്."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"ലോക്ക് സജ്ജീകരിക്കുക"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> എന്നതിലേക്ക് മാറുക"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"പുതിയ ഉപയോക്താവിനെ സൃഷ്ടിക്കുന്നു…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"വിളിപ്പേര്"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string>
<string name="guest_nickname" msgid="6332276931583337261">"അതിഥി"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ഒരു ഫോട്ടോ എടുക്കുക"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"ഫോട്ടോ തിരഞ്ഞെടുക്കുക"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ഉപകരണത്തിന്റെ ഡിഫോൾട്ട് പ്രവർത്തനം"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"പ്രവർത്തനക്ഷമമാക്കി"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 3dc4989..8074366 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Та хязгаарлагдсан профайл үүсгэхийн өмнө өөрийн апп-ууд болон хувийн өгөгдлийг хамгаалахын тулд дэлгэцийн түгжээг тохируулах шаардлагатай."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Түгжээг тохируулах"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> руу сэлгэх"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Шинэ хэрэглэгч үүсгэж байна…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Хоч"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Зочин"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Зураг авах"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Зураг сонгох"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Зураг сонгох"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Төхөөрөмжийн өгөгдмөл"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Идэвхгүй болгосон"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Идэвхжүүлсэн"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 7e418a4..fbfe84c 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"तुम्ही एक प्रतिबंधित प्रोफाईल तयार करु शकण्यापूर्वी तुम्हाला तुमचे अॅप्स आणि वैयक्तिक डेटा संरक्षित करण्यासाठी एक स्क्रीन लॉक सेट करण्याची आवश्यकता राहील."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"लॉक सेट करा"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> वर स्विच करा"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नवीन वापरकर्ता तयार करत आहे…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"टोपणनाव"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string>
<string name="guest_nickname" msgid="6332276931583337261">"अतिथी"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"फोटो काढा"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"इमेज निवडा"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"फोटो निवडा"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"डिव्हाइस डीफॉल्ट"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"बंद केले आहे"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सुरू केले आहे"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 4c23a84..d988dd0 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Sebelum anda boleh membuat profil yang terhad, anda perlu menyediakan kunci skrin untuk melindungi apl dan data peribadi anda."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Tetapkan kunci"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Tukar kepada <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Mencipta pengguna baharu…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Tetamu"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih imej"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Lalai peranti"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Dilumpuhkan"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Didayakan"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index aa3cf08..5a16743 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"ကန့်သတ်ကိုယ်ရေးအချက်အလက်တစ်ခုကို မပြုလုပ်မီ သင်၏ အပလီကေးရှင်းများနှင့် ကိုယ်ပိုင်အချက်အလက်များကို ကာကွယ်ရန် မျက်နှာပြင်သော့ချခြင်းကို စီမံရန် လိုအပ်လိမ့်မည်"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"သော့ချရန် သတ်မှတ်ပါ"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> သို့ ပြောင်းရန်"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"အသုံးပြုသူအသစ် ပြုလုပ်နေသည်…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"နာမည်ပြောင်"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည့် ထည့်ရန်"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ထုတ်ရန်"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ဧည့်သည်"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ဓာတ်ပုံရိုက်ရန်"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"ပုံရွေးရန်"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"ဓာတ်ပုံရွေးရန်"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"စက်ပစ္စည်းမူရင်း"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ပိတ်ထားသည်"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ဖွင့်ထားသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 0c5431b..df96ea6 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Før du kan opprette en begrenset profil, må du konfigurere skjermlåsen for å beskytte appene og de personlige dataene dine."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Angi lås"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Bytt til <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Oppretter en ny bruker …"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Kallenavn"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Legg til en gjest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gjest"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Velg et bilde"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Standard for enheten"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slått av"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Slått på"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 4c230d3..54adb3a 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"निषेधयुक्त प्रोफाइल बनाउनु अघि तपाईँको एप र व्यक्तिगत डेटा सुरक्षा गर्नाका लागि तपाईँले स्क्रिन लक सेटअप गर्नु पर्दछ ।"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"लक सेट गर्नुहोस्"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"प्रयोगकर्ता बदलेर <xliff:g id="USER_NAME">%s</xliff:g> पार्नुहोस्"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नयाँ प्रयोगकर्ता बनाउँदै…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"उपनाम"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"अतिथि थप्नुहोस्"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"अतिथि हटाउनुहोस्"</string>
<string name="guest_nickname" msgid="6332276931583337261">"अतिथि"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"फोटो खिच्नुहोस्"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"कुनै फोटो छनौट गर्नुहोस्"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"फोटो चयन गर्नुहोस्"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"पूर्वनिर्धारित यन्त्र"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"असक्षम पारिएको छ"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सक्षम पारिएको छ"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index ae734a5..729d57b 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Voordat je een beperkt profiel kunt maken, moet je een schermvergrendeling instellen om je apps en persoonsgegevens te beschermen."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Vergrendeling instellen"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Overschakelen naar <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Nieuwe gebruiker maken…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Bijnaam"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Foto selecteren"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Apparaatstandaard"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Uitgeschakeld"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ingeschakeld"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 18a88fa..1ce7d56e 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"ପ୍ରତିବନ୍ଧିତ ପ୍ରୋଫାଇଲ୍ ତିଆରି କରିବାବେଳେ, ନିଜ ଆପ୍ ଓ ବ୍ୟକ୍ତିଗତ ତଥ୍ୟର ସୁରକ୍ଷା ପାଇଁ ଏକ ସ୍କ୍ରୀନ୍ ଲକ୍ ସେଟ୍ କରନ୍ତୁ।"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"ଲକ୍ ସେଟ୍ କରନ୍ତୁ"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>କୁ ସ୍ୱିଚ୍ କରନ୍ତୁ"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରାଯାଉଛି…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"ଡାକନାମ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ଅତିଥି"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ଗୋଟିଏ ଫଟୋ ଉଠାନ୍ତୁ"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"ଏକ ଛବି ବାଛନ୍ତୁ"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"ଫଟୋ ବାଛନ୍ତୁ"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ଡିଭାଇସ୍ ଡିଫଲ୍ଟ"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ଅକ୍ଷମ କରାଯାଇଛି"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ସକ୍ଷମ କରାଯାଇଛି"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index b1bde27..c122a28 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"ਇਸਤੋਂ ਪਹਿਲਾਂ ਕਿ ਤੁਸੀਂ ਇੱਕ ਪ੍ਰਤਿਬੰਧਿਤ ਪ੍ਰੋਫਾਈਲ ਬਣਾ ਸਕੋ, ਤੁਹਾਨੂੰ ਆਪਣੀਆਂ ਐਪਾਂ ਅਤੇ ਨਿੱਜੀ ਡਾਟਾ ਸੁਰੱਖਿਅਤ ਕਰਨ ਲਈ ਇੱਕ ਸਕ੍ਰੀਨ ਲਾਕ ਸੈੱਟ ਅੱਪ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">" ਲਾਕ ਸੈੱਟ ਕਰੋ"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> \'ਤੇ ਜਾਓ"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਬਣਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"ਉਪਨਾਮ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ਮਹਿਮਾਨ"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ਇੱਕ ਫ਼ੋਟੋ ਖਿੱਚੋ"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"ਕੋਈ ਚਿੱਤਰ ਚੁਣੋ"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"ਫ਼ੋਟੋ ਚੁਣੋ"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ਡੀਵਾਈਸ ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 7ea2d0c..112ae47 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -547,19 +547,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Zanim utworzysz profil z ograniczeniami, musisz skonfigurować ekran blokady, by chronić aplikacje i osobiste dane."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Ustaw blokadę"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Przełącz na: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Tworzę nowego użytkownika…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gość"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Wybierz zdjęcie"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Ustawienie domyślne urządzenia"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Wyłączono"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Włączono"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index de536b2..7354835 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Antes de poder criar um perfil restrito, tem de configurar um bloqueio de ecrã para proteger as suas aplicações e dados pessoais."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Definir bloqueio"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Mudar para <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"A criar novo utilizador…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Pseudónimo"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predefinição do dispositivo"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativada"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativada"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 934ef64..32e70da 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -546,19 +546,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Înainte de a putea crea un profil cu permisiuni limitate, va trebui să configurați blocarea ecranului pentru a vă proteja aplicațiile și datele personale."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Configurați blocarea"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Treceți la <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Se creează un utilizator nou…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Faceți o fotografie"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Selectați fotografia"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Prestabilit pentru dispozitiv"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Dezactivat"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index e75ee7f..b04d93b 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -547,19 +547,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Чтобы создать профиль с ограниченным доступом, необходимо предварительно настроить блокировку экрана для защиты приложений и личных данных"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Включить блокировку"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Переключиться на этот аккаунт: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Создаем нового пользователя…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Добавить аккаунт гостя"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гость"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбрать фото"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Выбрать фотографию"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Вариант по умолчанию"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Отключено"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Включено"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index dc9b702..86725c2 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"සීමිත පැතිකඩක් නිර්මාණය කිරීමට කලින්. ඔබගේ යෙදුම් සහ පෞද්ගලික දත්ත ආරක්ෂා කිරීමට තිර අගුලක් සැකසිය යුතුයි."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"අගුල සකසන්න"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> වෙත මාරු වන්න"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"නව පරිශීලක තනමින්…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"අපනාමය"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"අමුත්තා එක් කරන්න"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"අමුත්තා ඉවත් කරන්න"</string>
<string name="guest_nickname" msgid="6332276931583337261">"අමුත්තා"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ඡායාරූපයක් ගන්න"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"රූපයක් තෝරන්න"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"ඡායාරූපය තෝරන්න"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"උපාංගයේ පෙරනිමිය"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"අබල කළා"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"සබලයි"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 203f253..91e9564 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -547,19 +547,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Pred vytvorením obmedzeného profilu je nutné najprv nastaviť zámku obrazovky na ochranu aplikácií a osobných údajov."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Nastaviť uzamknutie"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Prepnúť na používateľa <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Vytvára sa nový používateľ…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Prezývka"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Hosť"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Vybrať fotku"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predvol. nastavenie zariadenia"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuté"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuté"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 3bfda06..45ffcde 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -547,19 +547,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Preden lahko ustvarite profil z omejitvami, morate nastaviti zaklepanje zaslona, da zaščitite aplikacije in osebne podatke."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Nastavi zaklepanje"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Preklop na račun <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ustvarjanje novega uporabnika …"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Vzdevek"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodajanje gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Odstranitev gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Izbira fotografije"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Privzeta nastavitev naprave"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogočeno"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogočeno"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 859cc70..c398363 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Para se të mund të krijosh një profil të kufizuar, duhet të konfigurosh një kyçje të ekranit për të mbrojtur aplikacionet dhe të dhënat e tua personale."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Cakto kyçjen"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Kalo te <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Po krijohet një përdorues i ri…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Pseudonimi"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Shto të ftuar"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Hiq të ftuarin"</string>
<string name="guest_nickname" msgid="6332276931583337261">"I ftuar"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Bëj një fotografi"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Zgjidh një imazh"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Zgjidh një fotografi"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Parazgjedhja e pajisjes"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Joaktiv"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 6c02c3c..bb59bd1 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -546,19 +546,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Да бисте могли да направите ограничени профил, треба да подесите закључавање екрана да бисте заштитили апликације и личне податке."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Подеси закључавање"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Пређи на корисника <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Прави се нови корисник…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Надимак"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Сликај"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Одабери слику"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Изаберите слику"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Подразумевано за уређај"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Онемогућено"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Омогућено"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index dc21675..e7b1482 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Innan du skapar en begränsad profil måste du konfigurera ett skärmlås för att skydda dina appar och personliga data."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Konfigurera lås"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Byt till <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Skapar ny användare …"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Smeknamn"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gäst"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Ta ett foto"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Välj en bild"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Välj foto"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Enhetens standardinställning"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inaktiverat"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiverat"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 8f80e55..1f0d167 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Kabla uunde wasifu uliowekekwa vikwazo, utahitajika kuweka skrini iliyofungwa ili kulinda programu zako na data binafsi."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Weka ufunguo"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Badili utumie <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Inaweka mtumiaji mpya…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Weka mgeni"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Chagua picha"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Chagua picha"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Hali chaguomsingi ya kifaa"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Imezimwa"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Imewashwa"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 1069421..b6d8be8 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"நீங்கள் வரையறுக்கப்பட்டச் சுயவிவரத்தை உருவாக்குவதற்கு முன்பு, உங்கள் ஆப்ஸ் மற்றும் தனிப்பட்ட தரவைப் பாதுகாக்கும் வகையில் நீங்கள் திரைப் பூட்டை அமைக்க வேண்டும்."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"பூட்டை அமை"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>க்கு மாறு"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"புதிய பயனரை உருவாக்குகிறது…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"புனைப்பெயர்"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"கெஸ்ட்டைச் சேர்"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"கெஸ்ட்டை அகற்று"</string>
<string name="guest_nickname" msgid="6332276931583337261">"கெஸ்ட்"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"படமெடுங்கள்"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"படத்தைத் தேர்வுசெய்யுங்கள்"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"படத்தைத் தேர்ந்தெடுங்கள்"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"சாதனத்தின் இயல்புநிலை"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"முடக்கப்பட்டது"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"இயக்கப்பட்டது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 3ad2375..5a8e734 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"మీరు పరిమితం చేయబడిన ప్రొఫైల్ను సృష్టించడానికి ముందు, మీ అనువర్తనాలు మరియు వ్యక్తిగత డేటాను రక్షించడానికి స్క్రీన్ లాక్ను సెటప్ చేయాల్సి ఉంటుంది."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"లాక్ను సెట్ చేయి"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>కు మార్చు"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"కొత్త యూజర్ను క్రియేట్ చేస్తోంది…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"మారుపేరు"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"అతిథిని జోడించండి"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"అతిథిని తీసివేయండి"</string>
<string name="guest_nickname" msgid="6332276931583337261">"అతిథి"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"ఇమేజ్ను ఎంచుకోండి"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"ఫోటోను ఎంచుకోండి"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"పరికర ఆటోమేటిక్ సెట్టింగ్"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"డిజేబుల్ చేయబడింది"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ఎనేబుల్ చేయబడింది"</string>
diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml
index 58d8a45..21fe6e4 100644
--- a/packages/SettingsLib/res/values-th/arrays.xml
+++ b/packages/SettingsLib/res/values-th/arrays.xml
@@ -138,15 +138,15 @@
<item msgid="1333279807604675720">"สเตอริโอ"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
- <item msgid="1241278021345116816">"เพิ่มประสิทธิภาพสำหรับคุณภาพเสียง (990 kbps/909 kbps)"</item>
+ <item msgid="1241278021345116816">"เพิ่มประสิทธิภาพเพื่อคุณภาพเสียง (990 kbps/909 kbps)"</item>
<item msgid="3523665555859696539">"คุณภาพเสียงและการเชื่อมต่อที่สมดุล (660 kbps/606 kbps)"</item>
- <item msgid="886408010459747589">"เพิ่มประสิทธิภาพสำหรับคุณภาพการเชื่อมต่อ (330 kbps/303 kbps)"</item>
+ <item msgid="886408010459747589">"เพิ่มประสิทธิภาพเพื่อคุณภาพการเชื่อมต่อ (330 kbps/303 kbps)"</item>
<item msgid="3808414041654351577">"ดีที่สุดเท่าที่ทำได้ (ปรับอัตราบิตอัตโนมัติ)"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
- <item msgid="804499336721569838">"เพิ่มประสิทธิภาพสำหรับคุณภาพเสียง"</item>
+ <item msgid="804499336721569838">"เพิ่มประสิทธิภาพเพื่อคุณภาพเสียง"</item>
<item msgid="7451422070435297462">"คุณภาพเสียงและการเชื่อมต่อที่สมดุล"</item>
- <item msgid="6173114545795428901">"เพิ่มประสิทธิภาพสำหรับคุณภาพการเชื่อมต่อ"</item>
+ <item msgid="6173114545795428901">"เพิ่มประสิทธิภาพเพื่อคุณภาพการเชื่อมต่อ"</item>
<item msgid="4349908264188040530">"ดีที่สุดเท่าที่ทำได้ (ปรับอัตราบิตอัตโนมัติ)"</item>
</string-array>
<string-array name="bluetooth_audio_active_device_summaries">
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index cc0ac74..2ae4131 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"ก่อนที่คุณจะสามารถสร้างโปรไฟล์ที่ถูกจำกัดได้ คุณจะต้องตั้งค่าล็อกหน้าจอเพื่อปกป้องแอปและข้อมูลส่วนตัวของคุณ"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"ตั้งค่าล็อก"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"เปลี่ยนเป็น <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"กำลังสร้างผู้ใช้ใหม่…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"ชื่อเล่น"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้เข้าร่วม"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้เข้าร่วมออก"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ผู้ใช้ชั่วคราว"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"เลือกรูปภาพ"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ค่าเริ่มต้นของอุปกรณ์"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ปิดใช้"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"เปิดใช้"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 77ab7cf..e9f1da3 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Bago ka makakalikha ng pinaghihigpitang profile, kakailanganin mong mag-set up ng screen lock upang protektahan ang iyong apps at personal na data."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Itakda ang lock"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Lumipat sa <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Gumagawa ng bagong user…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Bisita"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Pumili ng larawan"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Default ng device"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Naka-disable"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Na-enable"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 9d980c4..0502359 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Kısıtlanmış bir profil oluşturabilmeniz için uygulamalarınızı ve kişisel verilerinizi korumak üzere bir ekran kilidi oluşturmanız gerekir."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Kilidi ayarla"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> hesabına geç"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yeni kullanıcı oluşturuluyor…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Takma ad"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Misafir"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Fotoğraf çek"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Resim seç"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Fotoğraf seç"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Cihaz varsayılanı"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Devre dışı"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Etkin"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 9a7b89f..b68aab4 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -547,19 +547,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Перш ніж створювати обмежений профіль, потрібно налаштувати блокування екрана, щоб захистити свої програми та особисті дані."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Налаштувати блокування"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Перейти до користувача <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Створення нового користувача…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Псевдонім"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гість"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Зробити фотографію"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Вибрати зображення"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Вибрати фотографію"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"За умовчанням для пристрою"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Вимкнено"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Увімкнено"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 1cc0859..1802be6 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"ایک محدود پروفائل بنانے سے پہلے، آپ کو اپنی ایپس اور ذاتی ڈیٹا کو محفوظ کرنے کیلئے ایک اسکرین لاک سیٹ اپ کرنا ہوگا۔"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"لاک سیٹ کریں"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> پر سوئچ کریں"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"نیا صارف تخلیق کرنا…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"عرفی نام"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string>
<string name="guest_nickname" msgid="6332276931583337261">"مہمان"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"ایک تصویر لیں"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"ایک تصویر منتخب کریں"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"تصویر منتخب کریں"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"آلہ ڈیفالٹ"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غیر فعال"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 2ed6ca8..4d4808f 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Trước khi bạn có thể tạo tiểu sử bị hạn chế, bạn sẽ cần thiết lập một màn hình khóa để bảo vệ các ứng dụng và dữ liệu cá nhân của bạn."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Thiết lập khóa"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Chuyển sang <xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Đang tạo người dùng mới…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Biệt hiệu"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Khách"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Chụp ảnh"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Chọn một hình ảnh"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Chọn ảnh"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Theo giá trị mặc định của thiết bị"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Đã tắt"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Đã bật"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 024ea79..b6e8eba2 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"您需要先设置锁定屏幕来保护您的应用和个人数据,然后才可以创建受限个人资料。"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"设置屏幕锁定方式"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"切换到<xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在创建新用户…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"昵称"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"添加访客"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"移除访客"</string>
<string name="guest_nickname" msgid="6332276931583337261">"访客"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"拍摄照片"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"选择图片"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"选择照片"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"设备默认设置"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已启用"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 75f050f..07bc887 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"建立限制存取的個人檔案前,您必須先設定上鎖畫面來保護您的應用程式和個人資料。"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"設定上鎖畫面"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"切換至<xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在建立新使用者…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"暱稱"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
<string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"揀相"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"裝置預設設定"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 9866b47..833a82f 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"如要建立設有限制的個人資料,你必須先設定螢幕鎖定來保護你的應用程式和個人資料。"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"設定鎖定"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"切換至<xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在建立新使用者…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"暱稱"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
<string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"選取相片"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"裝置預設設定"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index b1825c4..0065eb6 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -545,19 +545,14 @@
<string name="user_need_lock_message" msgid="4311424336209509301">"Ngaphambi kokuthi ungadala iphrofayela ekhawulelwe, kuzomele usethe ukukhiya isikrini ukuze uvikele izinhlelo zakho zokusebenza nedatha yakho yomuntu siqu."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Setha ukukhiya"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Shintshela ku-<xliff:g id="USER_NAME">%s</xliff:g>"</string>
- <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
- <skip />
- <!-- no translation found for user_nickname (262624187455825083) -->
- <skip />
+ <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Idala umsebenzisi omusha…"</string>
+ <string name="user_nickname" msgid="262624187455825083">"Isiteketiso"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Engeza isivakashi"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Susa isihambeli"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Isihambeli"</string>
- <!-- no translation found for user_image_take_photo (467512954561638530) -->
- <skip />
- <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
- <skip />
- <!-- no translation found for user_image_photo_selector (433658323306627093) -->
- <skip />
+ <string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string>
+ <string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Khetha isithombe"</string>
<string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Idivayisi ezenzakalelayo"</string>
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ikhutshaziwe"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Inikwe amandla"</string>
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 81cf118..b0a9136 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -1090,7 +1090,7 @@
// Verify second update AP is the same object as the first update AP
assertThat(passpointAccessPointsFirstUpdate.get(0))
- .isSameAs(passpointAccessPointsSecondUpdate.get(0));
+ .isSameInstanceAs(passpointAccessPointsSecondUpdate.get(0));
// Verify second update AP has the average of the first and second update RSSIs
assertThat(passpointAccessPointsSecondUpdate.get(0).getRssi())
.isEqualTo((prevRssi + newRssi) / 2);
@@ -1210,7 +1210,8 @@
providersAndScans, cachedAccessPoints);
// Verify second update AP is the same object as the first update AP
- assertThat(osuAccessPointsFirstUpdate.get(0)).isSameAs(osuAccessPointsSecondUpdate.get(0));
+ assertThat(osuAccessPointsFirstUpdate.get(0))
+ .isSameInstanceAs(osuAccessPointsSecondUpdate.get(0));
// Verify second update AP has the average of the first and second update RSSIs
assertThat(osuAccessPointsSecondUpdate.get(0).getRssi())
.isEqualTo((prevRssi + newRssi) / 2);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
index a83d7e0..b392c5e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
@@ -69,7 +69,7 @@
assertWithMessage("Intent filter should contain expected intents")
.that(ipAddressPreferenceController.getConnectivityIntents())
- .asList().containsAllIn(expectedIntents);
+ .asList().containsAtLeastElementsIn(expectedIntents);
}
private static class ConcreteIpAddressPreferenceController extends
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
index 40b9b13..3705267 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -90,7 +90,7 @@
assertWithMessage("Intent filter should contain expected intents")
.that(mController.getConnectivityIntents())
- .asList().containsAllIn(expectedIntents);
+ .asList().containsAtLeastElementsIn(expectedIntents);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 1769053..906e06e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -284,7 +284,7 @@
assertThat(outTiles).hasSize(1);
final Bundle newMetaData = outTiles.get(0).getMetaData();
- assertThat(newMetaData).isNotSameAs(oldMetadata);
+ assertThat(newMetaData).isNotSameInstanceAs(oldMetadata);
}
@Test
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 51f69a9..5ad43e3 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -240,4 +240,7 @@
<!-- Default for setting for Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED -->
<bool name="def_hdmiControlAutoDeviceOff">false</bool>
+
+ <!-- Default for Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY -->
+ <integer name="def_accessibility_magnification_capabilities">3</integer>
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 710c016..6dd2936 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -25,6 +25,7 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX;
import android.Manifest;
@@ -3341,7 +3342,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 192;
+ private static final int SETTINGS_VERSION = 193;
private final int mUserId;
@@ -4724,6 +4725,35 @@
currentVersion = 192;
}
+ if (currentVersion == 192) {
+ // Version 192: set the default value for magnification capabilities. If
+ // magnification is enabled by the user, set it to full-screen, and set a value
+ // to show a prompt when using the magnification first time after upgrading.
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ final Setting magnificationCapabilities = secureSettings.getSettingLocked(
+ Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY);
+ if (magnificationCapabilities.isNull()) {
+ secureSettings.insertSettingLocked(
+ Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
+ String.valueOf(getContext().getResources().getInteger(
+ R.integer.def_accessibility_magnification_capabilities)),
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+
+ if (isMagnificationSettingsOn(secureSettings)) {
+ secureSettings.insertSettingLocked(
+ Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY, String.valueOf(
+ Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN),
+ null, false /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ secureSettings.insertSettingLocked(
+ Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, "1",
+ null, false /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ }
+ currentVersion = 193;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
@@ -4862,5 +4892,45 @@
}
}
}
+
+ private boolean isMagnificationSettingsOn(SettingsState secureSettings) {
+ if ("1".equals(secureSettings.getSettingLocked(
+ Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED).getValue())) {
+ return true;
+ }
+
+ final Set<String> a11yButtonTargets = transformColonDelimitedStringToSet(
+ secureSettings.getSettingLocked(
+ Secure.ACCESSIBILITY_BUTTON_TARGETS).getValue());
+ if (a11yButtonTargets != null && a11yButtonTargets.contains(
+ MAGNIFICATION_CONTROLLER_NAME)) {
+ return true;
+ }
+
+ final Set<String> a11yShortcutServices = transformColonDelimitedStringToSet(
+ secureSettings.getSettingLocked(
+ Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE).getValue());
+ if (a11yShortcutServices != null && a11yShortcutServices.contains(
+ MAGNIFICATION_CONTROLLER_NAME)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Nullable
+ private Set<String> transformColonDelimitedStringToSet(String value) {
+ if (TextUtils.isEmpty(value)) return null;
+ final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(':');
+ splitter.setString(value);
+ final Set<String> items = new HashSet<>();
+ while (splitter.hasNext()) {
+ final String str = splitter.next();
+ if (TextUtils.isEmpty(str)) {
+ continue;
+ }
+ items.add(str);
+ }
+ return items;
+ }
}
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 69be144..9b0df96 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -314,6 +314,7 @@
Settings.Global.KERNEL_CPU_THREAD_READER,
Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
Settings.Global.LANG_ID_UPDATE_METADATA_URL,
+ Settings.Global.LATENCY_TRACKER,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
@@ -747,7 +748,8 @@
Settings.Secure.WINDOW_MAGNIFICATION,
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER,
Settings.Secure.SUPPRESS_DOZE,
- Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED);
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
+ Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT);
@Test
public void systemSettingsBackedUpOrDenied() {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index a927997..5f018a0 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -329,6 +329,15 @@
<!-- Permission needed for CTS test - TimeManagerTest -->
<uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
+ <!-- Permission required for CTS test - android.server.biometrics -->
+ <uses-permission android:name="android.permission.USE_BIOMETRIC" />
+
+ <!-- Permission required for CTS test - android.server.biometrics -->
+ <uses-permission android:name="android.permission.TEST_BIOMETRIC" />
+
+ <!-- Permissions required for CTS test - NotificationManagerTest -->
+ <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SoundPicker/res/values-uz/strings.xml b/packages/SoundPicker/res/values-uz/strings.xml
index 9018e66..a617733 100644
--- a/packages/SoundPicker/res/values-uz/strings.xml
+++ b/packages/SoundPicker/res/values-uz/strings.xml
@@ -21,7 +21,7 @@
<string name="alarm_sound_default" msgid="4787646764557462649">"Standart signal tovushi"</string>
<string name="add_ringtone_text" msgid="6642389991738337529">"Rington qo‘shish"</string>
<string name="add_alarm_text" msgid="3545497316166999225">"Signal qo‘shish"</string>
- <string name="add_notification_text" msgid="4431129543300614788">"Bildirishnoma qo‘shish"</string>
+ <string name="add_notification_text" msgid="4431129543300614788">"Bildirishnoma kiritish"</string>
<string name="delete_ringtone_text" msgid="201443984070732499">"O‘chirish"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Maxsus rington qo‘shib bo‘lmadi"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Maxsus ringtonni o‘chirib bo‘lmadi"</string>
diff --git a/packages/SystemUI/res/layout-land/global_screenshot_preview.xml b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
index b1f4cb7..040303a 100644
--- a/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
+++ b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml
@@ -28,6 +28,6 @@
android:visibility="gone"
android:background="@drawable/screenshot_rounded_corners"
android:adjustViewBounds="true"
- android:contentDescription="@string/screenshot_preview_description"
+ android:contentDescription="@string/screenshot_edit"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
index 4b3534b..1021186d 100644
--- a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
@@ -19,7 +19,7 @@
android:id="@+id/global_screenshot_action_chip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/screenshot_action_chip_margin_right"
+ android:layout_marginStart="@dimen/screenshot_action_chip_margin_start"
android:paddingVertical="@dimen/screenshot_action_chip_margin_vertical"
android:layout_gravity="center"
android:gravity="center"
diff --git a/packages/SystemUI/res/layout/global_screenshot_preview.xml b/packages/SystemUI/res/layout/global_screenshot_preview.xml
index e6295f5..c745854 100644
--- a/packages/SystemUI/res/layout/global_screenshot_preview.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_preview.xml
@@ -28,6 +28,6 @@
android:visibility="gone"
android:background="@drawable/screenshot_rounded_corners"
android:adjustViewBounds="true"
- android:contentDescription="@string/screenshot_preview_description"
+ android:contentDescription="@string/screenshot_edit"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_screenshot_static.xml b/packages/SystemUI/res/layout/global_screenshot_static.xml
index 9ec2f20..26edf3a 100644
--- a/packages/SystemUI/res/layout/global_screenshot_static.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_static.xml
@@ -51,7 +51,12 @@
<LinearLayout
android:id="@+id/global_screenshot_actions"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content">
+ <include layout="@layout/global_screenshot_action_chip"
+ android:id="@+id/screenshot_share_chip"/>
+ <include layout="@layout/global_screenshot_action_chip"
+ android:id="@+id/screenshot_edit_chip"/>
+ </LinearLayout>
</HorizontalScrollView>
<include layout="@layout/global_screenshot_preview"/>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 7d3390d..e4d751a 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probeer weer skermkiekie neem"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan weens beperkte bergingspasie nie skermkiekie stoor nie"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die program of jou organisasie laat nie toe dat skermkiekies geneem word nie"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Maak skermkiekie toe"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Skermkiekievoorskou"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skermopnemer"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Instellings"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Vergrotingvenster"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Vergrotingvensterkontroles"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoem in"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoem uit"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Skuif op"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Skuif af"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Beweeg links"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Beweeg regs"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Vergrotingwisselaar"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Vergroot die hele skerm"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Vergroot \'n deel van die skerm"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Wissel"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Toestelkontroles"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Voeg kontroles vir jou gekoppelde toestelle by"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Stel toestelkontroles op"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laai tans aanbevelings"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Versteek die huidige sessie."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Maak toe"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Hervat"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Instellings"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ontkoppel)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Kon nie koppel nie. Probeer weer."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bind nuwe toestel saam"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Bounommer"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Bounommer is na knipbord gekopieer."</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index a91182e..8c22e8f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ቅጽበታዊ ገጽ ዕይታን እንደገና ማንሳት ይሞክሩ"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ባለው ውሱን የማከማቻ ቦታ ምክንያት ቅጽበታዊ ገጽ ዕይታን ማስቀመጥ አይችልም"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ቅጽበታዊ ገጽ እይታዎችን ማንሳት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ቅጽበታዊ ገጽ እይታን አሰናብት"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"የቅጽበታዊ ገጽ ዕይታ ቅድመ-ዕይታ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"የማያ መቅጃ"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ቅንብሮች"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"የማጉያ መስኮት"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"የማጉያ መስኮት መቆጣጠሪያዎች"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"አጉላ"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"አሳንስ"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"ወደ ላይ ውሰድ"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ወደ ታች ውሰድ"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"ወደ ግራ ውሰድ"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"ወደ ቀኝ ውሰድ"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"የማጉላት ማብሪያ/ማጥፊያ"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ሙሉውን ማያ ገጽ አጉላ"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"የማያ ገጹን ክፍል አጉላ"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ማብሪያ/ማጥፊያ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"የመሣሪያ መቆጣጠሪያዎች"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"ለእርስዎ የተገናኙ መሣሪያዎች መቆጣጠሪያዎችን ያክሉ"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"የመሣሪያ መቆጣጠሪያዎችን ያቀናብሩ"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ምክሮችን በመጫን ላይ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"ሚዲያ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"የአሁኑን ክፍለ-ጊዜ ደብቅ።"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"አሰናብት"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ከቆመበት ቀጥል"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ቅንብሮች"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ግንኙነት ተቋርጧል)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ማገናኘት አልተቻለም። እንደገና ይሞክሩ።"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"አዲስ መሣሪያ ያጣምሩ"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"የግንብ ቁጥር"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"የገንባ ቁጥር ወደ ቅንጥብ ሰሌዳ ተቀድቷል።"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 74a9b64..2c74a27 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"جرّب أخذ لقطة الشاشة مرة أخرى"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"يتعذر حفظ لقطة الشاشة لأن مساحة التخزين المتاحة محدودة."</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"يحظر التطبيق أو تحظر مؤسستك التقاط لقطات شاشة"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"إغلاق لقطة الشاشة"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"معاينة لقطة الشاشة"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"مسجّل الشاشة"</string>
@@ -1032,18 +1034,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"الإعدادات"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"نافذة التكبير"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"عناصر التحكم في نافذة التكبير"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"تكبير"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"تصغير"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"نقل للأعلى"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"نقل للأسفل"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"نقل لليسار"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"نقل لليمين"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"مفتاح تبديل وضع التكبير"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"تكبير الشاشة بالكامل"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"تكبير جزء من الشاشة"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"تبديل"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"أدوات التحكم بالأجهزة"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"إضافة عناصر تحكّم لأجهزتك المتصلة"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"إعداد أدوات التحكم بالجهاز"</string>
@@ -1089,6 +1089,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"جارٍ تحميل الاقتراحات"</string>
<string name="controls_media_title" msgid="1746947284862928133">"الوسائط"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"إخفاء الجلسة الحالية"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"إغلاق"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"استئناف التشغيل"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"الإعدادات"</string>
@@ -1111,8 +1113,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (غير متّصل)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"تعذّر الاتصال. يُرجى إعادة المحاولة."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"إقران جهاز جديد"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"رقم الإصدار"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"تم نسخ رقم الإصدار إلى الحافظة."</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 297d774..94cd1a7 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"স্ক্ৰীণশ্বট আকৌ ল\'বলৈ চেষ্টা কৰক"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"সঞ্চয়াগাৰত সীমিত খালী ঠাই থকাৰ বাবে স্ক্ৰীণশ্বট ছেভ কৰিব পৰা নগ\'ল"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"এপটোৱে বা আপোনাৰ প্ৰতিষ্ঠানে স্ক্ৰীণশ্বট ল\'বলৈ অনুমতি নিদিয়ে"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"স্ক্ৰীনশ্বট অগ্ৰাহ্য কৰক"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্ৰীনশ্বটৰ পূৰ্বদৰ্শন"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"স্ক্ৰীন ৰেকৰ্ডাৰ"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ছেটিংসমূহ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"বিবৰ্ধন ৱিণ্ড’"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"বিবৰ্ধন ৱিণ্ড’ৰ নিয়ন্ত্ৰণসমূহ"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"জুম ইন কৰক"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"জুম আউট কৰক"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"ওপৰলৈ নিয়ক"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"তললৈ নিয়ক"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"বাওঁফাললৈ নিয়ক"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"সোঁফাললৈ নিয়ক"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"বিবৰ্ধনৰ ছুইচ"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"গোটেই স্ক্ৰীনখন বিবৰ্ধন কৰক"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্ৰীনৰ কিছু অংশ বিবৰ্ধন কৰক"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ছুইচ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইচৰ নিয়ন্ত্ৰণসমূহ"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"আপোনাৰ সংযোজিত ডিভাইচসমূহৰ বাবে নিয়ন্ত্ৰণসমূহ যোগ কৰক"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"ডিভাইচৰ নিয়ন্ত্ৰণসমূহ ছেট আপ কৰক"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"চুপাৰিছসমূহ ল’ড কৰি থকা হৈছে"</string>
<string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"বৰ্তমানৰ ছেশ্বনটো লুকুৱাওক।"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"অগ্ৰাহ্য কৰক"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"পুনৰ আৰম্ভ কৰক"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ছেটিংসমূহ"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (সংযোগ বিচ্ছিন্ন হৈছে)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"সংযোগ কৰিব পৰা নগ’ল। পুনৰ চেষ্টা কৰক।"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইচ পেয়াৰ কৰক"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"বিল্ডৰ নম্বৰ"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"ক্লিপব’ৰ্ডলৈ বিল্ডৰ নম্বৰ প্ৰতিলিপি কৰা হ’ল।"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index debad02..43aca9a 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Skrinşotu yenidən çəkin"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Yaddaş ehtiyatının az olması səbəbindən skrinşotu yadda saxlamaq olmur"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Skrinşot çəkməyə tətbiq və ya təşkilat tərəfindən icazə verilmir"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ekran şəklini ötürün"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran şəklinə önbaxış"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekran Yazıcısı"</string>
@@ -797,7 +799,7 @@
<string name="keyboard_key_page_up" msgid="173914303254199845">"Yuxarı Səhifə"</string>
<string name="keyboard_key_page_down" msgid="9035902490071829731">"Aşağı Səhifə"</string>
<string name="keyboard_key_forward_del" msgid="5325501825762733459">"Silin"</string>
- <string name="keyboard_key_move_home" msgid="3496502501803911971">"Əsas səhifə"</string>
+ <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
<string name="keyboard_key_move_end" msgid="99190401463834854">"Son"</string>
<string name="keyboard_key_insert" msgid="4621692715704410493">"Daxil edin"</string>
<string name="keyboard_key_num_lock" msgid="7209960042043090548">"Nömrələr"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ayarlar"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Böyütmə Pəncərəsi"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Böyütmə Pəncərəsi Kontrolları"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Yaxınlaşdırın"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Uzaqlaşdırın"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Yuxarı köçürün"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Aşağı köçürün"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sola köçürün"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sağa köçürün"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Böyütmə dəyişdiricisi"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Bütün ekranı böyüdün"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekranın bir hissəsini böyüdün"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Dəyişdirici"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Cihaz idarəetmələri"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Qoşulmuş cihazlarınız üçün nizamlayıcılar əlavə edin"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Cihaz idarəetmələrini ayarlayın"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tövsiyələr yüklənir"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Cari sessiyanı gizlədin."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"İmtina edin"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Davam edin"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (bağlantı kəsilib)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Qoşulmaq alınmadı. Yenə cəhd edin."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yeni cihazı qoşalaşdırın"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versiya nömrəsi"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Versiya nömrəsi mübadilə buferinə kopyalandı."</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index be31c2d..3777dfe 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probajte da ponovo napravite snimak ekrana"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Čuvanje snimka ekrana nije uspelo zbog ograničenog memorijskog prostora"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ili organizacija ne dozvoljavaju pravljenje snimaka ekrana"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacite snimak ekrana"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
@@ -1017,18 +1019,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Podešavanja"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Prozor za uvećanje"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za uvećanje"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Uvećajte"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Umanjite"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Pomerite nagore"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pomerite nadole"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pomerite nalevo"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pomerite nadesno"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prelazak na drugi režim uvećanja"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Uvećajte ceo ekran"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećajte deo ekrana"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Pređi"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodajte kontrole za povezane uređaje"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Podesite kontrole uređaja"</string>
@@ -1071,6 +1071,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavaju se preporuke"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte aktuelnu sesiju."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Podešavanja"</string>
@@ -1093,8 +1095,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (veza je prekinuta)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezivanje nije uspelo. Probajte ponovo."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Upari novi uređaj"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u privremenu memoriju."</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index e3831a8..8a5d42a 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Паспрабуйце зрабіць здымак экрана яшчэ раз"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Немагчыма захаваць здымак экрана, бо мала месца ў сховішчы"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Рабіць здымкі экрана не дазваляе праграма ці ваша арганізацыя"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Адхіліць здымак экрана"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Перадпрагляд здымка экрана"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Запіс экрана"</string>
@@ -1022,18 +1024,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Налады"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Акно павелічэння"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Налады акна павелічэння"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Павялічыць маштаб"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Паменшыць маштаб"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Перамясціць уверх"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Перамясціць ніжэй"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Перамясціць улева"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Перамясціць управа"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Пераключальнік павелічэння"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Павялічыць на ўвесь экран"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Павялічыць частку экрана"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Пераключальнік"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Элементы кіравання прыладай"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Дадайце элементы кіравання для падключаных прылад"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Наладзіць элементы кіравання прыладай"</string>
@@ -1077,6 +1077,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загружаюцца рэкамендацыі"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Мультымедыя"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Схаваць цяперашні сеанс."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Адхіліць"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Узнавіць"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Налады"</string>
@@ -1099,8 +1101,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (адключана)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не ўдалося падключыцца. Паўтарыце спробу."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спалучыць з новай прыладай"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Нумар зборкі"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Нумар зборкі скапіраваны ў буфер абмену."</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index d959178..f998749 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Опитайте да направите екранна снимка отново"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Екранната снимка не може да се запази поради ограничено място в хранилището"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Правенето на екранни снимки не е разрешено от приложението или организацията ви"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Отхвърляне на екранната снимка"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Визуализация на екранната снимка"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Запис на екрана"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Настройки"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Прозорец за ниво на мащаба"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Контроли за прозореца за ниво на мащаба"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Увеличаване на мащаба"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Намаляване на мащаба"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Преместване нагоре"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Преместване надолу"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Преместване наляво"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Преместване надясно"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Превключване на увеличението"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Увеличаване на целия екран"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличаване на част от екрана"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Превключване"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Контроли за устройството"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Добавяне на контроли за свързаните ви устройства"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Настройване на контролите за устройството"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Препоръките се зареждат"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Мултимедия"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Скриване на текущата сесия."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Отхвърляне"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Възобновяване"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (връзката е прекратена)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Неуспешно свързване. Опитайте отново."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Сдвояване на ново устройство"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер на компилацията"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Номерът на компилацията е копиран в буферната памет."</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index fffa555..6cdc128 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"আবার স্ক্রিনশট নেওয়ার চেষ্টা করুন"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"বেশি জায়গা নেই তাই স্ক্রিনশটটি সেভ করা যাবে না৷"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"এই অ্যাপ বা আপনার প্রতিষ্ঠান স্ক্রিনশট নেওয়ার অনুমতি দেয়নি"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"স্ক্রিনশট বাতিল করুন"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্রিনশটের প্রিভিউ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"স্ক্রিন রেকর্ডার"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"সেটিংস"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"উইন্ডো বড় করে দেখা"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"উইন্ডো কন্ট্রোল বড় করে দেখা"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"বড় করুন"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ছোট করুন"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"উপরে তুলুন"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"নিচে নামান"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"বাঁদিকে সরান"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"ডানদিকে সরান"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"বড় করে দেখার সুইচ"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ফুল-স্ক্রিন মোডে দেখুন"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্রিনের কিছুটা অংশ বড় করুন"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"বদল করুন"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইস কন্ট্রোল"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"আপনার কানেক্ট করা ডিভাইসের জন্য কন্ট্রোল যোগ করুন"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"ডিভাইস কন্ট্রোল সেট-আপ করুন"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"সাজেশন লোড করা হচ্ছে"</string>
<string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"বর্তমান সেশন লুকান।"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"খারিজ করুন"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"আবার চালু করুন"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"সেটিংস"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (কানেক্ট করা নেই)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"কানেক্ট করা যায়নি। আবার চেষ্টা করুন।"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইস পেয়ার করুন"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"বিল্ড নম্বর"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"বিল্ড নম্বর ক্লিপবোর্ডে কপি করা হয়েছে।"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index f5410cf..8f622c8 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pokušajte ponovo snimiti ekran"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snimak ekrana se ne može sačuvati zbog manjka prostora za pohranu"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ova aplikacija ili vaša organizacija ne dozvoljavaju snimanje ekrana"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacite snimak ekrana"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string>
@@ -726,7 +728,7 @@
<string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Nema nedavnih oblačića"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Nedavni i odbačeni oblačići će se pojaviti ovdje"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"Ta obavještenja se ne mogu izmijeniti."</string>
- <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ovdje nije moguće konfigurirati ovu grupu obavještenja"</string>
+ <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ovu grupu obavještenja nije moguće konfigurirati ovdje"</string>
<string name="notification_delegate_header" msgid="1264510071031479920">"Obavještenje preko proksi servera"</string>
<string name="notification_channel_dialog_title" msgid="6856514143093200019">"Sva obavještenja aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="see_more_title" msgid="7409317011708185729">"Prikaži više"</string>
@@ -1017,18 +1019,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Postavke"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Prozor za uvećavanje"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za uvećavanje"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Uvećavanje"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Umanjivanje"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Pomjeranje prema gore"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pomjeranje prema dolje"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pomjeranje lijevo"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pomjeranje desno"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prekidač za uvećavanje"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Uvećavanje cijelog ekrana"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećavanje dijela ekrana"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prekidač"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodajte kontrole za povezane uređaje"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Postavite kontrole uređaja"</string>
@@ -1071,6 +1071,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte trenutnu sesiju."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
@@ -1093,8 +1095,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (veza je prekinuta)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezivanje nije uspjelo. Pokušajte ponovo."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uparite novi uređaj"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u međumemoriju."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 28bf3bb..ea7f36a 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prova de tornar a fer una captura de pantalla"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"La captura de pantalla no es pot desar perquè no hi ha prou espai d\'emmagatzematge"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'aplicació o la teva organització no permeten fer captures de pantalla"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora la captura de pantalla"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Previsualització de la captura de pantalla"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravació de pantalla"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configuració"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Finestra d\'ampliació"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Finestra de controls d\'ampliació"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Amplia"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Redueix"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mou cap amunt"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mou cap avall"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mou cap a l\'esquerra"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mou cap a la dreta"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Canvia al mode d\'ampliació"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Amplia tota la pantalla"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplia una part de la pantalla"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Canvia"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controls de dispositius"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Afegeix controls per als teus dispositius connectats"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Configura els controls de dispositius"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregant les recomanacions"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Multimèdia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Amaga la sessió actual."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignora"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reprèn"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Configuració"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconnectat)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"No s\'ha pogut connectar. Torna-ho a provar."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincula un dispositiu nou"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilació"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"El número de compilació s\'ha copiat al porta-retalls."</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index c66f200..d3e3dbf 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Zkuste snímek pořídit znovu"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snímek obrazovky kvůli nedostatku místa v úložišti nelze uložit"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikace nebo organizace zakazuje pořizování snímků obrazovky"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zavřít snímek obrazovky"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Náhled snímku obrazovky"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
@@ -1022,18 +1024,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Nastavení"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Zvětšovací okno"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Ovládací prvky zvětšovacího okna"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Přiblížit"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Oddálit"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Přesunout nahoru"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Přesunout dolů"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Přesunout doleva"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Přesunout doprava"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Přepínač zvětšení"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Zvětšit celou obrazovku"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zvětšit část obrazovky"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Přepnout"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Ovládání zařízení"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Přidejte ovládací prvky pro připojená zařízení"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Nastavení ovládání zařízení"</string>
@@ -1077,6 +1077,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítání doporučení"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Média"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Skrýt aktuální relaci."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Zavřít"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Pokračovat"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavení"</string>
@@ -1099,8 +1101,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (odpojeno)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Spojení se nezdařilo. Zkuste to znovu."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovat nové zařízení"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo sestavení"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo sestavení bylo zkopírováno do schránky."</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 5b0566d..82e8fd1 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prøv at tage et screenshot igen"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Screenshottet kan ikke gemmes, fordi der er begrænset lagerplads"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller din organisation tillader ikke, at du tager screenshots"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Luk screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning af screenshot"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skærmoptagelse"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Indstillinger"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Vindue med forstørrelse"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Vindue med forstørrelsesstyring"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom ind"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom ud"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Flyt op"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Flyt ned"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Flyt til venstre"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Flyt til højre"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Skift forstørrelsestilstand"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Forstør hele skærmen"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstør en del af skærmen"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Skift"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Enhedsstyring"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Tilføj styring af dine tilsluttede enheder"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfigurer enhedsstyring"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Indlæser anbefalinger"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Medie"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den aktuelle session."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Luk"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Genoptag"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Indstillinger"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ingen forbindelse)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Der kunne ikke oprettes forbindelse. Prøv igen."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Par ny enhed"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildnummer"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Buildnummeret blev kopieret til udklipsholderen."</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 0ab1f0e..0e3f309 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -28,15 +28,15 @@
<string name="battery_low_percent_format" msgid="4276661262843170964">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend"</string>
<string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"Noch <xliff:g id="PERCENTAGE">%1$s</xliff:g> übrig; bei deinem Nutzungsmuster hast du noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> ausstehend; noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Stromsparmodus ist aktiviert."</string>
+ <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Energiesparmodus ist aktiviert."</string>
<string name="invalid_charger" msgid="4370074072117767416">"Aufladen über USB nicht möglich. Verwende das mit dem Gerät gelieferte Ladegerät."</string>
<string name="invalid_charger_title" msgid="938685362320735167">"Aufladen über USB nicht möglich"</string>
<string name="invalid_charger_text" msgid="2339310107232691577">"Verwende das mit dem Gerät gelieferte Ladegerät"</string>
<string name="battery_low_why" msgid="2056750982959359863">"Einstellungen"</string>
- <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Stromsparmodus aktivieren?"</string>
- <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Stromsparmodus"</string>
+ <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Energiesparmodus aktivieren?"</string>
+ <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Energiesparmodus"</string>
<string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Aktivieren"</string>
- <string name="battery_saver_start_action" msgid="4553256017945469937">"Stromsparmodus aktivieren"</string>
+ <string name="battery_saver_start_action" msgid="4553256017945469937">"Energiesparmodus aktivieren"</string>
<string name="status_bar_settings_settings_button" msgid="534331565185171556">"Einstellungen"</string>
<string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"WLAN"</string>
<string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Bildschirm automatisch drehen"</string>
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Versuche noch einmal, den Screenshot zu erstellen"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Speichern des Screenshots aufgrund von zu wenig Speicher nicht möglich"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die App oder deine Organisation lässt das Erstellen von Screenshots nicht zu"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Screenshot schließen"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshotvorschau"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Bildschirmaufzeichnung"</string>
@@ -419,7 +421,7 @@
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"Bis <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Dunkles Design"</string>
- <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Stromsparmodus"</string>
+ <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Energiesparmodus"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"An bei Sonnenuntergang"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Bis Sonnenaufgang"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -497,9 +499,9 @@
<string name="user_remove_user_title" msgid="9124124694835811874">"Nutzer entfernen?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Alle Apps und Daten dieses Nutzers werden gelöscht."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Entfernen"</string>
- <string name="battery_saver_notification_title" msgid="8419266546034372562">"Stromsparmodus ist aktiviert"</string>
+ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Energiesparmodus ist aktiviert"</string>
<string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduzierung der Leistung und Hintergrunddaten"</string>
- <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Stromsparmodus deaktivieren"</string>
+ <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Energiesparmodus deaktivieren"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"Die App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise angezeigte Passwörter und Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Der Anbieter dieser App erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Aufnahme oder Stream starten?"</string>
@@ -773,8 +775,8 @@
<item quantity="one">%d Minute</item>
</plurals>
<string name="battery_panel_title" msgid="5931157246673665963">"Akkunutzung"</string>
- <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Stromsparmodus ist beim Aufladen nicht verfügbar."</string>
- <string name="battery_detail_switch_title" msgid="6940976502957380405">"Stromsparmodus"</string>
+ <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Energiesparmodus ist beim Aufladen nicht verfügbar."</string>
+ <string name="battery_detail_switch_title" msgid="6940976502957380405">"Energiesparmodus"</string>
<string name="battery_detail_switch_summary" msgid="3668748557848025990">"Reduzierung der Leistung und Hintergrunddaten"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Pos1"</string>
@@ -961,11 +963,11 @@
<string name="slice_permission_checkbox" msgid="4242888137592298523">"<xliff:g id="APP">%1$s</xliff:g> darf Teile aus jeder beliebigen App anzeigen"</string>
<string name="slice_permission_allow" msgid="6340449521277951123">"Zulassen"</string>
<string name="slice_permission_deny" msgid="6870256451658176895">"Ablehnen"</string>
- <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Stromsparmodus"</string>
+ <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Energiesparmodus"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Aktivieren, wenn der Akku wahrscheinlich nicht mehr lange hält"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nein danke"</string>
- <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Stromsparmodus aktiviert"</string>
- <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Stromsparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
+ <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Energiesparmodus aktiviert"</string>
+ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Energiesparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"Einstellungen"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"Ok"</string>
<string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Einstellungen"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Vergrößerungsfenster"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Einstellungen für Vergrößerungsfenster"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Heranzoomen"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Herauszoomen"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Nach oben bewegen"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Nach unten bewegen"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Nach links bewegen"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Nach rechts bewegen"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Vergrößerungsschalter"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ganzen Bildschirm vergrößern"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Teil des Bildschirms vergrößern"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Schalter"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Gerätesteuerung"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Steuerelemente für verbundene Geräte hinzufügen"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Gerätesteuerung einrichten"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Empfehlungen werden geladen"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Medien"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Du kannst die aktuelle Sitzung ausblenden."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ablehnen"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Fortsetzen"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Einstellungen"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (nicht verbunden)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Verbindung nicht möglich. Versuch es noch einmal."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Neues Gerät koppeln"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build-Nummer"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Build-Nummer in Zwischenablage kopiert."</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 6d86e91..645ff08 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Δοκιμάστε να κάνετε ξανά λήψη του στιγμιότυπου οθόνης"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Αδύνατη η αποθήκευση του στιγμιότυπου οθόνης λόγω περιορισμένου αποθηκευτικού χώρου"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Η λήψη στιγμιότυπων οθόνης δεν επιτρέπεται από την εφαρμογή ή τον οργανισμό σας"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Παράβλεψη στιγμιότυπου οθόνης"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Προεπισκόπηση στιγμιότυπου οθόνης"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Εγγραφή οθόνης"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ρυθμίσεις"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Παράθυρο μεγέθυνσης"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Στοιχεία ελέγχου παραθύρου μεγέθυνσης"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Μεγέθυνση"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Σμίκρυνση"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Μετακίνηση επάνω"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Μετακίνηση κάτω"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Μετακίνηση αριστερά"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Μετακίνηση δεξιά"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Εναλλαγή μεγιστοποίησης"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Μεγέθυνση ολόκληρης της οθόνης"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Μεγέθυνση μέρους της οθόνης"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Εναλλαγή"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Στοιχεία ελέγχου συσκευής"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Προσθήκη στοιχείων ελέγχου για τις συνδεδεμένες συσκευές σας."</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Ρύθμιση στοιχείων ελέγχου συσκευής"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Φόρτωση προτάσεων"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Μέσα"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Απόκρυψη της τρέχουσας περιόδου λειτουργίας."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Παράβλεψη"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Συνέχιση"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Ρυθμίσεις"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (αποσυνδέθηκε)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Δεν ήταν δυνατή η σύνδεση. Δοκιμάστε ξανά."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Σύζευξη νέας συσκευής"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Αριθμός έκδοσης"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Ο αριθμός έκδοσης αντιγράφηκε στο πρόχειρο."</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index cb03d40..27824ec 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
@@ -1018,6 +1020,10 @@
<string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string>
<string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string>
<string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Magnify entire screen"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string>
@@ -1059,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 8e5849e..012e76a3 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
@@ -1018,6 +1020,10 @@
<string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string>
<string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string>
<string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Magnify entire screen"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string>
@@ -1059,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index cb03d40..27824ec 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
@@ -1018,6 +1020,10 @@
<string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string>
<string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string>
<string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Magnify entire screen"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string>
@@ -1059,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index cb03d40..27824ec 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
@@ -1018,6 +1020,10 @@
<string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string>
<string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string>
<string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Magnify entire screen"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string>
@@ -1059,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index e107ed5..78006fd 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -86,6 +86,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organization"</string>
+ <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string>
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string>
@@ -1018,6 +1019,10 @@
<string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string>
<string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string>
<string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Magnify entire screen"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string>
@@ -1059,6 +1064,7 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string>
+ <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string>
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index be37623..2b5e96f 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Vuelve a hacer una captura de pantalla"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"No se puede guardar la captura de pantalla debido a que no hay suficiente espacio de almacenamiento"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"La app o tu organización no permiten las capturas de pantalla"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Descartar captura de pantalla"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de la captura de pantalla"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Grabadora de pantalla"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configuración"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Ventana de ampliación"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Controles de ampliación de la ventana"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Acercar"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Alejar"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover hacia arriba"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover hacia abajo"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover hacia la izquierda"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover hacia la derecha"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Botón de ampliación"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ampliar toda la pantalla"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Botón"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controles de dispositivos"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Agrega controles para los dispositivos conectados"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar controles de dispositivos"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Contenido multimedia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Oculta la sesión actual."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Descartar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconectado)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"No se pudo establecer la conexión. Vuelve a intentarlo."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo nuevo"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Se copió el número de compilación en el portapapeles."</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 08b17cc..b0377fd 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Vuelve a intentar hacer la captura de pantalla"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"No se puede guardar la captura de pantalla porque no hay espacio de almacenamiento suficiente"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"La aplicación o tu organización no permiten realizar capturas de pantalla"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Cerrar captura de pantalla"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de captura de pantalla"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Grabación de pantalla"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ajustes"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Ventana de ampliación"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Ventana de controles de ampliación"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Ampliar"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Reducir"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover hacia arriba"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover hacia abajo"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover hacia la izquierda"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover hacia la derecha"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Botón para cambiar el modo de ampliación"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ampliar toda la pantalla"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Cambiar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Control de dispositivos"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Añade controles para tus dispositivos conectados"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar control de dispositivos"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar la sesión."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Cerrar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Ajustes"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconectado)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"No se ha podido conectar. Inténtalo de nuevo."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular nuevo dispositivo"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Número de compilación copiado en el portapapeles."</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index b33ad01..a22e12b 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Proovige ekraanipilt uuesti jäädvustada"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Piiratud salvestusruumi tõttu ei saa ekraanipilti salvestada"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Rakendus või teie organisatsioon ei luba ekraanipilte jäädvustada"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Sule ekraanipilt"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekraanipildi eelvaade"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekraanisalvesti"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Seaded"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Suurendamisaken"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Suurendamisakna juhtelemendid"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Suumi sisse"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Suumi välja"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Teisalda üles"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Teisalda alla"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Teisalda vasakule"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Teisalda paremale"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Suurenduse lüliti"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Kogu ekraanikuva suurendamine"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekraanikuva osa suurendamine"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Vaheta"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Seadmete juhikud"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Lisage juhtelemendid ühendatud seadmete jaoks"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Seadmete juhtimisvidinate seadistamine"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Soovituste laadimine"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Meedia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Peidetakse praegune seanss."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Loobu"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Jätka"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Seaded"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (pole ühendatud)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ühenduse loomine ebaõnnestus. Proovige uuesti."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uue seadme sidumine"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Järgunumber"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Järgunumber kopeeriti lõikelauale."</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 3f1a53c..f6ca697 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Saiatu berriro pantaila-argazkia ateratzen"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ezin da gorde pantaila-argazkia ez delako gelditzen tokirik"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikazioak edo erakundeak ez du onartzen pantaila-argazkiak ateratzea"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Baztertu pantaila-argazkia"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pantaila-argazkiaren aurrebista"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Pantaila-grabagailua"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ezarpenak"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Lupa-leihoa"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Lupa-leihoaren aukerak"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Handitu"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Txikitu"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Eraman gora"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Eraman behera"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Eraman ezkerrera"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Eraman eskuinera"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Lupa aplikatzeko botoia"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Handitu pantaila osoa"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Handitu pantailaren zati bat"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Botoia"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Gailuak kontrolatzeko widgetak"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Gehitu konektatutako gailuak kontrolatzeko widgetak"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfiguratu gailuak kontrolatzeko widgetak"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Gomendioak kargatzen"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Multimedia-edukia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ezkutatu uneko saioa."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Baztertu"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Berrekin"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Ezarpenak"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (deskonektatuta)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ezin izan da konektatu. Saiatu berriro."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parekatu beste gailu batekin"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Konpilazio-zenbakia"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Kopiatu da konpilazio-zenbakia arbelean."</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index e5606a2..4c913f6 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"دوباره نماگرفت بگیرید"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"به دلیل محدود بودن فضای ذخیرهسازی نمیتوان نماگرفت را ذخیره کرد"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"برنامه یا سازمان شما اجازه نمیدهند نماگرفت بگیرید."</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"رد کردن نماگرفت"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"پیشنمایش نماگرفت"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ضبطکننده صفحهنمایش"</string>
@@ -142,7 +144,7 @@
<string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"برای لغو راستیآزمایی ضربه بزنید"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"لطفاً دوباره امتحان کنید"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="3401633342366146535">"درحال جستجوی چهره"</string>
- <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"چهره احراز هویت شد"</string>
+ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"چهره اصالتسنجی شد"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"تأیید شد"</string>
<string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"برای تکمیل، روی تأیید ضربه بزنید"</string>
<string name="biometric_dialog_authenticated" msgid="7337147327545272484">"راستیآزماییشده"</string>
@@ -307,8 +309,8 @@
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"حالت کار روشن شد."</string>
<string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"صرفهجویی داده خاموش شد."</string>
<string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"صرفهجویی داده روشن شد."</string>
- <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="7608378211873807353">"«حریم خصوصی حسگر» خاموش است."</string>
- <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="4267393685085328801">"«حریم خصوصی حسگر» روشن است."</string>
+ <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="7608378211873807353">"«حریمخصوصی حسگر» خاموش است."</string>
+ <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="4267393685085328801">"«حریمخصوصی حسگر» روشن است."</string>
<string name="accessibility_brightness" msgid="5391187016177823721">"روشنایی نمایشگر"</string>
<string name="accessibility_ambient_display_charging" msgid="7725523068728128968">"درحال شارژ شدن"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5716594205739750015">"داده 2G-3G موقتاً متوقف شده است"</string>
@@ -431,7 +433,7 @@
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"شروع"</string>
<string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"توقف"</string>
<string name="media_seamless_remote_device" msgid="177033467332920464">"دستگاه"</string>
- <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"برای تغییر برنامهها، تند به بالا بکشید"</string>
+ <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"برای تغییر برنامهها، تند بهبالا بکشید"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"برای جابهجایی سریع میان برنامهها، به چپ بکشید"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تغییر وضعیت نمای کلی"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"شارژ کامل شد"</string>
@@ -450,8 +452,8 @@
<string name="keyguard_more_overflow_text" msgid="5819512373606638727">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="7248696377626341060">"اعلانهای کمتر فوری در زیر"</string>
<string name="notification_tap_again" msgid="4477318164947497249">"دوباره ضربه بزنید تا باز شود"</string>
- <string name="keyguard_unlock" msgid="8031975796351361601">"برای باز کردن، انگشتتان را تند به بالا بکشید"</string>
- <string name="keyguard_retry" msgid="886802522584053523">"برای امتحان مجدد، انگشتتان را تند به بالا بکشید"</string>
+ <string name="keyguard_unlock" msgid="8031975796351361601">"برای باز کردن، انگشتتان را تند بهبالا بکشید"</string>
+ <string name="keyguard_retry" msgid="886802522584053523">"برای امتحان مجدد، انگشتتان را تند بهبالا بکشید"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"این دستگاه به سازمان شما تعلق دارد"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"این دستگاه به <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> تعلق دارد"</string>
<string name="phone_hint" msgid="6682125338461375925">"انگشتتان را از نماد تلفن تند بکشید"</string>
@@ -740,7 +742,7 @@
<string name="feedback_promoted" msgid="8075757485407091976">"سیستمْ این اعلان را ارتقا داده است."</string>
<string name="feedback_demoted" msgid="5848066008939031913">"سیستمْ این اعلان را تنزل داده است."</string>
<string name="feedback_prompt" msgid="2278631214125128281">"این مورد درست بود؟"</string>
- <string name="feedback_response" msgid="4671729244976641339">"از بازخورد شما سپاسگذاریم!"</string>
+ <string name="feedback_response" msgid="4671729244976641339">"از بازخوردتان سپاسگزاریم!"</string>
<string name="feedback_ok" msgid="6481426753298857144">"تأیید"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"کنترلهای اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> باز شد"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"کنترلهای اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> بسته شد"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"تنظیمات"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"پنجره بزرگنمایی"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"کنترلهای پنجره بزرگنمایی"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"بزرگ کردن"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"کوچک کردن"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"انتقال به بالا"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"انتقال به پایین"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"انتقال به راست"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"انتقال به چپ"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"کلید درشتنمایی"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"درشتنمایی تمام صفحه"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"درشتنمایی بخشی از صفحه"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"کلید"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"کنترلهای دستگاه"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"افزودن کنترلها برای دستگاههای متصل"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"تنظیم کنترلهای دستگاه"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"درحال بار کردن توصیهها"</string>
<string name="controls_media_title" msgid="1746947284862928133">"رسانه"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"جلسه فعلی پنهان شود."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"رد کردن"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ازسرگیری"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"تنظیمات"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (اتصال قطع شد)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"متصل نشد. دوباره امتحان کنید."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"مرتبط کردن دستگاه جدید"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"شماره ساخت"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"شماره ساخت در بریدهدان کپی شد."</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index e00b50e..0ab407a 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Yritä ottaa kuvakaappaus uudelleen."</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kuvakaappauksen tallennus epäonnistui, sillä tallennustilaa ei ole riittävästi"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Sovellus tai organisaatio ei salli kuvakaappauksien tallentamista."</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Hylkää kuvakaappaus"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Kuvakaappauksen esikatselu"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Näytön tallentaja"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Asetukset"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Suurennusikkuna"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Suurennusikkunan ohjaimet"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Lähennä"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Loitonna"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Siirrä ylös"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Siirrä alas"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Siirrä vasemmalle"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Siirrä oikealle"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Suurennusvalinta"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Suurenna koko näyttö"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Suurenna osa näytöstä"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Vaihda"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Laitteiden hallinta"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Lisää ohjaimia yhdistettyjä laitteita varten"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Laitteiden hallinnan käyttöönotto"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ladataan suosituksia"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Piilota nykyinen käyttökerta."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ohita"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Jatka"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Asetukset"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (yhteys katkaistu)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ei yhteyttä. Yritä uudelleen."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Muodosta uusi laitepari"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Koontiversion numero"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Koontiversion numero kopioitu leikepöydälle"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 03e205d..1b7a308 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Essayez de faire une autre capture d\'écran"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'application ou votre organisation n\'autorise pas les saisies d\'écran"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Fermer la capture d\'écran"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Paramètres"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Commandes pour la fenêtre d\'agrandissement"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Effectuer un zoom avant"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Effectuer un zoom arrière"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Déplacer vers le haut"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Déplacer vers le bas"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Déplacer vers la gauche"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Déplacer vers la droite"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Commutateur d\'agrandissement"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Agrandir l\'écran entier"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Commutateur"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Commandes des appareils"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Ajoutez des commandes pour vos appareils connectés"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurer les commandes des appareils"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations…"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Commandes multimédias"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Fermer"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (déconnecté)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Impossible de se connecter. Réessayez."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un autre appareil"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de version"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Le numéro de version a été copié dans le presse-papiers."</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 82df63a..74a6861 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Essayez de nouveau de faire une capture d\'écran"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Les captures d\'écran ne sont pas autorisées par l\'application ni par votre organisation"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Fermer la capture d\'écran"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Paramètres"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Fenêtre des commandes d\'agrandissement"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Faire un zoom avant"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Faire un zoom arrière"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Déplacer vers le haut"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Déplacer vers le bas"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Déplacer vers la gauche"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Déplacer vers la droite"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Changer de mode d\'agrandissement"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Agrandir tout l\'écran"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Changer"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Commandes des appareils"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Ajouter des commandes pour vos appareils connectés"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurer les commandes des appareils"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Fermer"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (déconnecté)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Impossible de se connecter. Réessayez."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un nouvel appareil"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de build"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Numéro de build copié dans le presse-papiers."</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 9be451d..26cf07a 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Volve tentar crear unha captura de pantalla"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Non se puido gardar a captura de pantalla porque o espazo de almacenamento é limitado"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"A aplicación ou a túa organización non permite realizar capturas de pantalla"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora a captura de pantalla"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa da captura de pantalla"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravadora da pantalla"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configuración"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Ventá de superposición"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Controis de ampliación da ventá"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Achegar"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Afastar"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover cara arriba"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover cara abaixo"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover cara á esquerda"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover cara á dereita"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Interruptor do modo de ampliación"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Amplía toda a pantalla"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplía parte da pantalla"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Cambiar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Control de dispositivos"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Engade controis para os dispositivos conectados"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar o control de dispositivos"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendacións"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Contido multimedia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Oculta a sesión actual."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignorar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (dispositivo desconectado)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Non se puido establecer a conexión. Téntao de novo."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo novo"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Copiouse o número de compilación no portapapeis."</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 864d05f..acaa442 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ફરીથી સ્ક્રીનશૉટ લેવાનો પ્રયાસ કરો"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"મર્યાદિત સ્ટોરેજ સ્પેસને કારણે સ્ક્રીનશૉટ સાચવી શકાતો નથી"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ઍપ્લિકેશન કે તમારી સંસ્થા દ્વારા સ્ક્રીનશૉટ લેવાની મંજૂરી નથી"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"સ્ક્રીનશૉટ છોડી દો"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"સ્ક્રીનશૉટનો પ્રીવ્યૂ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"સ્ક્રીન રેકૉર્ડર"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"સેટિંગ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"વિસ્તૃતીકરણ વિંડો"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"વિસ્તૃતીકરણ વિંડોના નિયંત્રણો"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"મોટું કરો"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"નાનું કરો"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"ઉપર ખસેડો"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"નીચે ખસેડો"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"ડાબી બાજુ ખસેડો"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"જમણી બાજુ ખસેડો"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"મોટું કરવાની સુવિધાવાળી સ્વિચ"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"સંપૂર્ણ સ્ક્રીન મોટી કરો"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"સ્ક્રીનનો કોઈ ભાગ મોટો કરો"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"સ્વિચ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ડિવાઇસનાં નિયંત્રણો"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"તમારા કનેક્ટ કરેલા ડિવાઇસ માટે નિયંત્રણો ઉમેરો"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"ડિવાઇસનાં નિયંત્રણો સેટઅપ કરો"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"સુઝાવ લોડ કરી રહ્યાં છીએ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"મીડિયા"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"હાલનું સત્ર છુપાવો."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"છોડી દો"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ફરી શરૂ કરો"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"સેટિંગ"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ડિસ્કનેક્ટ થયેલું)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"કનેક્ટ કરી શકાયું નહીં. ફરી પ્રયાસ કરો."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"નવા ડિવાઇસ સાથે જોડાણ કરો"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"બિલ્ડ નંબર"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"બિલ્ડ નંબર ક્લિપબૉર્ડ પર કૉપિ કર્યો."</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index e11055c..d211dce 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट दोबारा लेने की कोशिश करें"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"मेमोरी कम होने की वजह से स्क्रीनशॉट सेव नहीं किया जा सका"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ऐप्लिकेशन या आपका संगठन स्क्रीनशॉट लेने की अनुमति नहीं देता"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रीनशॉट खारिज करें"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉट की झलक"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रिकॉर्डर"</string>
@@ -1014,18 +1016,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"सेटिंग"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"स्क्रीन को बड़ा करके दिखाने वाली विंडो"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"स्क्रीन को बड़ा करके दिखाने वाली विंडो के नियंत्रण"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ज़ूम इन करें"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ज़ूम आउट करें"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"ऊपर ले जाएं"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"नीचे ले जाएं"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"बाईं ओर ले जाएं"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"दाईं ओर ले जाएं"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ज़ूम करने की सुविधा वाला स्विच"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"पूरी स्क्रीन को ज़ूम करें"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीन के किसी हिस्से को ज़ूम करें"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"डिवाइस कंट्रोल"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"कनेक्ट किए गए डिवाइस के लिए कंट्रोल जोड़ें"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"डिवाइस कंट्रोल सेट अप करें"</string>
@@ -1067,6 +1067,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"सुझाव लोड हो रहे हैं"</string>
<string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"इस मीडिया सेशन को छिपाएं."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"खारिज करें"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"फिर से शुरू करें"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग"</string>
@@ -1089,8 +1091,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (डिसकनेक्ट किया गया)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"कनेक्ट नहीं किया जा सका. फिर से कोशिश करें."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नया डिवाइस जोड़ें"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर को क्लिपबोर्ड पर कॉपी किया गया."</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index d5033fa..b5671bf 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pokušajte ponovo napraviti snimku zaslona"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Zaslon nije snimljen zbog ograničenog prostora za pohranu"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ili vaša organizacija ne dopuštaju snimanje zaslona"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacivanje snimke zaslona"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimke zaslona"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snimač zaslona"</string>
@@ -1017,18 +1019,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Postavke"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Prozor za povećavanje"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za povećavanje"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Povećaj"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Smanji"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Premjesti gore"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Premjesti dolje"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Premjesti ulijevo"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Premjesti udesno"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prebacivanje povećavanja"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Povećaj cijeli zaslon"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povećaj dio zaslona"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prebacivanje"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodavanje kontrola za povezane uređaje"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Postavljanje kontrola uređaja"</string>
@@ -1071,6 +1071,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Sakrij trenutačnu sesiju."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string>
@@ -1093,8 +1095,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (nije povezano)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezivanje nije bilo moguće. Pokušajte ponovo."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Upari novi uređaj"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj međuverzije"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Broj međuverzije kopiran je u međuspremnik."</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 6d960e7..291976e 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Próbálja meg újra elkészíteni a képernyőképet"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nem menthet képernyőképet, mert kevés a tárhely"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Az alkalmazás vagy az Ön szervezete nem engedélyezi képernyőkép készítését"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Képernyőkép elvetése"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Képernyőkép előnézete"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Képernyőrögzítő"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Beállítások"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Nagyítás ablaka"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Nagyítási vezérlők ablaka"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Nagyítás"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Kicsinyítés"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mozgatás felfelé"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mozgatás lefelé"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mozgatás balra"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mozgatás jobbra"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Nagyításváltó"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Teljes képernyő nagyítása"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Képernyő bizonyos részének nagyítása"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Váltás"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Eszközvezérlők"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Vezérlők hozzáadása a csatlakoztatott eszközökhöz"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Eszközvezérlők beállítása"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Javaslatok betöltése…"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Média"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Jelenlegi munkamenet elrejtése."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Elvetés"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Folytatás"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Beállítások"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (leválasztva)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Sikertelen csatlakozás. Próbálja újra."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Új eszköz párosítása"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildszám"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Buildszám a vágólapra másolva."</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 9e746fa..4efef9c 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Փորձեք նորից"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Չհաջողվեց պահել սքրինշոթը անբավարար հիշողության պատճառով"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Հավելվածը կամ ձեր կազմակերպությունը չի թույլատրում սքրինշոթի ստացումը"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Փակել սքրինշոթը"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Սքրինշոթի նախադիտում"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Էկրանի տեսագրիչ"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Կարգավորումներ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Խոշորացման պատուհան"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Խոշորացման պատուհանի կառավարման տարրեր"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Մեծացնել"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Փոքրացնել"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Տեղափոխել վերև"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Տեղափոխել ներքև"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Տեղափոխել ձախ"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Տեղափոխել աջ"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Խոշորացման փոփոխություն"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Խոշորացնել ամբողջ էկրանը"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Խոշորացնել էկրանի որոշակի հատվածը"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Փոխել"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Սարքերի կառավարման տարրեր"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Ավելացրեք կառավարման տարրեր ձեր միացված սարքերի համար"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Սարքերի կառավարման տարրերի կարգավորում"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Բեռնման խորհուրդներ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Մեդիա"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Թաքցրեք ընթացիկ աշխատաշրջանը"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Փակել"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Շարունակել"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Կարգավորումներ"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (անջատված է)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Չհաջողվեց միանալ։ Նորից փորձեք։"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Նոր սարքի զուգակցում"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Կառուցման համարը"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Կառուցման համարը պատճենվեց սեղմատախտակին։"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 380e943..f219520 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Coba ambil screenshot lagi"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Tidak dapat menyimpan screenshot karena ruang penyimpanan terbatas"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Mengambil screenshot tidak diizinkan oleh aplikasi atau organisasi"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Menutup screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pratinjau screenshot"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Perekam Layar"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Setelan"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Jendela Pembesaran"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontrol Jendela Pembesaran"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Perbesar"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Perkecil"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Pindahkan ke atas"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pindahkan ke bawah"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pindahkan ke kiri"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pindahkan ke kanan"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Tombol pembesaran"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Perbesar seluruh layar"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Perbesar sebagian layar"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Alihkan"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrol perangkat"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Tambahkan kontrol untuk perangkat terhubung"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Siapkan kontrol perangkat"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuat rekomendasi"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Menyembunyikan sesi saat ini."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Tutup"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Lanjutkan"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Setelan"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (terputus)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Tidak dapat terhubung. Coba lagi."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sambungkan perangkat baru"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nomor versi"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Nomor versi disalin ke papan klip."</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index dd12ed6..8f1049f 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prófaðu að taka skjámynd aftur"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ekki tókst að vista skjámynd vegna takmarkaðs geymslupláss"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Forritið eða fyrirtækið þitt leyfir ekki skjámyndatöku"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Loka skjámynd"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Forskoðun skjámyndar"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skjáupptaka"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Stillingar"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Stækkunargluggi"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Stækkunarstillingar glugga"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Auka aðdrátt"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Minnka aðdrátt"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Færa upp"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Færa niður"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Færa til vinstri"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Færa til hægri"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Stækkunarrofi"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Stækka allan skjáinn"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Stækka hluta skjásins"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Rofi"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Tækjastjórnun"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Bæta við stýringum fyrir tengd tæki"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Setja upp tækjastjórnun"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Hleður tillögum"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Margmiðlunarefni"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Fela núverandi lotu."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Hunsa"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Halda áfram"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Stillingar"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (aftengt)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Tenging mistókst. Reyndu aftur."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Para nýtt tæki"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Útgáfunúmer smíðar"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Útgáfunúmer smíðar afritað á klippiborð."</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 851228f..a273ea8 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Riprova ad acquisire lo screenshot"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossibile salvare lo screenshot a causa dello spazio di archiviazione limitato"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'acquisizione di screenshot non è consentita dall\'app o dall\'organizzazione"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Anteprima screenshot"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Registrazione dello schermo"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Impostazioni"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Finestra ingrandimento"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Finestra controlli di ingrandimento"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Aumenta lo zoom"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Diminuisci lo zoom"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Sposta su"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Sposta giù"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sposta a sinistra"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sposta a destra"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Opzione Ingrandimento"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ingrandisci l\'intero schermo"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ingrandisci parte dello schermo"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Opzione"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controllo dei dispositivi"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Aggiungi controlli per i dispositivi connessi"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Configura il controllo dei dispositivi"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Caricamento dei consigli"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Contenuti multimediali"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Nascondi la sessione attuale."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignora"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Riprendi"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Impostazioni"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnesso)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Impossibile connettersi. Riprova."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Accoppia nuovo dispositivo"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero build"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Numero build copiato negli appunti."</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index d4ad45e..5ae10ad 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"יש לנסות שוב לבצע צילום מסך"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"לא היה מספיק מקום לשמור את צילום המסך"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"האפליקציה או הארגון שלך אינם מתירים ליצור צילומי מסך"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"סגירת צילום מסך"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"תצוגה מקדימה של צילום מסך"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"מקליט המסך"</string>
@@ -941,7 +943,7 @@
<string name="notification_channel_general" msgid="4384774889645929705">"הודעות כלליות"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"אחסון"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"טיפים"</string>
- <string name="instant_apps" msgid="8337185853050247304">"אפליקציות אינסטנט"</string>
+ <string name="instant_apps" msgid="8337185853050247304">"אפליקציות ללא התקנה"</string>
<string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> פועלת"</string>
<string name="instant_apps_message" msgid="6112428971833011754">"האפליקציה נפתחת בלי התקנה."</string>
<string name="instant_apps_message_with_help" msgid="1816952263531203932">"האפליקציה נפתחת בלי התקנה. אפשר להקיש כדי לקבל מידע נוסף."</string>
@@ -1022,18 +1024,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"הגדרות"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"חלון הגדלה"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"בקרות של חלון ההגדלה"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"התקרבות"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"התרחקות"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"הזזה למעלה"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"הזזה למטה"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"הזזה שמאלה"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"הזזה ימינה"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"מעבר למצב הגדלה"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"הגדלת כל המסך"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"הגדלת חלק מהמסך"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"מעבר"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"פקדי מכשירים"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"יש להוסיף פקדים למכשירים המחוברים"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"הגדרה של פקדי מכשירים"</string>
@@ -1077,6 +1077,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"בטעינת המלצות"</string>
<string name="controls_media_title" msgid="1746947284862928133">"מדיה"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"הסתרת הסשן הנוכחי."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"סגירה"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"המשך"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"הגדרות"</string>
@@ -1099,8 +1101,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (מנותק)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"לא ניתן היה להתחבר. יש לנסות שוב."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"התאמה של מכשיר חדש"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"מספר Build"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"מספר ה-Build הועתק ללוח."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 7547853..5d22c9a 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"スクリーンショットを撮り直してください"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"空き容量が足りないため、スクリーンショットを保存できません"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"スクリーンショットの作成はアプリまたは組織で許可されていません"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"スクリーンショットを閉じます"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"スクリーンショットのプレビュー"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"スクリーン レコーダー"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"設定"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"拡大ウィンドウ"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"拡大ウィンドウ コントロール"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"拡大"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"縮小"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"上に移動"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"下に移動"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"左に移動"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"右に移動"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"拡大スイッチ"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"画面全体を拡大します"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"画面の一部を拡大します"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"スイッチ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"デバイス コントロール"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"接続済みデバイスのコントロールを追加します"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"デバイス コントロールの設定"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"候補を読み込んでいます"</string>
<string name="controls_media_title" msgid="1746947284862928133">"メディア"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"現在のセッションを非表示にします。"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"閉じる"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"再開"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>(未接続)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"接続できませんでした。もう一度お試しください。"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"新しいデバイスとのペア設定"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"ビルド番号"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"ビルド番号をクリップボードにコピーしました。"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 69b1d14..9bc0356 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ხელახლა ცადეთ ეკრანის ანაბეჭდის გაკეთება"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ეკრანის ანაბეჭდის შენახვა ვერ მოხერხდა შეზღუდული მეხსიერების გამო"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ეკრანის ანაბეჭდების შექმნა არ არის ნებადართული აპის ან თქვენი ორგანიზაციის მიერ"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ეკრანის ანაბეჭდის დახურვა"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ეკრანის ანაბეჭდის გადახედვა"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ეკრანის ჩამწერი"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"პარამეტრები"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"გადიდების ფანჯარა"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"გადიდების კონტროლის ფანჯარა"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"მასშტაბის გადიდება"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"მასშტაბის შემცირება"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"ზემოთ გადატანა"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ქვემოთ გადატანა"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"მარცხნივ გადატანა"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"მარჯვნივ გადატანა"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"გადიდების გადართვა"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"მთლიანი ეკრანის გადიდება"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ეკრანის ნაწილის გადიდება"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"გადართვა"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"მოწყობილ. მართვის საშუალებები"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"მართვის საშუალებების დამატება თქვენს დაკავშირებულ მოწყობილობებზე"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"მოწყობილობის მართვის საშუალებების დაყენება"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"მიმდინარეობს რეკომენდაციების ჩატვირთვა"</string>
<string name="controls_media_title" msgid="1746947284862928133">"მედია"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"დაიმალოს მიმდინარე სესია"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"დახურვა"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"გაგრძელება"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"პარამეტრები"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (კავშირი გაწყვეტილია)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"დაკავშირება ვერ მოხერხდა. ცადეთ ხელახლა."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ახალი მოწყობილობის დაწყვილება"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"ანაწყობის ნომერი"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"ანაწყობის ნომერი დაკოპირებულია გაცვლის ბუფერში."</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index b86f32e..68cd247 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Қайта скриншот жасап көріңіз"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Жадтағы шектеулі бос орынға байланысты скриншот сақталмайды"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Қолданба немесе ұйым скриншоттар түсіруге рұқсат етпейді"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Скриншотты жабу"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотты алдын ала қарау"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Экран жазғыш"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Параметрлер"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Ұлғайту терезесі"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Ұлғайту терезесінің басқару элементтері"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Ұлғайту"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Кішірейту"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Жоғары қарай жылжыту"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Төмен қарай жылжыту"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Солға жылжыту"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Оңға жылжыту"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Ұлғайту режиміне ауыстырғыш"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Толық экранды ұлғайту"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экранның бөлігін ұлғайту"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Ауысу"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Құрылғыны басқару элементтері"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Жалғанған құрылғылар үшін басқару виджеттерін қосу"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Құрылғыны басқару элементтерін реттеу"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Жүктеуге қатысты ұсыныстар"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Мультимедиа"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ағымдағы сеансты жасыру"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Жабу"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Жалғастыру"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Параметрлер"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ажыратылған)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Қосылмады. Қайта қосылып көріңіз."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңа құрылғыны жұптау"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Құрама нөмірі"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Құрама нөмірі буферге көшірілді."</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index e895140..55f3f44 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"សាកល្បងថតរូបថតអេក្រង់ម្តងទៀត"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"មិនអាចរក្សាទុករូបថតអេក្រង់បានទេ ដោយសារទំហំផ្ទុកមានកម្រិតទាប"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ការថតរូបអេក្រង់មិនត្រូវបានអនុញ្ញាតដោយកម្មវិធីនេះ ឬស្ថាប័នរបស់អ្នក"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ច្រានចោលរូបថតអេក្រង់"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ការមើលរូបថតអេក្រង់សាកល្បង"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"មុខងារថតអេក្រង់"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ការកំណត់"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"វិនដូការពង្រីក"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"វិនដូគ្រប់គ្រងការពង្រីក"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ពង្រីក"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"បង្រួម"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"ផ្លាស់ទីឡើងលើ"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ផ្លាស់ទីចុះក្រោម"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"ផ្លាស់ទីទៅឆ្វេង"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"ផ្លាស់ទីទៅស្តាំ"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ប៊ូតុងបិទបើកការពង្រីក"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ពង្រីកអេក្រង់ទាំងមូល"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ពង្រីកផ្នែកនៃអេក្រង់"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ប៊ូតុងបិទបើក"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ផ្ទាំងគ្រប់គ្រងឧបករណ៍"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"បញ្ចូលផ្ទាំងគ្រប់គ្រងសម្រាប់ឧបករណ៍ដែលអ្នកបានភ្ជាប់"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"រៀបចំផ្ទាំងគ្រប់គ្រងឧបករណ៍"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"កំពុងផ្ទុកការណែនាំ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"មេឌៀ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"លាក់វគ្គបច្ចុប្បន្ន។"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ច្រានចោល"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"បន្ត"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ការកំណត់"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (បានផ្ដាច់)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"មិនអាចភ្ជាប់បានទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ផ្គូផ្គងឧបករណ៍ថ្មី"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"លេខកំណែបង្កើត"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"បានចម្លងលេខកំណែបង្កើតទៅឃ្លីបបត។"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 2a9f649..569837b 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಅನ್ನು ಪುನಃ ತೆಗೆದುಕೊಳ್ಳಲು ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ಪರಿಮಿತ ಸಂಗ್ರಹಣೆ ಸ್ಥಳದ ಕಾರಣದಿಂದಾಗಿ ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ಅಪ್ಲಿಕೇಶನ್ ಅಥವಾ ಸಂಸ್ಥೆಯು ಸ್ಕ್ರೀನ್ಶಾಟ್ಗಳನ್ನು ತೆಗೆಯುವುದನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಅನ್ನು ವಜಾಗೊಳಿಸಿ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ಸ್ಕ್ರೀನ್ಶಾಟ್ನ ಪೂರ್ವವೀಕ್ಷಣೆ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡರ್"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"ವರ್ಧನೆಯ ವಿಂಡೋ"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"ವರ್ಧನೆಯ ವಿಂಡೋ ನಿಯಂತ್ರಣಗಳು"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ಝೂಮ್ ಇನ್ ಮಾಡಿ"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ಝೂಮ್ ಔಟ್ ಮಾಡಿ"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"ಮೇಲೆಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ಕೆಳಗೆ ಸರಿಸಿ"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"ಎಡಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"ಬಲಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ಝೂಮ್ ಮಾಡುವ ಸ್ವಿಚ್"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ಸ್ಕ್ರೀನ್ನ ಸಂಪೂರ್ಣ ಭಾಗವನ್ನು ಝೂಮ್ ಮಾಡಿ"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ಸ್ಕ್ರೀನ್ನ ಅರ್ಧಭಾಗವನ್ನು ಝೂಮ್ ಮಾಡಿ"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ಸ್ವಿಚ್"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ಸಾಧನ ನಿಯಂತ್ರಣಗಳು"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"ನಿಮ್ಮ ಸಂಪರ್ಕಿತ ಸಾಧನಗಳಿಗೆ ನಿಯಂತ್ರಣಗಳನ್ನು ಸೇರಿಸಿ"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"ಸಾಧನ ನಿಯಂತ್ರಣಗಳನ್ನು ಸೆಟಪ್ ಮಾಡಿ"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ಶಿಫಾರಸುಗಳು ಲೋಡ್ ಆಗುತ್ತಿವೆ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"ಮಾಧ್ಯಮ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"ಪ್ರಸ್ತುತ ಸೆಶನ್ ಅನ್ನು ಮರೆಮಾಡಿ."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ವಜಾಗೊಳಿಸಿ"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ಪುನರಾರಂಭಿಸಿ"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಿ"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"ಬಿಲ್ಡ್ ಸಂಖ್ಯೆ"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"ಬಿಲ್ಡ್ ಸಂಖ್ಯೆಯನ್ನು ಕ್ಲಿಪ್ಬೋರ್ಡ್ನಲ್ಲಿ ನಕಲಿಸಲಾಗಿದೆ."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 559205c..1fdff74 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"스크린샷을 다시 찍어 보세요."</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"저장용량이 부족하여 스크린샷을 저장할 수 없습니다"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"앱이나 조직에서 스크린샷 촬영을 허용하지 않습니다."</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"스크린샷 닫기"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"스크린샷 미리보기"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"화면 녹화"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"설정"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"확대 창"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"확대 창 컨트롤"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"확대"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"축소"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"위로 이동"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"아래로 이동"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"왼쪽으로 이동"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"오른쪽으로 이동"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"확대 전환"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"전체 화면 확대"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"화면 일부 확대"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"전환"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"기기 컨트롤"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"연결된 기기의 컨트롤을 추가하세요."</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"기기 컨트롤 설정"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"추천 제어 기능 로드 중"</string>
<string name="controls_media_title" msgid="1746947284862928133">"미디어"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"현재 세션을 숨깁니다."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"닫기"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"다시 시작"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"설정"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>(연결 끊김)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"연결할 수 없습니다. 다시 시도하세요."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"새 기기와 페어링"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"빌드 번호"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"빌드 번호가 클립보드에 복사되었습니다."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index a688150..970d5fa 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Скриншотту кайра тартып көрүңүз"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сактагычта бош орун аз болгондуктан, скриншот сакталбай жатат"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Скриншот тартууга колдонмо же ишканаңыз тыюу салган."</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Скриншотту четке кагуу"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотту алдын ала көрүү"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"экрандан видео жаздырып алуу"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Жөндөөлөр"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Чоңойтуу терезеси"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Чоңойтуу терезесин башкаруу каражаттары"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Жакындатуу"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Алыстатуу"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Жогору жылдыруу"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Төмөн жылдыруу"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Солго жылдыруу"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Оңго жылдыруу"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Чоңойтуу режимине которулуу"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Толук экранды чоңойтуу"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экрандын бир бөлүгүн чоңойтуу"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Которулуу"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Түзмөктү башкаруу элементтери"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Байланышкан түзмөктөрүңүздү башкаруу элементтерин кошосуз"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Түзмөктү башкаруу элементтерин жөндөө"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Сунуштар жүктөлүүдө"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Учурдагы сеансты жашыруу."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Жабуу"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Улантуу"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Жөндөөлөр"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ажыратылды)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Байланышпай койду. Кайталоо."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңы түзмөктү жупташтыруу"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Курама номери"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Курама номери алмашуу буферине көчүрүлдү."</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 1a6c338..69d3443 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ກະລຸນາລອງຖ່າຍຮູບໜ້າຈໍອີກຄັ້ງ"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ບໍ່ສາມາດຖ່າຍຮູບໜ້າຈໍໄດ້ເນື່ອງຈາກພື້ນທີ່ຈັດເກັບຂໍ້ມູນມີຈຳກັດ"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ແອັບ ຫຼື ອົງກອນຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ຖ່າຍຮູບໜ້າຈໍ"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ປິດຮູບໜ້າຈໍ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ຕົວຢ່າງຮູບໜ້າຈໍ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ໂປຣແກຣມບັນທຶກໜ້າຈໍ"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ການຕັ້ງຄ່າ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"ໜ້າຈໍການຂະຫຍາຍ"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"ການຄວບຄຸມໜ້າຈໍການຂະຫຍາຍ"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ຊູມເຂົ້າ"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ຊູມອອກ"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"ຍ້າຍຂຶ້ນ"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ຍ້າຍລົງ"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"ຍ້າຍໄປຊ້າຍ"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"ຍ້າຍໄປຂວາ"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ສະຫຼັບການຂະຫຍາຍ"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ຂະຫຍາຍທັງໜ້າຈໍ"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ຂະຫຍາຍບາງສ່ວນຂອງໜ້າຈໍ"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ສະຫຼັບ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ການຄວບຄຸມອຸປະກອນ"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"ເພີ່ມການຄວບຄຸມສຳລັບອຸປະກອນທີ່ເຊື່ອມຕໍ່ແລ້ວຂອງທ່ານ"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"ຕັ້ງຄ່າການຄວບຄຸມອຸປະກອນ"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ກຳລັງໂຫຼດຄຳແນະນຳ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"ມີເດຍ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"ເຊື່ອງເຊດຊັນປັດຈຸບັນ."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ປິດໄວ້"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ສືບຕໍ່"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ການຕັ້ງຄ່າ"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ຕັດການເຊື່ອມຕໍ່ແລ້ວ)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້. ລອງໃໝ່."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ຈັບຄູ່ອຸປະກອນໃໝ່"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"ໝາຍເລກສ້າງ"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"ສຳເນົາໝາຍເລກສ້າງໄປໃສ່ຄລິບບອດແລ້ວ."</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 6fb84dd8..81696c2 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pabandykite padaryti ekrano kopiją dar kartą"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Negalima išsaugoti ekrano kopijos dėl ribotos saugyklos vietos"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Jūsų organizacijoje arba naudojant šią programą neleidžiama daryti ekrano kopijų"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Praleisti ekrano kopiją"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrano kopijos peržiūra"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekrano vaizdo įrašytuvas"</string>
@@ -1028,6 +1030,10 @@
<string name="accessibility_control_move_down" msgid="5390922476900974512">"Perkelti žemyn"</string>
<string name="accessibility_control_move_left" msgid="8156206978511401995">"Perkelti kairėn"</string>
<string name="accessibility_control_move_right" msgid="8926821093629582888">"Perkelti dešinėn"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Didinimo jungiklis"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Didinti visą ekraną"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Didinti ekrano dalį"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Perjungti"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Įrenginio valdikliai"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Pridėkite prijungtų įrenginių valdiklių"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Įrenginio valdiklių nustatymas"</string>
@@ -1071,6 +1077,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Įkeliamos rekomendacijos"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Medija"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Slėpti dabartinį seansą."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Atsisakyti"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Tęsti"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Nustatymai"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 9864a61..d37dd91 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Mēģiniet izveidot jaunu ekrānuzņēmumu."</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nevar saglabāt ekrānuzņēmumu, jo krātuvē nepietiek vietas."</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Lietotne vai jūsu organizācija neatļauj veikt ekrānuzņēmumus."</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Nerādīt ekrānuzņēmumu"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrānuzņēmuma priekšskatījums"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekrāna ierakstītājs"</string>
@@ -1017,18 +1019,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Iestatījumi"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Palielināšanas logs"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Palielināšanas loga vadīklas"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Tuvināt"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Tālināt"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Pārvietot uz augšu"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pārvietot uz leju"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pārvietot pa kreisi"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pārvietot pa labi"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Palielinājuma slēdzis"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Palielināt visu ekrānu"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Palielināt ekrāna daļu"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Pārslēgt"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Ierīču vadīklas"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Pievienojiet vadīklas pievienotajām ierīcēm"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Ierīču vadīklu iestatīšana"</string>
@@ -1071,6 +1071,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Notiek ieteikumu ielāde"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Multivide"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Paslēpiet pašreizējo sesiju."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Nerādīt"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Atsākt"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Iestatījumi"</string>
@@ -1093,8 +1095,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (savienojums pārtraukts)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nevarēja izveidot savienojumu. Mēģiniet vēlreiz."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Savienošana pārī ar jaunu ierīci"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versijas numurs"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Versijas numurs ir kopēts starpliktuvē."</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 362cbdc..28c4ee7 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Повторно обидете се да направите слика од екранот"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сликата од екранот не може да се зачува поради ограничена меморија"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Апликацијата или вашата организација не дозволува снимање слики од екранот"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Отфрлете ја сликата од екранот"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед на слика од екранот"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Снимач на екран"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Поставки"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Прозорец за зголемување"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Контроли на прозорец за зголемување"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Зумирај"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Одзумирај"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Премести нагоре"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Премести надолу"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Премести налево"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Премести надесно"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Прекинувач за зголемување"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Зголеми цел екран"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Зголеми дел од екранот"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Префрли"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Контроли за уредите"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Додајте контроли за поврзаните уреди"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Поставете ги контролите за уредите"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Се вчитуваат препораки"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Аудиовизуелни содржини"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Сокриј ја тековнава сесија."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Отфрли"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Продолжи"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Поставки"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (исклучен)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не можеше да се поврзе. Обидете се повторно."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спарете нов уред"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Број на верзија"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Бројот на верзијата е копиран во привремената меморија."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 95634b8..f942641 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"സ്ക്രീൻഷോട്ട് എടുക്കാൻ വീണ്ടും ശ്രമിക്കുക"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"സ്റ്റോറേജ് ഇടം പരിമിതമായതിനാൽ സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കാനാകുന്നില്ല"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"സ്ക്രീൻഷോട്ടുകൾ എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"സ്ക്രീൻഷോട്ട് ഡിസ്മിസ് ചെയ്യുക"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"സ്ക്രീൻഷോട്ട് പ്രിവ്യു"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"സ്ക്രീൻ റെക്കോർഡർ"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ക്രമീകരണം"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"മാഗ്നിഫിക്കേഷൻ വിൻഡോ"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"മാഗ്നിഫിക്കേഷൻ വിൻഡോ നിയന്ത്രണങ്ങൾ"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"സൂം ഇൻ ചെയ്യുക"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"സൂം ഔട്ട് ചെയ്യുക"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"മുകളിലേക്ക് നീക്കുക"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"താഴേക്ക് നീക്കുക"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"ഇടത്തേക്ക് നീക്കുക"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"വലത്തേക്ക് നീക്കുക"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"മാഗ്നിഫിക്കേഷൻ മോഡ് മാറുക"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"മുഴുവൻ സ്ക്രീനും മാഗ്നിഫൈ ചെയ്യുക"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"സ്ക്രീനിന്റെ ഭാഗം മാഗ്നിഫൈ ചെയ്യുക"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"മാറുക"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ഉപകരണ നിയന്ത്രണങ്ങൾ"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"കണക്റ്റ് ചെയ്ത ഉപകരണങ്ങൾക്ക് നിയന്ത്രണങ്ങൾ ചേർക്കുക"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"ഉപകരണ നിയന്ത്രണങ്ങൾ സജ്ജീകരിക്കുക"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"നിർദ്ദേശങ്ങൾ ലോഡ് ചെയ്യുന്നു"</string>
<string name="controls_media_title" msgid="1746947284862928133">"മീഡിയ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"നിലവിലെ സെഷൻ മറയ്ക്കുക."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ഡിസ്മിസ് ചെയ്യുക"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"പുനരാരംഭിക്കുക"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ക്രമീകരണം"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (വിച്ഛേദിച്ചു)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"കണക്റ്റ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"പുതിയ ഉപകരണവുമായി ജോടിയാക്കുക"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"ബിൽഡ് നമ്പർ"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"ക്ലിപ്പ്ബോർഡിലേക്ക് ബിൽഡ് നമ്പർ പകർത്തി."</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index a2e1745..fc41f6c 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Дэлгэцийн зургийг дахин дарж үзнэ үү"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сангийн багтаамж бага байгаа тул дэлгэцээс дарсан зургийг хадгалах боломжгүй байна"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Таны апп, байгууллагад дэлгэцийн зураг авахыг зөвшөөрдөггүй"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Дэлгэцийн агшныг хаах"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Дэлгэцийн агшныг урьдчилан үзэх"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Дэлгэцийн үйлдэл бичигч"</string>
@@ -549,29 +551,29 @@
<string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Таны байгууллага таны ажлын профайлд сертификатын зөвшөөрөл суулгасан байна. Таны аюулгүй сүлжээний ачааллыг өөрчлөх эсвэл хянах боломжтой."</string>
<string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Сертификатын зөвшөөрлийг энэ төхөөрөмжид суулгасан байна. Таны аюулгүй сүлжээний ачааллыг өөрчлөх эсвэл хянах боломжтой."</string>
<string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Таны админ төхөөрөмжийн ачааллыг хянадаг сүлжээний логийг асаасан байна."</string>
- <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Та имэйл, апп, вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна."</string>
- <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Та имэйл, апп, вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP_0">%1$s</xliff:g>, <xliff:g id="VPN_APP_1">%2$s</xliff:g>-д холбогдсон байна."</string>
- <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Таны ажлын профайл <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна. Энэ нь таны имэйл, апп, вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой."</string>
- <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"Таны хувийн профайлыг имэйл, апп, вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбосон байна."</string>
+ <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Та имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна."</string>
+ <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Та имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP_0">%1$s</xliff:g>, <xliff:g id="VPN_APP_1">%2$s</xliff:g>-д холбогдсон байна."</string>
+ <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Таны ажлын профайл <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна. Энэ нь таны имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой."</string>
+ <string name="monitoring_description_personal_profile_named_vpn" msgid="8179722332380953673">"Таны хувийн профайлыг имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбосон байна."</string>
<string name="monitoring_description_do_header_generic" msgid="6130190408164834986">"Таны төхөөрөмжийг <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> удирддаг."</string>
<string name="monitoring_description_do_header_with_name" msgid="2696255132542779511">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> таны төхөөрөмжийг удирдахын тулд <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g>-г ашигладаг."</string>
<string name="monitoring_description_do_body" msgid="7700878065625769970">"Таны админ тохиргоо, байгууллагын хандалт, апп, төхөөрөмжтэй холбоотой өгөгдөл болон таны төхөөрөмжийн байршлын мэдээллийг хянах, удирдах боломжтой."</string>
<string name="monitoring_description_do_learn_more_separator" msgid="1467280496376492558">" "</string>
<string name="monitoring_description_do_learn_more" msgid="645149183455573790">"Дэлгэрэнгүй үзэх"</string>
- <string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"Таны имэйл, апп, вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна."</string>
+ <string name="monitoring_description_do_body_vpn" msgid="7699280130070502303">"Таны имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна."</string>
<string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string>
<string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"VPN тохиргоог нээх"</string>
<string name="monitoring_description_ca_cert_settings_separator" msgid="7107390013344435439">" "</string>
<string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"Итгэмжлэгдсэн мандат үнэмлэхийг нээх"</string>
<string name="monitoring_description_network_logging" msgid="577305979174002252">"Таны админ төхөөрөмжийн ачааллыг хянадаг сүлжээний логийг асаасан байна.\n\nДэлгэрэнгүй мэдээлэл авах бол админтайгаа холбогдоно уу."</string>
- <string name="monitoring_description_vpn" msgid="1685428000684586870">"Та апп-д VPN холболт хийхийг зөвшөөрсөн байна.\n\nЭнэхүү апп нь таны имэйл, апп, вэбсайт зэрэг төхөөрөмж болон сүлжээний үйл ажиллагааг хянах боломжтой."</string>
- <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> таны ажлын профайлыг удирддаг.\n\nТаны админ имэйл, апп болон вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой.\n\nДэлгэрэнгүй мэдээлэл авах бол админтайгаа холбогдоно уу.\n\nТа сүлжээний үйл ажиллагааг хянах боломжтой VPN-д холбогдсон байна."</string>
+ <string name="monitoring_description_vpn" msgid="1685428000684586870">"Та апп-д VPN холболт хийхийг зөвшөөрсөн байна.\n\nЭнэхүү апп нь таны имэйл, апп, вебсайт зэрэг төхөөрөмж болон сүлжээний үйл ажиллагааг хянах боломжтой."</string>
+ <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> таны ажлын профайлыг удирддаг.\n\nТаны админ имэйл, апп болон веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой.\n\nДэлгэрэнгүй мэдээлэл авах бол админтайгаа холбогдоно уу.\n\nТа сүлжээний үйл ажиллагааг хянах боломжтой VPN-д холбогдсон байна."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
- <string name="monitoring_description_app" msgid="376868879287922929">"Та <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон бөгөөд энэ нь таны имэйл, апп, вэбсайт зэрэг сүлжээний үйл ажиллагааг хянах боломжтой."</string>
- <string name="monitoring_description_app_personal" msgid="1970094872688265987">"Та <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон бөгөөд энэ нь таны имэйл, апп, вэбсайт зэрэг сүлжээний хувийн үйл ажиллагааг хянах боломжтой."</string>
- <string name="branded_monitoring_description_app_personal" msgid="1703511985892688885">"Та имэйл, апп, вэб хуудас зэрэг хувийн сүлжээнийхээ үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон байна."</string>
- <string name="monitoring_description_app_work" msgid="3713084153786663662">"Таны ажлын профайлыг <xliff:g id="ORGANIZATION">%1$s</xliff:g> удирддаг. Энэ нь таны имэйл, апп, вэб хуудас зэрэг ажлын сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION">%2$s</xliff:g>-д холбогдсон. \n\nДэлгэрэнгүй мэдээллийг авахын тулд админтай холбогдоно уу."</string>
- <string name="monitoring_description_app_personal_work" msgid="6175816356939166101">"Таны ажлын профайлыг <xliff:g id="ORGANIZATION">%1$s</xliff:g> удирддаг. Энэ нь таны имэйл, апп, вэб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>-тай холбогдсон. \n\nМөн таны сүлжээний хувийн үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>-д холбогдсон байна."</string>
+ <string name="monitoring_description_app" msgid="376868879287922929">"Та <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон бөгөөд энэ нь таны имэйл, апп, вебсайт зэрэг сүлжээний үйл ажиллагааг хянах боломжтой."</string>
+ <string name="monitoring_description_app_personal" msgid="1970094872688265987">"Та <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон бөгөөд энэ нь таны имэйл, апп, вебсайт зэрэг сүлжээний хувийн үйл ажиллагааг хянах боломжтой."</string>
+ <string name="branded_monitoring_description_app_personal" msgid="1703511985892688885">"Та имэйл, апп, веб хуудас зэрэг хувийн сүлжээнийхээ үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION">%1$s</xliff:g>-д холбогдсон байна."</string>
+ <string name="monitoring_description_app_work" msgid="3713084153786663662">"Таны ажлын профайлыг <xliff:g id="ORGANIZATION">%1$s</xliff:g> удирддаг. Энэ нь таны имэйл, апп, веб хуудас зэрэг ажлын сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION">%2$s</xliff:g>-д холбогдсон. \n\nДэлгэрэнгүй мэдээллийг авахын тулд админтай холбогдоно уу."</string>
+ <string name="monitoring_description_app_personal_work" msgid="6175816356939166101">"Таны ажлын профайлыг <xliff:g id="ORGANIZATION">%1$s</xliff:g> удирддаг. Энэ нь таны имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>-тай холбогдсон. \n\nМөн таны сүлжээний хувийн үйл ажиллагааг хянах боломжтой <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>-д холбогдсон байна."</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent-р түгжээгүй байлгасан"</string>
<string name="keyguard_indication_trust_disabled" msgid="6820793704816727918">"Таныг гараар нээх хүртэл төхөөрөмж түгжээтэй байх болно"</string>
<string name="keyguard_indication_trust_unlocked_plugged_in" msgid="2323452175329362855">"<xliff:g id="KEYGUARD_INDICATION">%1$s</xliff:g>\n<xliff:g id="POWER_INDICATION">%2$s</xliff:g>"</string>
@@ -653,8 +655,8 @@
<string name="status_bar_alarm" msgid="87160847643623352">"Сэрүүлэг"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Ажлын профайл"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Нислэгийн горим"</string>
- <string name="add_tile" msgid="6239678623873086686">"Вэбсайтын цонх нэмэх"</string>
- <string name="broadcast_tile" msgid="5224010633596487481">"Вэбсайтын цонх дамжуулах"</string>
+ <string name="add_tile" msgid="6239678623873086686">"Вебсайтын цонх нэмэх"</string>
+ <string name="broadcast_tile" msgid="5224010633596487481">"Вебсайтын цонх дамжуулах"</string>
<string name="zen_alarm_warning_indef" msgid="5252866591716504287">"Та өмнө нь унтраагаагүй тохиолдолд <xliff:g id="WHEN">%1$s</xliff:g>-т сэрүүлгээ сонсохгүй"</string>
<string name="zen_alarm_warning" msgid="7844303238486849503">"<xliff:g id="WHEN">%1$s</xliff:g>-т та дараагийн сэрүүлгээ сонсохгүй"</string>
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> цагт"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Тохиргоо"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Томруулалтын цонх"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Томруулалтын цонхны хяналт"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Томруулах"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Жижигрүүлэх"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Дээш зөөх"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Доош зөөх"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Зүүн тийш зөөх"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Баруун тийш зөөх"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Томруулах сэлгэлт"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Дэлгэцийг бүхэлд нь томруулах"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Дэлгэцийн нэг хэсгийг томруулах"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Сэлгэх"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Төхөөрөмжийн хяналт"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Холбогдсон төхөөрөмжүүд дээрээ хяналт нэмэх"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Төхөөрөмжийн хяналтыг тохируулах"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Зөвлөмжүүдийг ачаалж байна"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Одоогийн харилцан үйлдлийг нуугаарай."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Хаах"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Үргэлжлүүлэх"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Тохиргоо"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (салсан)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Холбогдож чадсангүй. Дахин оролдоно уу."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Шинэ төхөөрөмж хослуулах"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Хийгдсэн дугаар"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Хийгдсэн дугаарыг түр санах ойд хуулсан."</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 7a62c17..2b01fef 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट पुन्हा घेण्याचा प्रयत्न करा"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"मर्यादित स्टोरेज जागेमुळे स्क्रीनशॉट सेव्ह करू शकत नाही"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"अॅप किंवा आपल्या संस्थेद्वारे स्क्रीनशॉट घेण्याची अनुमती नाही"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रीनशॉट डिसमिस करा"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉटचे पूर्वावलोकन"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रेकॉर्डर"</string>
@@ -265,7 +267,7 @@
<string name="accessibility_quick_settings_wifi" msgid="167707325133803052">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
<string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi बंद झाले."</string>
<string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi सुरू झाले."</string>
- <string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"मोबाईल <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
+ <string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"मोबाइल <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
<string name="accessibility_quick_settings_battery" msgid="533594896310663853">"बॅटरी <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_airplane_off" msgid="1275658769368793228">"विमान मोड बंद."</string>
<string name="accessibility_quick_settings_airplane_on" msgid="8106176561295294255">"विमान मोड सुरू."</string>
@@ -298,8 +300,8 @@
<string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"फ्लॅशलाइट सुरू केला."</string>
<string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"रंग उत्क्रमण बंद केले."</string>
<string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"रंग उत्क्रमण सुरू केले."</string>
- <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"मोबाईल हॉटस्पॉट बंद केला."</string>
- <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"मोबाईल हॉटस्पॉट सुरू केला."</string>
+ <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"मोबाइल हॉटस्पॉट बंद केला."</string>
+ <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"मोबाइल हॉटस्पॉट सुरू केला."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"स्क्रीन कास्ट करणे थांबले."</string>
<string name="accessibility_quick_settings_work_mode_off" msgid="562749867895549696">"कार्य मोड बंद."</string>
<string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"कार्य मोड सुरू."</string>
@@ -723,7 +725,7 @@
<string name="bubble_overflow_empty_title" msgid="3120029421991510842">"अलीकडील कोणतेही बबल नाहीत"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"अलीकडील बबल आणि डिसमिस केलेले बबल येथे दिसतील"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"या सूचनांमध्ये सुधारणा केली जाऊ शकत नाही."</string>
- <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉन्फिगर केला जाऊ शकत नाही"</string>
+ <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉंफिगर केला जाऊ शकत नाही"</string>
<string name="notification_delegate_header" msgid="1264510071031479920">"प्रॉक्सी केलेल्या सूचना"</string>
<string name="notification_channel_dialog_title" msgid="6856514143093200019">"सर्व <xliff:g id="APP_NAME">%1$s</xliff:g> वरील सूचना"</string>
<string name="see_more_title" msgid="7409317011708185729">"आणखी पाहा"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"सेटिंग्ज"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"मॅग्निफिकेशन विंडो"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"मॅग्निफिकेशन विंडो नियंत्रणे"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"झूम इन करा"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"झूम आउट करा"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"वर हलवा"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"खाली हलवा"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"डावीकडे हलवा"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"उजवीकडे हलवा"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"मॅग्निफिकेशन स्विच"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"संपूर्ण स्क्रीन मॅग्निफाय करा"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच करा"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"डिव्हाइस नियंत्रणे"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"तुमच्या कनेक्ट केलेल्या डिव्हाइससाठी नियंत्रणे जोडा"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"डिव्हाइस नियंत्रणे सेट करा"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"शिफारशी लोड करत आहे"</string>
<string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"सध्याचे सेशन लपवा."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"डिसमिस करा"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"पुन्हा सुरू करा"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग्ज"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (डिस्कनेक्ट केले)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"कनेक्ट करू शकलो नाही. पुन्हा प्रयत्न करा."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नवीन डिव्हाइससोबत पेअर करा"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर क्लिपबोर्डवर कॉपी केला."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 2b501bb..5b0f91f 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Cuba ambil tangkapan skrin sekali lagi"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Tidak dapat menyimpan tangkapan skrin kerana ruang storan terhad"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Pengambilan tangkapan skrin tidak dibenarkan oleh apl atau organisasi anda"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ketepikan tangkapan skrin"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pratonton tangkapan skrin"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Perakam Skrin"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Tetapan"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Tetingkap Pembesaran"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kawalan Tetingkap Pembesaran"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zum masuk"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zum keluar"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Alih ke atas"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Alih ke bawah"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Alih ke kiri"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Alih ke kanan"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Suis pembesaran"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Besarkan seluruh skrin"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Besarkan sebahagian skrin"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Tukar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kawalan peranti"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Tambah kawalan untuk peranti yang disambungkan"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Sediakan kawalan peranti"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuatkan cadangan"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Sembunyikan sesi semasa."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Tolak"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Sambung semula"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Tetapan"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (diputuskan sambungan)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Tidak boleh menyambung. Cuba lagi."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Gandingkan peranti baharu"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nombor binaan"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Nombor binaan disalin ke papan keratan."</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 1cbf17fe..b814607 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"မျက်နှာပြင်ပုံကို ထပ်ရိုက်ကြည့်ပါ"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"သိုလှောင်ခန်းနေရာ အကန့်အသတ်ရှိသောကြောင့် ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းဆည်း၍မရပါ"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ဖန်သားပြင်ဓာတ်ပုံရိုက်ကူးခြင်းကို ဤအက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ဖန်သားပြင်ဓာတ်ပုံ ပယ်ရန်"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ဖန်သားပြင်ဓာတ်ပုံ အစမ်းကြည့်ရှုခြင်း"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ဖန်သားပြင် ရိုက်ကူးမှု"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ဆက်တင်များ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"ဝင်းဒိုး ချဲ့ခြင်း"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"ဝင်းဒိုး ထိန်းချုပ်မှုများ ချဲ့ခြင်း"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ဇူးမ်ဆွဲရန်"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ဇူးမ်ဖြုတ်ရန်"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"အပေါ်သို့ရွှေ့ရန်"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"အောက်သို့ရွှေ့ရန်"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"ဘယ်ဘက်သို့ရွှေ့ရန်"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"ညာဘက်သို့ရွှေ့ရန်"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ချဲ့ရန် ခလုတ်"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ဖန်သားပြင် တစ်ခုလုံးကို ချဲ့ပါ"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ဖန်သားပြင် တစ်စိတ်တစ်ပိုင်းကို ချဲ့ပါ"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ခလုတ်"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"စက်ထိန်းစနစ်"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"ချိတ်ဆက်စက်များအတွက် ထိန်းချုပ်မှုများထည့်ပါ"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"စက်ထိန်းစနစ် ထည့်သွင်းခြင်း"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"အကြံပြုချက်များ ဖွင့်နေသည်"</string>
<string name="controls_media_title" msgid="1746947284862928133">"မီဒီယာ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"လက်ရှိ စက်ရှင်ကို ဖျောက်ထားမည်။"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ပယ်ရန်"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ဆက်လုပ်ရန်"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ဆက်တင်များ"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ချိတ်ဆက်မထားပါ)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ချိတ်ဆက်၍ မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"စက်အသစ် တွဲချိတ်ရန်"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"တည်ဆောက်မှုနံပါတ်"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"တည်ဆောက်မှုနံပါတ်ကို ကလစ်ဘုတ်သို့ မိတ္တူကူးပြီးပါပြီ။"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 6d6d66f..a5d548c 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prøv å ta skjermdump på nytt"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan ikke lagre skjermdumpen på grunn av begrenset lagringsplass"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller organisasjonen din tillater ikke at du tar skjermdumper"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Avvis skjermdumpen"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning av skjermdump"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skjermopptaker"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Innstillinger"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Forstørringsvindu"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontroller for forstørringsvindu"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom inn"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom ut"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Flytt opp"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Flytt ned"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Flytt til venstre"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Flytt til høyre"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Forstørringsbryter"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Forstørr hele skjermen"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstørr en del av skjermen"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Bytt"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Enhetsstyring"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Legg til kontroller for de tilkoblede enhetene dine"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfigurer enhetsstyring"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laster inn anbefalinger"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Medier"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den nåværende økten."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Lukk"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Gjenoppta"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Innstillinger"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (frakoblet)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Kunne ikke koble til. Prøv på nytt."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Koble til en ny enhet"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delversjonsnummer"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Delversjonsnummeret er kopiert til utklippstavlen."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index e1dd8c0..5eea7fd 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रिनसट फेरि लिएर हेर्नुहोस्"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"भण्डारण ठाउँ सीमित भएका कारण स्क्रिनसट सुरक्षित गर्न सकिएन"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"उक्त एप वा तपाईंको संगठनले स्क्रिनसटहरू लिन दिँदैन"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रिनसट हटाउनुहोस्"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रिनसटको पूर्वावलोकन"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"स्क्रिन रेकर्डर"</string>
@@ -339,7 +341,7 @@
<string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
<string name="start_dreams" msgid="9131802557946276718">"स्क्रिन सेभर"</string>
<string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
- <string name="quick_settings_header_onboarding_text" msgid="1918085351115504765">"थप विकल्पहरूका लागि आइकनहरूमा छोइराख्नुहोस्"</string>
+ <string name="quick_settings_header_onboarding_text" msgid="1918085351115504765">"थप विकल्पहरूका लागि आइकनहरूमा टच एण्ड होल्ड गर्नुहोस्"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"बाधा नपुऱ्याउनुहोस्"</string>
<string name="quick_settings_dnd_priority_label" msgid="6251076422352664571">"प्राथमिकता मात्र"</string>
<string name="quick_settings_dnd_alarms_label" msgid="1241780970469630835">"अलार्महरू मात्र"</string>
@@ -366,7 +368,7 @@
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"स्थान बन्द छ"</string>
<string name="quick_settings_media_device_label" msgid="8034019242363789941">"मिडिया उपकरण"</string>
<string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string>
- <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"आपतकालीन कल मात्र"</string>
+ <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"आपत्कालीन कल मात्र"</string>
<string name="quick_settings_settings_label" msgid="2214639529565474534">"सेटिङहरू"</string>
<string name="quick_settings_time_label" msgid="3352680970557509303">"समय"</string>
<string name="quick_settings_user_label" msgid="1253515509432672496">"मलाई"</string>
@@ -591,16 +593,16 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"असक्षम पार्नुहोस्"</string>
<string name="accessibility_output_chooser" msgid="7807898688967194183">"आउटपुट यन्त्र बदल्नुहोस्"</string>
<string name="screen_pinning_title" msgid="9058007390337841305">"एप पिन गरिएको छ"</string>
- <string name="screen_pinning_description" msgid="8699395373875667743">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र परिदृश्य बटनलाई छोइराख्नुहोस्।"</string>
- <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र गृह नामक बटनहरूलाई छोइराख्नुहोस्।"</string>
+ <string name="screen_pinning_description" msgid="8699395373875667743">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र परिदृश्य बटनलाई टच एण्ड होल्ड गर्नुहोस्।"</string>
+ <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र गृह नामक बटनहरूलाई टच एण्ड होल्ड गर्नुहोस्।"</string>
<string name="screen_pinning_description_gestural" msgid="7246323931831232068">"तपाईंले यो एप अनपिन नगरेसम्म यो एप यहाँ देखिइरहने छ। अनपिन गर्न माथितिर स्वाइप गरी होल्ड गर्नुहोस्।"</string>
- <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न परिदृश्य बटनलाई छोइराख्नुहोस्।"</string>
- <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न गृह नामक बटनलाई छोइराख्नुहोस्।"</string>
+ <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न परिदृश्य बटनलाई टच एण्ड होल्ड गर्नुहोस्।"</string>
+ <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न गृह नामक बटनलाई टच एण्ड होल्ड गर्नुहोस्।"</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"स्क्रिनमा व्यक्तिगत डेटा (जस्तै सम्पर्क ठेगाना र इमेलको सामग्री) देखिन सक्छ।"</string>
<string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"पिन गरिएको एपले अन्य एप खोल्न सक्छ।"</string>
- <string name="screen_pinning_toast" msgid="8177286912533744328">"यो एप अनपनि गर्न पछाडि र विवरण नामक बटनहरूलाई छोइराख्नुहोस्"</string>
- <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"यो एप अनपनि गर्न पछाडि र होम बटनलाई छोइराख्नुहोस्"</string>
- <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"यो एप अनपिन गर्न माथितिर स्वाइप गरी स्क्रिनमा छोइराख्नुहोस्"</string>
+ <string name="screen_pinning_toast" msgid="8177286912533744328">"यो एप अनपनि गर्न पछाडि र विवरण नामक बटनहरूलाई टच एण्ड होल्ड गर्नुहोस्"</string>
+ <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"यो एप अनपनि गर्न पछाडि र होम बटनलाई टच एण्ड होल्ड गर्नुहोस्"</string>
+ <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"यो एप अनपिन गर्न माथितिर स्वाइप गरी स्क्रिनमा टच एण्ड होल्ड गर्नुहोस्"</string>
<string name="screen_pinning_positive" msgid="3285785989665266984">"बुझेँ"</string>
<string name="screen_pinning_negative" msgid="6882816864569211666">"धन्यवाद पर्दैन"</string>
<string name="screen_pinning_start" msgid="7483998671383371313">"एप पिन गरियो"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"सेटिङ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"म्याग्निफिकेसन विन्डो"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"म्याग्निफिकेसन विन्डोका नियन्त्रणहरू"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"जुम इन गर्नुहोस्"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"जुम आउट गर्नुहोस्"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"माथि सार्नुहोस्"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"तल सार्नुहोस्"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"बायाँ सार्नुहोस्"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"दायाँ सार्नुहोस्"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"म्याग्निफिकेसन स्विच"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"पूरै स्क्रिन म्याग्निफाइ गर्नुहोस्"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रिनको केही भाग म्याग्निफाइ गर्नुहोस्"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"बदल्नुहोस्"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"यन्त्र नियन्त्रण गर्ने विजेटहरू"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"आफ्ना जोडिएका यन्त्रहरूका लागि नियन्त्रण सुविधाहरू थप्नुहोस्"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"यन्त्र नियन्त्रण गर्ने विजेटहरू सेटअप गर्नुहोस्"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"सिफारिसहरू लोड गर्दै"</string>
<string name="controls_media_title" msgid="1746947284862928133">"मिडिया"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"हालको सत्र लुकाउनुहोस्।"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"हटाउनुहोस्"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"सुचारु गर्नुहोस्"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिङ"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (डिस्कनेक्ट गरिएको)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"कनेक्ट गर्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नयाँ यन्त्रको जोडा बनाउनुहोस्"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नम्बर"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नम्बर कपी गरी क्लिपबोर्डमा सारियो।"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index e2db22c..2126b1a 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probeer opnieuw een screenshot te maken"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan screenshot niet opslaan vanwege beperkte opslagruimte"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Het maken van screenshots wordt niet toegestaan door de app of je organisatie"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Screenshot sluiten"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Voorbeeld van screenshot"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Schermopname"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Instellingen"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Vergrotingsvenster"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Bediening van vergrotingsvenster"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Inzoomen"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Uitzoomen"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Omhoog verplaatsen"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Omlaag verplaatsen"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Naar links verplaatsen"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Naar rechts verplaatsen"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Vergrotingsschakelaar"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Hele scherm vergroten"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Deel van het scherm vergroten"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Schakelen"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Apparaatbediening"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Bedieningselementen voor je gekoppelde apparaten toevoegen"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Apparaatbediening instellen"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Aanbevelingen laden"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"De huidige sessie verbergen."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Sluiten"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Hervatten"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Instellingen"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (verbinding verbroken)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Kan geen verbinding maken. Probeer het nog eens."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Nieuw apparaat koppelen"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build-nummer"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Build-nummer naar klembord gekopieerd."</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 970e61c..b55fd67 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ପୁଣିଥରେ ସ୍କ୍ରୀନ୍ଶଟ୍ ନେବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ସୀମିତ ଷ୍ଟୋରେଜ୍ ସ୍ପେସ୍ ହେତୁ ସ୍କ୍ରୀନଶଟ୍ ସେଭ୍ ହୋଇପାରିବ ନାହିଁ"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ଆପ୍ କିମ୍ବା ସଂସ୍ଥା ଦ୍ୱାରା ସ୍କ୍ରୀନଶଟ୍ ନେବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ସ୍କ୍ରିନସଟ୍ ଖାରଜ କରନ୍ତୁ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ସ୍କ୍ରିନସଟର ପ୍ରିଭ୍ୟୁ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ସ୍କ୍ରିନ୍ ରେକର୍ଡର୍"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ସେଟିଂସ୍"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ୱିଣ୍ଡୋ"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ୱିଣ୍ଡୋ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ଜୁମ୍ ଇନ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ଜୁମ୍ ଆଉଟ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"ଉପରକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ତଳକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"ବାମକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"ଡାହାଣକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ସ୍ୱିଚ୍"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ ମାଗ୍ନିଫାଏ କରନ୍ତୁ"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ସ୍କ୍ରିନର ଅଂଶ ମାଗ୍ନିଫାଏ କରନ୍ତୁ"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ସ୍ୱିଚ୍ କରନ୍ତୁ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ଡିଭାଇସ୍ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"ଆପଣଙ୍କ ସଂଯୁକ୍ତ ଡିଭାଇସଗୁଡ଼ିକ ପାଇଁ ନିୟନ୍ତ୍ରଣ ଯୋଗ କରନ୍ତୁ"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"ଡିଭାଇସ୍ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକୁ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ସୁପାରିଶଗୁଡ଼ିକ ଲୋଡ୍ କରାଯାଉଛି"</string>
<string name="controls_media_title" msgid="1746947284862928133">"ମିଡିଆ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"ବର୍ତ୍ତମାନର ସେସନ୍ ଲୁଚାନ୍ତୁ।"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ଖାରଜ କରନ୍ତୁ"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ସେଟିଂସ୍"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ବିଚ୍ଛିନ୍ନ କରାଯାଇଛି)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ସଂଯୋଗ କରାଯାଇପାରିଲା ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ନୂଆ ଡିଭାଇସକୁ ପେୟାର୍ କରନ୍ତୁ"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"ବିଲ୍ଡ ନମ୍ୱର"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"କ୍ଲିପବୋର୍ଡକୁ କପି କରାଯାଇଥିବା ବିଲ୍ଡ ନମ୍ୱର।"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 690d6e5b..9714f34 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਦੁਬਾਰਾ ਲੈ ਕੇ ਦੇਖੋ"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ਸੀਮਿਤ ਸਟੋਰੇਜ ਹੋਣ ਕਾਰਨ ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਸਕ੍ਰੀਨਸ਼ਾਟ ਲੈਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਹੈ"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਖਾਰਜ ਕਰੋ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਪੂਰਵ-ਝਲਕ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਰ"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ਸੈਟਿੰਗਾਂ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"ਵੱਡਦਰਸ਼ੀਕਰਨ Window"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"ਵੱਡਦਰਸ਼ੀਕਰਨ Window ਦੇ ਕੰਟਰੋਲ"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ਜ਼ੂਮ ਵਧਾਓ"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ਜ਼ੂਮ ਘਟਾਓ"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"ਉੱਪਰ ਲਿਜਾਓ"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ਹੇਠਾਂ ਲਿਜਾਓ"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"ਖੱਬੇ ਲਿਜਾਓ"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"ਸੱਜੇ ਲਿਜਾਓ"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ਵੱਡਦਰਸ਼ੀਕਰਨ ਸਵਿੱਚ"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ਸਾਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ਸਕ੍ਰੀਨ ਦੇ ਹਿੱਸੇ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ਸਵਿੱਚ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ਡੀਵਾਈਸ ਕੰਟਰੋਲ"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"ਆਪਣੇ ਕਨੈਕਟ ਕੀਤੇ ਡੀਵਾਈਸਾਂ ਲਈ ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"ਡੀਵਾਈਸ ਕੰਟਰੋਲਾਂ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"ਸਿਫ਼ਾਰਸ਼ਾਂ ਲੋਡ ਹੋ ਰਹੀਆਂ ਹਨ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"ਮੀਡੀਆ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਨੂੰ ਲੁਕਾਓ।"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ਖਾਰਜ ਕਰੋ"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"ਮੁੜ-ਚਾਲੂ ਕਰੋ"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ਸੈਟਿੰਗਾਂ"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ਡਿਸਕਨੈਕਟ ਹੋਇਆ)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"ਬਿਲਡ ਨੰਬਰ"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"ਬਿਲਡ ਨੰਬਰ ਨੂੰ ਕਲਿੱਪਬੋਰਡ \'ਤੇ ਕਾਪੀ ਕੀਤਾ ਗਿਆ।"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 9b31c52..f5bcdba 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Spróbuj jeszcze raz wykonać zrzut ekranu"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nie można zapisać zrzutu ekranu, bo brakuje miejsca w pamięci"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Nie możesz wykonać zrzutu ekranu, bo nie zezwala na to aplikacja lub Twoja organizacja."</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zamknij zrzut ekranu"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Podgląd zrzutu ekranu"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Nagrywanie ekranu"</string>
@@ -1022,18 +1024,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ustawienia"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Okno powiększenia"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Elementy sterujące okna powiększenia"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Powiększ"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Pomniejsz"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Przesuń w górę"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Przesuń w dół"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Przesuń w lewo"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Przesuń w prawo"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Przełączanie powiększenia"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Powiększ cały ekran"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Powiększ część ekranu"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Przełącz"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Sterowanie urządzeniami"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodaj elementy sterujące połączonymi urządzeniami"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfigurowanie sterowania urządzeniami"</string>
@@ -1077,6 +1077,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Wczytuję rekomendacje"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ukryj bieżącą sesję."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odrzuć"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Wznów"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Ustawienia"</string>
@@ -1099,8 +1101,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (rozłączono)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nie udało się połączyć. Spróbuj ponownie."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sparuj nowe urządzenie"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numer kompilacji"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Numer kompilacji został skopiowany do schowka."</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index b76ebda..0e09185 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Tente fazer a captura de tela novamente"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Não é possível salvar a captura de tela, porque não há espaço suficiente"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"O app ou a organização não permitem capturas de tela"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dispensar captura de tela"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Visualização de captura de tela"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string>
@@ -1018,6 +1020,10 @@
<string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string>
<string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string>
<string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Chave de ampliação"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ampliar toda a tela"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Trocar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Adiciona controles aos dispositivos conectados"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar controles do dispositivo"</string>
@@ -1059,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregando recomendações"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Mídia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar a sessão atual."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dispensar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 089bbd4..ce01888 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Experimente voltar a efetuar a captura de ecrã."</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Não é possível guardar a captura de ecrã devido a espaço de armazenamento limitado."</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"A app ou a sua entidade não permitem tirar capturas de ecrã"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignorar captura de ecrã"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pré-visualização da captura de ecrã"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravador de ecrã"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Definições"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Controlos da janela de ampliação"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Aumentar zoom"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Diminuir zoom"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover para cima"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Interruptor de ampliação"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ampliar o ecrã inteiro"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte do ecrã"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Mudar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controlos de dispositivos"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Adicione controlos para os dispositivos associados."</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Configure os controlos de dispositivos"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"A carregar recomendações…"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Oculte a sessão atual."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignorar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Definições"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desligado)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Não foi possível ligar. Tente novamente."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sincronize o novo dispositivo"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da compilação"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Número da compilação copiado para a área de transferência."</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index b76ebda..0e09185 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Tente fazer a captura de tela novamente"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Não é possível salvar a captura de tela, porque não há espaço suficiente"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"O app ou a organização não permitem capturas de tela"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dispensar captura de tela"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Visualização de captura de tela"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string>
@@ -1018,6 +1020,10 @@
<string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string>
<string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string>
<string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Chave de ampliação"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ampliar toda a tela"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Trocar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Adiciona controles aos dispositivos conectados"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar controles do dispositivo"</string>
@@ -1059,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregando recomendações"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Mídia"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar a sessão atual."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dispensar"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 3194b9f..9c73e5a 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Încercați să faceți din nou o captură de ecran"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Captura de ecran nu poate fi salvată din cauza spațiului de stocare limitat"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Crearea capturilor de ecran nu este permisă de aplicație sau de organizația dvs."</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Închideți captura de ecran"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Previzualizare a capturii de ecran"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Recorder pentru ecran"</string>
@@ -1017,18 +1019,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Setări"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Fereastra de mărire"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Comenzi pentru fereastra de mărire"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Măriți"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Micșorați"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Deplasați în sus"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Deplasați în jos"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Deplasați spre stânga"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Deplasați spre dreapta"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Comutator de mărire"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Măriți întregul ecran"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Măriți o parte a ecranului"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Comutator"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Comenzile dispozitivelor"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Adăugați comenzi pentru dispozitivele conectate"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurați comenzile dispozitivelor"</string>
@@ -1071,6 +1071,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Se încarcă recomandările"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ascunde sesiunea actuală."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Închideți"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Reia"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Setări"</string>
@@ -1093,8 +1095,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (s-a deconectat)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nu s-a putut conecta. Reîncercați."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Asociați un nou dispozitiv"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numărul versiunii"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Numărul versiunii s-a copiat în clipboard."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 26e3433..b51a5b2 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Попробуйте сделать скриншот снова."</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Не удалось сохранить скриншот: недостаточно места."</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Не удалось сделать скриншот: нет разрешения от приложения или организации."</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Закрыть скриншот"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Предварительный просмотр скриншота"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Запись видео с экрана"</string>
@@ -1022,18 +1024,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Настройки"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Окно увеличения"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Настройки окна увеличения"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Увеличить"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Уменьшить"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Переместить вверх"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Переместить вниз"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Переместить влево"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Переместить вправо"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Переключатель режима увеличения"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Увеличить весь экран"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличить часть экрана"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Переключить"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Управление устройствами"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Добавьте виджеты для управления устройствами."</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Настройте виджеты управления устройствами"</string>
@@ -1077,6 +1077,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загрузка рекомендаций…"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Скрыть текущий сеанс?"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Скрыть"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Возобновить"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string>
@@ -1099,8 +1101,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (отключено)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не удалось подключиться. Повторите попытку."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Подключить новое устройство"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер сборки"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Номер сборки скопирован в буфер обмена."</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 8eea1ed..5d019e94 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"තිර රුව නැවත ගැනීමට උත්සාහ කරන්න"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"සීමිත ගබඩා ඉඩ නිසා තිර රුව සුරැකිය නොහැකිය"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"තිර රූ ගැනීමට යෙදුම හෝ ඔබගේ සංවිධානය ඉඩ නොදේ"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"තිර රුව ඉවත ලන්න"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"තිර රූ පෙර දසුන"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"තිර රෙකෝඩරය"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"සැකසීම්"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"විශාලන කවුළුව"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"විශාලනය කිරීමේ කවුළු පාලන"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"විශාලනය වැඩි කරන්න"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"විශාලනය අඩු කරන්න"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"ඉහළට ගෙන යන්න"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"පහළට ගෙන යන්න"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"වමට ගෙන යන්න"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"දකුණට ගෙන යන්න"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"විශාලන ස්විචය"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"සම්පූර්ණ තිරය විශාලනය කරන්න"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"තිරයේ කොටසක් විශාලනය කරන්න"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ස්විචය"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"උපාංග පාලන"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"ඔබේ සම්බන්ධිත උපාංග සඳහා පාලන එක් කරන්න"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"උපාංග පාලන පිහිටුවන්න"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"නිර්දේශ පූරණය කරමින්"</string>
<string name="controls_media_title" msgid="1746947284862928133">"මාධ්ය"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"වත්මන් සැසිය සඟවන්න."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ඉවත ලන්න"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"නැවත පටන් ගන්න"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"සැකසීම්"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (විසන්ධි විය)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"සම්බන්ධ වීමට නොහැකි විය. නැවත උත්සාහ කරන්න."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"නව උපාංගය යුගල කරන්න"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"නිමැවුම් අංකය"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"නිමැවුම් අංකය පසුරු පුවරුවට පිටපත් කරන ලදි."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 47045d8..88cc1a2 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Skúste snímku urobiť znova"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snímka obrazovky sa nedá uložiť z dôvodu nedostatku miesta v úložisku"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Vytváranie snímok obrazovky je zakázané aplikáciou alebo vašou organizáciou"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zavrieť snímku obrazovky"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ukážka snímky obrazovky"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string>
@@ -1022,18 +1024,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Nastavenia"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Okno priblíženia"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Ovládacie prvky okna priblíženia"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Priblížiť"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Oddialiť"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Posunúť nahor"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Posunúť nadol"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Posunúť doľava"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Posunúť doprava"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prepínač zväčenia"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Zväčšiť celú obrazovku"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zväčšiť časť obrazovky"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prepnúť"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Ovládanie zariadení"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Pridajte si ovládače pripojených zariadení"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Nastavenie ovládania zariadení"</string>
@@ -1077,6 +1077,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítavajú sa odporúčania"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Médiá"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Skryť aktuálnu reláciu."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Zavrieť"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Pokračovať"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavenia"</string>
@@ -1099,8 +1101,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (odpojené)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nepodarilo sa pripojiť. Skúste to znova."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovať nové zariadenie"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo zostavy"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo zostavy bolo skopírované do schránky."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 7b68a3a..d1d14b2 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Poskusite znova ustvariti posnetek zaslona"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Shranjevanje posnetka zaslona ni mogoče zaradi omejenega prostora za shranjevanje"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ali vaša organizacija ne dovoljuje posnetkov zaslona"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Opusti posnetek zaslona"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Predogled posnetka zaslona"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Snemalnik zaslona"</string>
@@ -1022,18 +1024,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Nastavitve"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Povečevalno okno"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontrolniki povečevalnega okna"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Povečaj"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Pomanjšaj"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Premakni navzgor"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Premakni navzdol"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Premakni levo"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Premakni desno"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Stikalo za povečavo"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Povečava celotnega zaslona"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povečava dela zaslona"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Stikalo"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrolniki naprave"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodajte kontrolnike za povezane naprave"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Nastavitev kontrolnikov naprave"</string>
@@ -1077,6 +1077,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nalaganje priporočil"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Predstavnost"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Skrije trenutno sejo."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Opusti"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Nadaljuj"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavitve"</string>
@@ -1099,8 +1101,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (povezava prekinjena)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezave ni bilo mogoče vzpostaviti. Poskusite znova."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Seznanitev nove naprave"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delovna različica"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Delovna različica je bila kopirana v odložišče."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index b7ebb2a..8d79aa4 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Provo ta nxjerrësh përsëri pamjen e ekranit"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Pamja e ekranit nuk mund të ruhet për shkak të hapësirës ruajtëse të kufizuar"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Nxjerrja e pamjeve të ekranit nuk lejohet nga aplikacioni ose organizata jote."</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Hiq pamjen e ekranit"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Pamja paraprake e imazhit"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Regjistruesi i ekranit"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Cilësimet"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Dritarja e zmadhimit"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontrollet e dritares së zmadhimit"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zmadho"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zvogëlo"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Lëvize lart"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Lëvize poshtë"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Lëvize majtas"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Lëvize djathtas"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Ndërrimi i zmadhimit"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Zmadho të gjithë ekranin"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zmadho një pjesë të ekranit"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Ndërro"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrollet e pajisjes"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Shto kontrolle për pajisjet e tua të lidhura"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfiguro kontrollet e pajisjes"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Po ngarkon rekomandimet"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Fshih sesionin aktual."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Hiq"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Vazhdo"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Cilësimet"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (e shkëputur)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nuk mund të lidhej. Provo sërish."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Çifto pajisjen e re"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numri i ndërtimit"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Numri i ndërtimit u kopjua te kujtesa e fragmenteve"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index b6a90cd..5affcdf 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Пробајте да поново направите снимак екрана"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Чување снимка екрана није успело због ограниченог меморијског простора"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Апликација или организација не дозвољавају прављење снимака екрана"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Одбаците снимак екрана"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед снимка екрана"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Снимач екрана"</string>
@@ -1017,18 +1019,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Подешавања"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Прозор за увећање"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Контроле прозора за увећање"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Увећајте"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Умањите"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Померите нагоре"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Померите надоле"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Померите налево"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Померите надесно"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Прелазак на други режим увећања"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Увећајте цео екран"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увећајте део екрана"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Пређи"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Контроле уређаја"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Додајте контроле за повезане уређаје"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Подесите контроле уређаја"</string>
@@ -1071,6 +1071,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Учитавају се препоруке"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Медији"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Сакријте актуелну сесију."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Одбаци"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Настави"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Подешавања"</string>
@@ -1093,8 +1095,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (веза је прекинута)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Повезивање није успело. Пробајте поново."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Упари нови уређај"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Број верзије"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Број верзије је копиран у привремену меморију."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 890f504..a51ed28 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Testa att ta en skärmdump igen"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Det går inte att spara skärmdumpen eftersom lagringsutrymmet inte räcker"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller organisationen tillåter inte att du tar skärmdumpar"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Stäng skärmdump"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Förhandsgranskning av skärmdump"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Skärminspelare"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Inställningar"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Förstoringsfönster"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Inställningar för förstoringsfönster"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zooma in"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zooma ut"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Flytta uppåt"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Flytta nedåt"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Flytta åt vänster"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Flytta åt höger"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Förstoringsreglage"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Förstora hela skärmen"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Förstora en del av skärmen"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Reglage"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Enhetsstyrning"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Lägg till snabbkontroller för anslutna enheter"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfigurera enhetsstyrning"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Rekommendationer läses in"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Dölj den aktuella sessionen."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Stäng"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Återuppta"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Inställningar"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (frånkopplad)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Det gick inte att ansluta. Försök igen."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parkoppla en ny enhet"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versionsnummer"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Versionsnumret har kopierats till urklipp."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 34042ad..29489fd 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Jaribu kupiga picha ya skrini tena"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Imeshindwa kuhifadhi picha ya skrini kwa sababu nafasi haitoshi"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Programu au shirika lako halikuruhusu kupiga picha za skrini"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ondoa picha ya skrini"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Onyesho la kukagua picha ya skrini"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Kinasa Skrini"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Mipangilio"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Dirisha la Ukuzaji"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Vidhibiti vya Dirisha la Ukuzaji"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Vuta karibu"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Sogeza mbali"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Sogeza juu"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Sogeza chini"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sogeza kushoto"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sogeza kulia"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Swichi ya ukuzaji"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Kuza skrini yote"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Kuza sehemu ya skrini"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Swichi"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Vidhibiti vya vifaa"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Weka vidhibiti vya vifaa ulivyounganisha"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Weka mipangilio ya vidhibiti vya vifaa"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Inapakia mapendekezo"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Maudhui"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ficha kipindi cha sasa."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ondoa"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Endelea"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Mipangilio"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (hakijaunganishwa)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Imeshindwa kuunganisha. Jaribu tena."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Oanisha kifaa kipya"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nambari ya muundo"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Nambari ya muundo imewekwa kwenye ubao wa kunakili."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index b30859f..3c1c12a 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ஸ்கிரீன் ஷாட்டை மீண்டும் எடுக்க முயலவும்"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"போதுமான சேமிப்பிடம் இல்லாததால் ஸ்கிரீன்ஷாட்டைச் சேமிக்க முடியவில்லை"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ஸ்கிரீன் ஷாட்டுகளை எடுப்பதை, ஆப்ஸ் அல்லது உங்கள் நிறுவனம் அனுமதிக்கவில்லை"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ஸ்கிரீன்ஷாட்டை நிராகரி"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ஸ்கிரீன்ஷாட்டின் மாதிரிக்காட்சி"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"ஸ்கிரீன் ரெக்கார்டர்"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"அமைப்புகள்"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"பெரிதாக்கல் சாளரம்"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"பெரிதாக்கல் சாளரக் கட்டுப்பாடுகள்"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"பெரிதாக்கு"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"சிறிதாக்கு"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"மேலே நகர்த்து"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"கீழே நகர்த்து"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"இடப்புறம் நகர்த்து"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"வலப்புறம் நகர்த்து"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"பெரிதாக்கல் ஸ்விட்ச்"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"முழுத்திரையைப் பெரிதாக்கும்"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"திரையின் ஒரு பகுதியைப் பெரிதாக்கும்"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ஸ்விட்ச்"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"சாதனக் கட்டுப்பாடுகள்"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"இணைக்கப்பட்ட சாதனங்களில் கட்டுப்பாடுகளைச் சேர்க்கலாம்"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"சாதனக் கட்டுப்பாடுகளை அமைத்தல்"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"பரிந்துரைகளை ஏற்றுகிறது"</string>
<string name="controls_media_title" msgid="1746947284862928133">"மீடியா"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"இந்த அமர்வை மறையுங்கள்."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"மூடுக"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"தொடர்க"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"அமைப்புகள்"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (இணைப்பு துண்டிக்கப்பட்டது)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"இணைக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"புதிய சாதனத்தை இணைத்தல்"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"பதிப்பு எண்"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"பதிப்பு எண் கிளிப்போர்டுக்கு நகலெடுக்கப்பட்டது."</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index cb134a1..2680d22 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"స్క్రీన్షాట్ తీయడానికి మళ్లీ ప్రయత్నించండి"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"నిల్వ స్థలం పరిమితంగా ఉన్న కారణంగా స్క్రీన్షాట్ను సేవ్ చేయడం సాధ్యపడదు"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"స్క్రీన్షాట్లు తీయడానికి యాప్ లేదా మీ సంస్థ అనుమతించలేదు"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"స్క్రీన్షాట్ను మూసివేస్తుంది"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"స్క్రీన్షాట్ ప్రివ్యూ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"స్క్రీన్ రికార్డర్"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"సెట్టింగ్లు"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"మాగ్నిఫికేషన్ విండో"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"మాగ్నిఫికేషన్ నియంత్రణల విండో"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"దగ్గరగా జూమ్ చేయండి"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"దూరంగా జూమ్ చేయండి"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"పైకి పంపండి"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"కిందకి పంపండి"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"ఎడమవైపుగా జరపండి"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"కుడివైపుగా జరపండి"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"మాగ్నిఫికేషన్ స్విచ్"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"స్క్రీన్ మొత్తాన్ని మాగ్నిఫై చేయండి"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"స్క్రీన్లో భాగాన్ని మాగ్నిఫై చేయండి"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"స్విచ్ చేయి"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"పరికరం నియంత్రణలు"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"మీ కనెక్ట్ అయిన పరికరాలకు నియంత్రణలను జోడించండి"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"పరికరం నియంత్రణలను సెటప్ చేయడం"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"సిఫార్సులు లోడ్ అవుతున్నాయి"</string>
<string name="controls_media_title" msgid="1746947284862928133">"మీడియా"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"ప్రస్తుత సెషన్ను దాచు."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"విస్మరించు"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"కొనసాగించండి"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"సెట్టింగ్లు"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (డిస్కనెక్ట్ అయ్యింది)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"కనెక్ట్ చేయడం సాధ్యపడలేదు. మళ్లీ ట్రై చేయండి."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"బిల్డ్ నంబర్"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"బిల్డ్ నంబర్, క్లిప్బోర్డ్కు కాపీ చేయబడింది."</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index d678353..2e4682b 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ลองบันทึกภาพหน้าจออีกครั้ง"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"บันทึกภาพหน้าจอไม่ได้เนื่องจากพื้นที่เก็บข้อมูลมีจำกัด"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"แอปหรือองค์กรของคุณไม่อนุญาตให้จับภาพหน้าจอ"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ปิดภาพหน้าจอ"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"ตัวอย่างภาพหน้าจอ"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"โปรแกรมอัดหน้าจอ"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"การตั้งค่า"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"หน้าต่างการขยาย"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"การควบคุมหน้าต่างการขยาย"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ซูมเข้า"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ซูมออก"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"ย้ายขึ้น"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ย้ายลง"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"ย้ายไปทางซ้าย"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"ย้ายไปทางขวา"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"เปลี่ยนโหมดการขยาย"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ขยายทั้งหน้าจอ"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ขยายบางส่วนของหน้าจอ"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"เปลี่ยน"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ระบบควบคุมอุปกรณ์"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"เพิ่มตัวควบคุมของอุปกรณ์ที่เชื่อมต่อ"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"ตั้งค่าระบบควบคุมอุปกรณ์"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"กำลังโหลดคำแนะนำ"</string>
<string name="controls_media_title" msgid="1746947284862928133">"สื่อ"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"ซ่อนเซสชันปัจจุบัน"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"ปิด"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"เล่นต่อ"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"การตั้งค่า"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ยกเลิกการเชื่อมต่อแล้ว)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"เชื่อมต่อไม่ได้ ลองใหม่"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"จับคู่อุปกรณ์ใหม่"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"หมายเลขบิวด์"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"คัดลอกหมายเลขบิวด์ไปยังคลิปบอร์ดแล้ว"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 21fdd31..ae69c4f 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Subukang kumuhang muli ng screenshot"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Hindi ma-save ang screenshot dahil sa limitadong espasyo ng storage"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Hindi pinahihintulutan ng app o ng iyong organisasyon ang pagkuha ng mga screenshot"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"I-dismiss ang screenshot"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Preview ng screenshot"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Recorder ng Screen"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Mga Setting"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Window ng Pag-magnify"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Mga Kontrol sa Pag-magnify ng Window"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Mag-zoom in"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Mag-zoom out"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Itaas"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Ibaba"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Ilipat pakaliwa"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Ilipat pakanan"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Switch ng pag-magnify"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"I-magnify ang buong screen"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"I-magnify ang isang bahagi ng screen"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Mga kontrol ng device"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Magdagdag ng kontrol para sa mga nakakonektang device"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"I-set up ang mga kontrol ng device"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nilo-load ang rekomendasyon"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Itago ang kasalukuyang session."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"I-dismiss"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Ituloy"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Mga Setting"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (nakadiskonekta)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Hindi makakonekta. Subukan ulit."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Magpares ng bagong device"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero ng build"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Nakopya sa clipboard ang numero ng build."</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 3e04d19..0265176 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Tekrar ekran görüntüsü almayı deneyin"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Depolama alanı sınırlı olduğundan ekran görüntüsü kaydedilemiyor"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Uygulama veya kuruluşunuz, ekran görüntüsü alınmasına izin vermiyor."</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ekran görüntüsünü kapat"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran görüntüsü önizlemesi"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekran Kaydedicisi"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ayarlar"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Büyütme Penceresi"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Büyütme Penceresi Kontrolleri"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Yakınlaştır"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Uzaklaştır"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Yukarı taşı"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Aşağı taşı"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sola taşı"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sağa taşı"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Büyütme moduna geçin"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ekranın tamamını büyütün"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekranın bir parçasını büyütün"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Geç"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Cihaz denetimleri"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Bağlı cihazlarınız için denetimler ekleyin"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Cihaz denetimlerini kur"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Öneriler yükleniyor"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Medya"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Mevcut oturumu gizle."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Kapat"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Devam ettir"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (bağlı değil)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Bağlanılamadı. Tekrar deneyin."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yeni cihaz eşle"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Derleme numarası"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Derleme numarası panoya kopyalandı."</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index a7c6bef..c9fd503 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Спробуйте зробити знімок екрана ще раз"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Не вдалося зберегти знімок екрана через обмежений обсяг пам’яті"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Додаток або адміністратор вашої організації не дозволяють робити знімки екрана"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Закрити знімок екрана"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Перегляд знімка екрана"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Відеозапис екрана"</string>
@@ -1022,18 +1024,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Налаштування"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Вікно збільшення"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Елементи керування вікна збільшення"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Наблизити"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Віддалити"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Перемістити вгору"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Перемістити вниз"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Перемістити ліворуч"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Перемістити праворуч"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Перемикач режиму збільшення"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Збільшити весь екран"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Збільшити частину екрана"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Перемкнути"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Керування пристроями"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Додайте елементи керування для підключених пристроїв"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Налаштувати елементи керування пристроями"</string>
@@ -1077,6 +1077,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Завантаження рекомендацій"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Медіа"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Приховати поточний сеанс."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Закрити"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Відновити"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Налаштування"</string>
@@ -1099,8 +1101,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (відключено)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не вдалося підключитися. Повторіть спробу."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Підключити новий пристрій"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер складання"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Номер складання скопійовано в буфер обміну."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index f24492c..1bfa853 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"دوبارہ اسکرین شاٹ لینے کی کوشش کریں"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"اسٹوریج کی محدود جگہ کی وجہ سے اسکرین شاٹ کو محفوظ نہیں کیا جا سکتا"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ایپ یا آپ کی تنظیم کی جانب سے اسکرین شاٹس لینے کی اجازت نہیں ہے"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"اسکرین شاٹ برخاست کریں"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"اسکرین شاٹ کا پیش منظر"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"سکرین ریکارڈر"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ترتیبات"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"میگنیفکیشن ونڈو"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"میگنیفکیشن ونڈو کنٹرولز"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"زوم ان کریں"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"زوم آؤٹ کریں"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"اوپر منتقل کریں"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"نیچے منتقل کریں"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"بائیں منتقل کریں"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"دائیں منتقل کریں"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"میگنیفکیشن پر سوئچ کریں"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"پوری اسکرین کو بڑا کریں"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"اسکرین کا حصہ بڑا کریں"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"سوئچ کریں"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"آلہ کے کنٹرولز"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"اپنے منسلک آلات کے لیے کنٹرولز شامل کریں"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"آلہ کے کنٹرولز سیٹ اپ کریں"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"تجاویز لوڈ ہو رہی ہیں"</string>
<string name="controls_media_title" msgid="1746947284862928133">"میڈیا"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"موجودہ سیشن چھپائیں۔"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"برخاست کریں"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"دوبارہ شروع کریں"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"ترتیبات"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (غیر منسلک ہو گیا)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"منسلک نہیں ہو سکا۔ پھر کوشش کریں۔"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"نئے آلہ کا جوڑا بنائیں"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"بلڈ نمبر"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"بلڈ نمبر کلپ بورڈ میں کاپی ہو گیا۔"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 32c2406..71c1dde 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Qayta skrinshot olib ko‘ring"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Xotirada joy kamligi uchun skrinshot saqlanmadi"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ilova yoki tashkilotingiz skrinshot olishni taqiqlagan"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Skrinshotni yopish"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Skrinshotga razm solish"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Ekrandan yozib olish"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Sozlamalar"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Kattalashtirish oynasi"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kattalashtirish oynasi sozlamalari"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Yaqinlashtirish"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Uzoqlashtirish"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Tepaga siljitish"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pastga siljitish"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Chapga siljitish"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Oʻngga siljitish"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Kattalashtirish rejimini almashtirish"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Butun ekranni kattalashtirish"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran qismini kattalashtirish"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Almashtirish"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Qurilmalarni boshqarish"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Ulangan qurilmalar uchun boshqaruv elementlari"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Qurilma boshqaruv elementlarini sozlash"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tavsiyalar yuklanmoqda"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Joriy seans berkitilsin."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Yopish"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Davom etish"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Sozlamalar"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (uzilgan)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ulanmadi. Qayta urining."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yangi qurilmani ulash"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nashr raqami"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Nashr raqami vaqtinchalik xotiraga nusxalandi."</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index d9c80df..947bd43 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Hãy thử chụp lại màn hình"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Không thể lưu ảnh chụp màn hình do giới hạn dung lượng bộ nhớ"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ứng dụng hoặc tổ chức của bạn không cho phép chụp ảnh màn hình"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Đóng ảnh chụp màn hình"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Xem trước ảnh chụp màn hình"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Trình ghi màn hình"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Cài đặt"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Cửa sổ phóng to"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Các tùy chọn điều khiển cửa sổ phóng to"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Phóng to"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Thu nhỏ"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Di chuyển lên"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Di chuyển xuống"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Di chuyển sang trái"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Di chuyển sang phải"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Nút chuyển phóng to"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Phóng to toàn bộ màn hình"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Phóng to một phần màn hình"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Chuyển"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Điều khiển thiết bị"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Thêm các tùy chọn điều khiển cho các thiết bị đã kết nối của bạn"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Thiết lập các tùy chọn điều khiển thiết bị"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Đang tải các đề xuất"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Nội dung nghe nhìn"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Ẩn phiên hiện tại."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Đóng"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Tiếp tục"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Cài đặt"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (đã ngắt kết nối)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Không thể kết nối. Hãy thử lại."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Ghép nối thiết bị mới"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Số bản dựng"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Đã sao chép số bản dựng vào khay nhớ tạm."</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 9f9424d..730fa81 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"请再次尝试截屏"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由于存储空间有限,无法保存屏幕截图"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"此应用或您所在的单位不允许进行屏幕截图"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"关闭屏幕截图"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"屏幕截图预览"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"屏幕录制器"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"设置"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"放大窗口"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"放大窗口控件"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"放大"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"缩小"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"上移"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"下移"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"左移"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"右移"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"切换放大模式"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"放大整个屏幕"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分屏幕"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切换"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"设备控制器"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"为您所连接的设备添加控件"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"设置设备控件"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在加载推荐内容"</string>
<string name="controls_media_title" msgid="1746947284862928133">"媒体"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"隐藏当前会话。"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"关闭"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"继续播放"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"设置"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>(已断开连接)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"无法连接。请重试。"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"与新设备配对"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本号"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"已将版本号复制到剪贴板。"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 2825a9e..4236107 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"請再嘗試拍攝螢幕擷取畫面"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由於儲存空間有限,因此無法儲存螢幕擷取畫面"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"應用程式或您的機構不允許擷取螢幕畫面"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"關閉螢幕截圖"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"螢幕畫面錄影工具"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"設定"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"放大視窗"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"放大視窗控制項"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"放大"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"縮細"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"向上移"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"向下移"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"向左移"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"向右移"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"放大開關"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"放大整個螢幕畫面"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分螢幕畫面"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切換"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"裝置控制"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"為連接的裝置新增控制選項"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"設定裝置控制"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議"</string>
<string name="controls_media_title" msgid="1746947284862928133">"媒體"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"關閉"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (已中斷連線)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"無法連線,請再試一次。"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本號碼"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"版本號碼已複製到剪貼簿。"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 8acf2c6..c7407ef 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"請再次嘗試拍攝螢幕截圖"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由於儲存空間有限,因此無法儲存螢幕截圖"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"這個應用程式或貴機構不允許擷取螢幕畫面"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"關閉螢幕截圖"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"螢幕錄影器"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"設定"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"放大視窗"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"放大視窗控制項"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"放大"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"縮小"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"向上移"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"向下移"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"向左移"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"向右移"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"切換放大模式"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"放大整個螢幕畫面"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大局部螢幕畫面"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切換"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"裝置控制"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"新增已連結裝置的控制項"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"設定裝置控制"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議控制項"</string>
<string name="controls_media_title" msgid="1746947284862928133">"媒體"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"關閉"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (已中斷連線)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"無法連線,請再試一次。"</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本號碼"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"已將版本號碼複製到剪貼簿。"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 4935206..dd7f6f6 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -86,6 +86,8 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Zama ukuthatha isithombe-skrini futhi"</string>
<string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ayikwazi ukulondoloza isithombe-skrini ngenxa yesikhala sesitoreji esikhawulelwe"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ukuthatha izithombe-skrini akuvunyelwe uhlelo lokusebenza noma inhlangano yakho"</string>
+ <!-- no translation found for screenshot_edit (3510496440489019191) -->
+ <skip />
<string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Cashisa isithombe-skrini"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ukubuka kuqala isithombe-skrini"</string>
<string name="screenrecord_name" msgid="2596401223859996572">"Irekhoda yesikrini"</string>
@@ -1012,18 +1014,16 @@
<string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Amasethingi"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Iwindi Lesikhulisi"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Izilawuli Zewindi Lesikhulisi"</string>
- <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
- <skip />
- <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
- <skip />
- <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
- <skip />
- <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
- <skip />
- <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
- <skip />
- <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
- <skip />
+ <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Sondeza"</string>
+ <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Hlehlisa"</string>
+ <string name="accessibility_control_move_up" msgid="6622825494014720136">"Khuphula"</string>
+ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Yehlisa"</string>
+ <string name="accessibility_control_move_left" msgid="8156206978511401995">"Yisa kwesokunxele"</string>
+ <string name="accessibility_control_move_right" msgid="8926821093629582888">"Yisa kwesokudla"</string>
+ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Iswishi yokukhulisa"</string>
+ <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Khulisa sonke isikrini"</string>
+ <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Khulisa ingxenye eyesikrini"</string>
+ <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Iswishi"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Izilawuli zezinsiza"</string>
<string name="quick_controls_subtitle" msgid="1667408093326318053">"Engeza izilawuli zedivayisi yakho exhunyiwe"</string>
<string name="quick_controls_setup_title" msgid="8901436655997849822">"Setha izilawuli zezinsiza"</string>
@@ -1065,6 +1065,8 @@
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ilayisha izincomo"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Imidiya"</string>
<string name="controls_media_close_session" msgid="3957093425905475065">"Fihla iseshini yamanje."</string>
+ <!-- no translation found for controls_media_active_session (1984383994625845642) -->
+ <skip />
<string name="controls_media_dismiss_button" msgid="9081375542265132213">"Cashisa"</string>
<string name="controls_media_resume" msgid="1933520684481586053">"Qalisa kabusha"</string>
<string name="controls_media_settings_button" msgid="5815790345117172504">"Izilungiselelo"</string>
@@ -1087,8 +1089,6 @@
<string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (inqamukile)"</string>
<string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ayikwazanga ukuxhumeka. Zama futhi."</string>
<string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bhanqa idivayisi entsha"</string>
- <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
- <skip />
- <!-- no translation found for build_number_copy_toast (877720921605503046) -->
- <skip />
+ <string name="build_number_clip_data_label" msgid="3623176728412560914">"Yakha inombolo"</string>
+ <string name="build_number_copy_toast" msgid="877720921605503046">"Yakha inombolo ekopishelwe kubhodi yokunamathisela."</string>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 3d7b779..6df8b4e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -54,6 +54,8 @@
<color name="global_actions_emergency_background">@color/GM2_red_400</color>
<color name="global_actions_emergency_text">@color/GM2_grey_100</color>
+ <color name="global_actions_shutdown_ui_text">@color/control_primary_text</color>
+
<!-- Tint color for the content on the notification overflow card. -->
<color name="keyguard_overflow_content_color">#ff686868</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7cbbaf9..4b1ed0a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -323,7 +323,7 @@
<dimen name="screenshot_action_container_padding_right">8dp</dimen>
<!-- Radius of the chip background on global screenshot actions -->
<dimen name="screenshot_button_corner_radius">20dp</dimen>
- <dimen name="screenshot_action_chip_margin_right">8dp</dimen>
+ <dimen name="screenshot_action_chip_margin_start">8dp</dimen>
<dimen name="screenshot_action_chip_margin_vertical">10dp</dimen>
<dimen name="screenshot_action_chip_padding_vertical">7dp</dimen>
<dimen name="screenshot_action_chip_icon_size">18dp</dimen>
@@ -1217,7 +1217,7 @@
<!-- Interior padding of the message bubble -->
<dimen name="bubble_message_padding">4dp</dimen>
<!-- Offset between bubbles in their stacked position. -->
- <dimen name="bubble_stack_offset">5dp</dimen>
+ <dimen name="bubble_stack_offset">10dp</dimen>
<!-- How far offscreen the bubble stack rests. Cuts off padding and part of icon bitmap. -->
<dimen name="bubble_stack_offscreen">9dp</dimen>
<!-- How far down the screen the stack starts. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6bada51..e2ba615 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -218,7 +218,7 @@
<!-- Notification ticker displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=30] -->
<string name="screenshot_saving_ticker">Saving screenshot\u2026</string>
- <!-- Notification title displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=50] -->
+ <!-- Informs the user that a screenshot is being saved. [CHAR LIMIT=50] -->
<string name="screenshot_saving_title">Saving screenshot\u2026</string>
<!-- Notification title displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=50] -->
<string name="screenshot_saved_title">Screenshot saved</string>
@@ -233,6 +233,8 @@
<!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
<string name="screenshot_failed_to_capture_text">Taking screenshots isn\'t allowed by the app or
your organization</string>
+ <!-- Content description indicating that tapping the element will allow editing the screenshot [CHAR LIMIT=NONE] -->
+ <string name="screenshot_edit">Edit screenshot</string>
<!-- Content description indicating that tapping a button will dismiss the screenshots UI [CHAR LIMIT=NONE] -->
<string name="screenshot_dismiss_ui_description">Dismiss screenshot</string>
<!-- Content description indicating that the view is a preview of the screenshot that was just taken [CHAR LIMIT=NONE] -->
@@ -2782,6 +2784,8 @@
<string name="controls_media_title">Media</string>
<!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=NONE] -->
<string name="controls_media_close_session">Hide the current session.</string>
+ <!-- Explanation that controls associated with a specific media session are active [CHAR_LIMIT=NONE] -->
+ <string name="controls_media_active_session">Current session cannot be hidden.</string>
<!-- Label for a button that will hide media controls [CHAR_LIMIT=30] -->
<string name="controls_media_dismiss_button">Dismiss</string>
<!-- Label for button to resume media playback [CHAR_LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index b526a92..4238019 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -20,10 +20,6 @@
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.annotation.NonNull;
import android.app.Activity;
@@ -46,7 +42,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -59,7 +54,6 @@
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.ThumbnailData;
import java.util.ArrayList;
@@ -82,13 +76,11 @@
private final PackageManager mPackageManager;
private final BackgroundExecutor mBackgroundExecutor;
- private final TaskStackChangeListeners mTaskStackChangeListeners;
private ActivityManagerWrapper() {
final Context context = AppGlobals.getInitialApplication();
mPackageManager = context.getPackageManager();
mBackgroundExecutor = BackgroundExecutor.get();
- mTaskStackChangeListeners = new TaskStackChangeListeners(Looper.getMainLooper());
}
public static ActivityManagerWrapper getInstance() {
@@ -229,6 +221,22 @@
public void startRecentsActivity(Intent intent, long eventTime,
final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback,
Handler resultCallbackHandler) {
+ boolean result = startRecentsActivity(intent, eventTime, animationHandler);
+ if (resultCallback != null) {
+ resultCallbackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ resultCallback.accept(result);
+ }
+ });
+ }
+ }
+
+ /**
+ * Starts the recents activity. The caller should manage the thread on which this is called.
+ */
+ public boolean startRecentsActivity(
+ Intent intent, long eventTime, RecentsAnimationListener animationHandler) {
try {
IRecentsAnimationRunner runner = null;
if (animationHandler != null) {
@@ -260,23 +268,9 @@
};
}
ActivityTaskManager.getService().startRecentsActivity(intent, eventTime, runner);
- if (resultCallback != null) {
- resultCallbackHandler.post(new Runnable() {
- @Override
- public void run() {
- resultCallback.accept(true);
- }
- });
- }
+ return true;
} catch (Exception e) {
- if (resultCallback != null) {
- resultCallbackHandler.post(new Runnable() {
- @Override
- public void run() {
- resultCallback.accept(false);
- }
- });
- }
+ return false;
}
}
@@ -294,53 +288,17 @@
/**
* Starts a task from Recents.
*
- * @see {@link #startActivityFromRecentsAsync(TaskKey, ActivityOptions, int, int, Consumer, Handler)}
- */
- public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
- Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
- startActivityFromRecentsAsync(taskKey, options, WINDOWING_MODE_UNDEFINED,
- ACTIVITY_TYPE_UNDEFINED, resultCallback, resultCallbackHandler);
- }
-
- /**
- * Starts a task from Recents.
- *
* @param resultCallback The result success callback
* @param resultCallbackHandler The handler to receive the result callback
*/
- public void startActivityFromRecentsAsync(final Task.TaskKey taskKey, ActivityOptions options,
- int windowingMode, int activityType, final Consumer<Boolean> resultCallback,
- final Handler resultCallbackHandler) {
- if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- // We show non-visible docked tasks in Recents, but we always want to launch
- // them in the fullscreen stack.
- if (options == null) {
- options = ActivityOptions.makeBasic();
- }
- options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- } else if (windowingMode != WINDOWING_MODE_UNDEFINED
- || activityType != ACTIVITY_TYPE_UNDEFINED) {
- if (options == null) {
- options = ActivityOptions.makeBasic();
- }
- options.setLaunchWindowingMode(windowingMode);
- options.setLaunchActivityType(activityType);
- }
- final ActivityOptions finalOptions = options;
-
-
- boolean result = false;
- try {
- result = startActivityFromRecents(taskKey.id, finalOptions);
- } catch (Exception e) {
- // Fall through
- }
- final boolean finalResult = result;
+ public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
+ Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
+ final boolean result = startActivityFromRecents(taskKey, options);
if (resultCallback != null) {
resultCallbackHandler.post(new Runnable() {
@Override
public void run() {
- resultCallback.accept(finalResult);
+ resultCallback.accept(result);
}
});
}
@@ -349,6 +307,14 @@
/**
* Starts a task from Recents synchronously.
*/
+ public boolean startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options) {
+ ActivityOptionsCompat.addTaskInfo(options, taskKey);
+ return startActivityFromRecents(taskKey.id, options);
+ }
+
+ /**
+ * Starts a task from Recents synchronously.
+ */
public boolean startActivityFromRecents(int taskId, ActivityOptions options) {
try {
Bundle optsBundle = options == null ? null : options.toBundle();
@@ -360,23 +326,17 @@
}
/**
- * Registers a task stack listener with the system.
- * This should be called on the main thread.
+ * @deprecated use {@link TaskStackChangeListeners#registerTaskStackListener}
*/
public void registerTaskStackListener(TaskStackChangeListener listener) {
- synchronized (mTaskStackChangeListeners) {
- mTaskStackChangeListeners.addListener(ActivityManager.getService(), listener);
- }
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(listener);
}
/**
- * Unregisters a task stack listener with the system.
- * This should be called on the main thread.
+ * @deprecated use {@link TaskStackChangeListeners#unregisterTaskStackListener}
*/
public void unregisterTaskStackListener(TaskStackChangeListener listener) {
- synchronized (mTaskStackChangeListeners) {
- mTaskStackChangeListeners.removeListener(listener);
- }
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(listener);
}
/**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 0db4faf..1a71f11 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -26,6 +26,8 @@
import android.content.Context;
import android.os.Handler;
+import com.android.systemui.shared.recents.model.Task;
+
/**
* Wrapper around internal ActivityOptions creation.
*/
@@ -94,4 +96,15 @@
opts.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER, uptimeMillis);
return opts;
}
+
+ /**
+ * Sets Task specific information to the activity options
+ */
+ public static void addTaskInfo(ActivityOptions opts, Task.TaskKey taskKey) {
+ if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ // We show non-visible docked tasks in Recents, but we always want to launch
+ // them in the fullscreen stack.
+ opts.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ }
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
deleted file mode 100644
index 7cd6c51..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.shared.system;
-
-import android.graphics.Bitmap;
-import android.os.Bundle;
-
-/**
- * Abstract class for assist data receivers.
- */
-public abstract class AssistDataReceiver {
- public void onHandleAssistData(Bundle resultData) {}
- public void onHandleAssistScreenshot(Bitmap screenshot) {}
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java
deleted file mode 100644
index 51fcb0a..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.shared.system;
-
-import android.content.Context;
-
-/**
- * Wraps a context to expose some methods for launcher to call.
- */
-public class ContextCompat {
- private final Context mWrapped;
-
- public ContextCompat(Context context) {
- mWrapped = context;
- }
-
- public int getUserId() {
- return mWrapped.getUserId();
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
index 0d5933e..bf4fb0b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
@@ -28,7 +28,18 @@
return LatencyTracker.isEnabled(context);
}
+ /**
+ * @see LatencyTracker
+ * @deprecated Please use {@link LatencyTrackerCompat#logToggleRecents(Context, int)} instead.
+ */
+ @Deprecated
public static void logToggleRecents(int duration) {
- LatencyTracker.logAction(LatencyTracker.ACTION_TOGGLE_RECENTS, duration);
+ LatencyTracker.logActionDeprecated(LatencyTracker.ACTION_TOGGLE_RECENTS, duration, false);
+ }
+
+ /** @see LatencyTracker */
+ public static void logToggleRecents(Context context, int duration) {
+ LatencyTracker.getInstance(context).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS,
+ duration);
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index 86129e0..70021b6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -65,7 +65,7 @@
public SyncRtSurfaceTransactionApplierCompat(View targetView) {
mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
mBarrierSurfaceControl = mTargetViewRootImpl != null
- ? mTargetViewRootImpl.getRenderSurfaceControl() : null;
+ ? mTargetViewRootImpl.getSurfaceControl() : null;
mApplyHandler = new Handler(new Callback() {
@Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index f214648..765cd3d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -19,14 +19,12 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityTaskManager;
-import android.app.IActivityManager;
import android.app.TaskStackListener;
import android.content.ComponentName;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;
@@ -42,208 +40,40 @@
public class TaskStackChangeListeners extends TaskStackListener {
private static final String TAG = TaskStackChangeListeners.class.getSimpleName();
+ private static final TaskStackChangeListeners INSTANCE = new TaskStackChangeListeners();
+
+ private final Impl mImpl;
+
+ private TaskStackChangeListeners() {
+ mImpl = new Impl(Looper.getMainLooper());
+ }
+
+ public static TaskStackChangeListeners getInstance() {
+ return INSTANCE;
+ }
/**
- * List of {@link TaskStackChangeListener} registered from {@link #addListener}.
+ * Registers a task stack listener with the system.
+ * This should be called on the main thread.
*/
- private final List<TaskStackChangeListener> mTaskStackListeners = new ArrayList<>();
- private final List<TaskStackChangeListener> mTmpListeners = new ArrayList<>();
-
- private final Handler mHandler;
- private boolean mRegistered;
-
- public TaskStackChangeListeners(Looper looper) {
- mHandler = new H(looper);
- }
-
- public void addListener(IActivityManager am, TaskStackChangeListener listener) {
- synchronized (mTaskStackListeners) {
- mTaskStackListeners.add(listener);
- }
- if (!mRegistered) {
- // Register mTaskStackListener to IActivityManager only once if needed.
- try {
- ActivityTaskManager.getService().registerTaskStackListener(this);
- mRegistered = true;
- } catch (Exception e) {
- Log.w(TAG, "Failed to call registerTaskStackListener", e);
- }
+ public void registerTaskStackListener(TaskStackChangeListener listener) {
+ synchronized (mImpl) {
+ mImpl.addListener(listener);
}
}
- public void removeListener(TaskStackChangeListener listener) {
- boolean isEmpty;
- synchronized (mTaskStackListeners) {
- mTaskStackListeners.remove(listener);
- isEmpty = mTaskStackListeners.isEmpty();
- }
- if (isEmpty && mRegistered) {
- // Unregister mTaskStackListener once we have no more listeners
- try {
- ActivityTaskManager.getService().unregisterTaskStackListener(this);
- mRegistered = false;
- } catch (Exception e) {
- Log.w(TAG, "Failed to call unregisterTaskStackListener", e);
- }
+ /**
+ * Unregisters a task stack listener with the system.
+ * This should be called on the main thread.
+ */
+ public void unregisterTaskStackListener(TaskStackChangeListener listener) {
+ synchronized (mImpl) {
+ mImpl.removeListener(listener);
}
}
- @Override
- public void onTaskStackChanged() throws RemoteException {
- // Call the task changed callback for the non-ui thread listeners first. Copy to a set of
- // temp listeners so that we don't lock on mTaskStackListeners while calling all the
- // callbacks. This call is always on the same binder thread, so we can just synchronize
- // on the copying of the listener list.
- synchronized (mTaskStackListeners) {
- mTmpListeners.addAll(mTaskStackListeners);
- }
- for (int i = mTmpListeners.size() - 1; i >= 0; i--) {
- mTmpListeners.get(i).onTaskStackChangedBackground();
- }
- mTmpListeners.clear();
+ private static class Impl extends TaskStackListener implements Handler.Callback {
- mHandler.removeMessages(H.ON_TASK_STACK_CHANGED);
- mHandler.sendEmptyMessage(H.ON_TASK_STACK_CHANGED);
- }
-
- @Override
- public void onActivityPinned(String packageName, int userId, int taskId, int stackId)
- throws RemoteException {
- mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
- mHandler.obtainMessage(H.ON_ACTIVITY_PINNED,
- new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget();
- }
-
- @Override
- public void onActivityUnpinned() throws RemoteException {
- mHandler.removeMessages(H.ON_ACTIVITY_UNPINNED);
- mHandler.sendEmptyMessage(H.ON_ACTIVITY_UNPINNED);
- }
-
- @Override
- public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
- boolean clearedTask, boolean wasVisible) throws RemoteException {
- final SomeArgs args = SomeArgs.obtain();
- args.arg1 = task;
- args.argi1 = homeTaskVisible ? 1 : 0;
- args.argi2 = clearedTask ? 1 : 0;
- args.argi3 = wasVisible ? 1 : 0;
- mHandler.removeMessages(H.ON_ACTIVITY_RESTART_ATTEMPT);
- mHandler.obtainMessage(H.ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
- }
-
- @Override
- public void onActivityForcedResizable(String packageName, int taskId, int reason)
- throws RemoteException {
- mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
- .sendToTarget();
- }
-
- @Override
- public void onActivityDismissingDockedStack() throws RemoteException {
- mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK);
- }
-
- @Override
- public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo,
- int requestedDisplayId) throws RemoteException {
- mHandler.obtainMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED, requestedDisplayId,
- 0 /* unused */,
- taskInfo).sendToTarget();
- }
-
- @Override
- public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo,
- int requestedDisplayId) throws RemoteException {
- mHandler.obtainMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED,
- requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
- }
-
- @Override
- public void onTaskProfileLocked(int taskId, int userId) throws RemoteException {
- mHandler.obtainMessage(H.ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
- }
-
- @Override
- public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) throws RemoteException {
- mHandler.obtainMessage(H.ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
- }
-
- @Override
- public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
- mHandler.obtainMessage(H.ON_TASK_CREATED, taskId, 0, componentName).sendToTarget();
- }
-
- @Override
- public void onTaskRemoved(int taskId) throws RemoteException {
- mHandler.obtainMessage(H.ON_TASK_REMOVED, taskId, 0).sendToTarget();
- }
-
- @Override
- public void onTaskMovedToFront(RunningTaskInfo taskInfo)
- throws RemoteException {
- mHandler.obtainMessage(H.ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget();
- }
-
- @Override
- public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) throws RemoteException {
- mHandler.obtainMessage(H.ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget();
- }
-
- @Override
- public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation)
- throws RemoteException {
- mHandler.obtainMessage(H.ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
- requestedOrientation).sendToTarget();
- }
-
- @Override
- public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken)
- throws RemoteException {
- mHandler.obtainMessage(H.ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId, 0 /* unused */,
- activityToken).sendToTarget();
- }
-
- @Override
- public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
- mHandler.obtainMessage(H.ON_SINGLE_TASK_DISPLAY_DRAWN, displayId,
- 0 /* unused */).sendToTarget();
- }
-
- @Override
- public void onSingleTaskDisplayEmpty(int displayId) throws RemoteException {
- mHandler.obtainMessage(H.ON_SINGLE_TASK_DISPLAY_EMPTY, displayId,
- 0 /* unused */).sendToTarget();
- }
-
- @Override
- public void onTaskDisplayChanged(int taskId, int newDisplayId) throws RemoteException {
- mHandler.obtainMessage(H.ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
- }
-
- @Override
- public void onRecentTaskListUpdated() throws RemoteException {
- mHandler.obtainMessage(H.ON_TASK_LIST_UPDATED).sendToTarget();
- }
-
- @Override
- public void onRecentTaskListFrozenChanged(boolean frozen) {
- mHandler.obtainMessage(H.ON_TASK_LIST_FROZEN_UNFROZEN, frozen ? 1 : 0, 0 /* unused */)
- .sendToTarget();
- }
-
- @Override
- public void onTaskDescriptionChanged(RunningTaskInfo taskInfo) {
- mHandler.obtainMessage(H.ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget();
- }
-
- @Override
- public void onActivityRotation(int displayId) {
- mHandler.obtainMessage(H.ON_ACTIVITY_ROTATION, displayId, 0 /* unused */)
- .sendToTarget();
- }
-
- private final class H extends Handler {
private static final int ON_TASK_STACK_CHANGED = 1;
private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
private static final int ON_ACTIVITY_PINNED = 3;
@@ -268,13 +98,205 @@
private static final int ON_TASK_DESCRIPTION_CHANGED = 24;
private static final int ON_ACTIVITY_ROTATION = 25;
+ /**
+ * List of {@link TaskStackChangeListener} registered from {@link #addListener}.
+ */
+ private final List<TaskStackChangeListener> mTaskStackListeners = new ArrayList<>();
+ private final List<TaskStackChangeListener> mTmpListeners = new ArrayList<>();
- public H(Looper looper) {
- super(looper);
+ private final Handler mHandler;
+ private boolean mRegistered;
+
+ Impl(Looper looper) {
+ mHandler = new Handler(looper, this);
+ }
+
+ public void addListener(TaskStackChangeListener listener) {
+ synchronized (mTaskStackListeners) {
+ mTaskStackListeners.add(listener);
+ }
+ if (!mRegistered) {
+ // Register mTaskStackListener to IActivityManager only once if needed.
+ try {
+ ActivityTaskManager.getService().registerTaskStackListener(this);
+ mRegistered = true;
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to call registerTaskStackListener", e);
+ }
+ }
+ }
+
+ public void removeListener(TaskStackChangeListener listener) {
+ boolean isEmpty;
+ synchronized (mTaskStackListeners) {
+ mTaskStackListeners.remove(listener);
+ isEmpty = mTaskStackListeners.isEmpty();
+ }
+ if (isEmpty && mRegistered) {
+ // Unregister mTaskStackListener once we have no more listeners
+ try {
+ ActivityTaskManager.getService().unregisterTaskStackListener(this);
+ mRegistered = false;
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to call unregisterTaskStackListener", e);
+ }
+ }
}
@Override
- public void handleMessage(Message msg) {
+ public void onTaskStackChanged() {
+ // Call the task changed callback for the non-ui thread listeners first. Copy to a set
+ // of temp listeners so that we don't lock on mTaskStackListeners while calling all the
+ // callbacks. This call is always on the same binder thread, so we can just synchronize
+ // on the copying of the listener list.
+ synchronized (mTaskStackListeners) {
+ mTmpListeners.addAll(mTaskStackListeners);
+ }
+ for (int i = mTmpListeners.size() - 1; i >= 0; i--) {
+ mTmpListeners.get(i).onTaskStackChangedBackground();
+ }
+ mTmpListeners.clear();
+
+ mHandler.removeMessages(ON_TASK_STACK_CHANGED);
+ mHandler.sendEmptyMessage(ON_TASK_STACK_CHANGED);
+ }
+
+ @Override
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
+ mHandler.removeMessages(ON_ACTIVITY_PINNED);
+ mHandler.obtainMessage(ON_ACTIVITY_PINNED,
+ new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget();
+ }
+
+ @Override
+ public void onActivityUnpinned() {
+ mHandler.removeMessages(ON_ACTIVITY_UNPINNED);
+ mHandler.sendEmptyMessage(ON_ACTIVITY_UNPINNED);
+ }
+
+ @Override
+ public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
+ boolean clearedTask, boolean wasVisible) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = task;
+ args.argi1 = homeTaskVisible ? 1 : 0;
+ args.argi2 = clearedTask ? 1 : 0;
+ args.argi3 = wasVisible ? 1 : 0;
+ mHandler.removeMessages(ON_ACTIVITY_RESTART_ATTEMPT);
+ mHandler.obtainMessage(ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
+ }
+
+ @Override
+ public void onActivityForcedResizable(String packageName, int taskId, int reason) {
+ mHandler.obtainMessage(ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onActivityDismissingDockedStack() {
+ mHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK);
+ }
+
+ @Override
+ public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo,
+ int requestedDisplayId) {
+ mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED,
+ requestedDisplayId,
+ 0 /* unused */,
+ taskInfo).sendToTarget();
+ }
+
+ @Override
+ public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo,
+ int requestedDisplayId) {
+ mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED,
+ requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
+ }
+
+ @Override
+ public void onTaskProfileLocked(int taskId, int userId) {
+ mHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
+ }
+
+ @Override
+ public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
+ mHandler.obtainMessage(ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
+ }
+
+ @Override
+ public void onTaskCreated(int taskId, ComponentName componentName) {
+ mHandler.obtainMessage(ON_TASK_CREATED, taskId, 0, componentName).sendToTarget();
+ }
+
+ @Override
+ public void onTaskRemoved(int taskId) {
+ mHandler.obtainMessage(ON_TASK_REMOVED, taskId, 0).sendToTarget();
+ }
+
+ @Override
+ public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
+ mHandler.obtainMessage(ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget();
+ }
+
+ @Override
+ public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
+ mHandler.obtainMessage(ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget();
+ }
+
+ @Override
+ public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
+ mHandler.obtainMessage(ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
+ requestedOrientation).sendToTarget();
+ }
+
+ @Override
+ public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
+ mHandler.obtainMessage(ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId,
+ 0 /* unused */,
+ activityToken).sendToTarget();
+ }
+
+ @Override
+ public void onSingleTaskDisplayDrawn(int displayId) {
+ mHandler.obtainMessage(ON_SINGLE_TASK_DISPLAY_DRAWN, displayId,
+ 0 /* unused */).sendToTarget();
+ }
+
+ @Override
+ public void onSingleTaskDisplayEmpty(int displayId) {
+ mHandler.obtainMessage(ON_SINGLE_TASK_DISPLAY_EMPTY, displayId,
+ 0 /* unused */).sendToTarget();
+ }
+
+ @Override
+ public void onTaskDisplayChanged(int taskId, int newDisplayId) {
+ mHandler.obtainMessage(ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
+ }
+
+ @Override
+ public void onRecentTaskListUpdated() {
+ mHandler.obtainMessage(ON_TASK_LIST_UPDATED).sendToTarget();
+ }
+
+ @Override
+ public void onRecentTaskListFrozenChanged(boolean frozen) {
+ mHandler.obtainMessage(ON_TASK_LIST_FROZEN_UNFROZEN, frozen ? 1 : 0, 0 /* unused */)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onTaskDescriptionChanged(RunningTaskInfo taskInfo) {
+ mHandler.obtainMessage(ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget();
+ }
+
+ @Override
+ public void onActivityRotation(int displayId) {
+ mHandler.obtainMessage(ON_ACTIVITY_ROTATION, displayId, 0 /* unused */)
+ .sendToTarget();
+ }
+
+ @Override
+ public boolean handleMessage(Message msg) {
synchronized (mTaskStackListeners) {
switch (msg.what) {
case ON_TASK_STACK_CHANGED: {
@@ -298,7 +320,8 @@
final PinnedActivityInfo info = (PinnedActivityInfo) msg.obj;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onActivityPinned(
- info.mPackageName, info.mUserId, info.mTaskId, info.mStackId);
+ info.mPackageName, info.mUserId, info.mTaskId,
+ info.mStackId);
}
break;
}
@@ -404,8 +427,7 @@
}
case ON_SINGLE_TASK_DISPLAY_EMPTY: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onSingleTaskDisplayEmpty(
- msg.arg1);
+ mTaskStackListeners.get(i).onSingleTaskDisplayEmpty(msg.arg1);
}
break;
}
@@ -423,7 +445,8 @@
}
case ON_TASK_LIST_FROZEN_UNFROZEN: {
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onRecentTaskListFrozenChanged(msg.arg1 != 0);
+ mTaskStackListeners.get(i).onRecentTaskListFrozenChanged(
+ msg.arg1 != 0);
}
break;
}
@@ -445,6 +468,7 @@
if (msg.obj instanceof SomeArgs) {
((SomeArgs) msg.obj).recycle();
}
+ return true;
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
index 73783ae..4a28d56 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
@@ -34,10 +34,6 @@
}
public SurfaceControl getRenderSurfaceControl() {
- return mViewRoot == null ? null : mViewRoot.getRenderSurfaceControl();
- }
-
- public SurfaceControl getSurfaceControl() {
return mViewRoot == null ? null : mViewRoot.getSurfaceControl();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 5122f6c..97196d1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -102,6 +102,18 @@
}
/**
+ * Sets if app requested fixed orientation should be ignored for given displayId.
+ */
+ public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().setIgnoreOrientationRequest(
+ displayId, ignoreOrientationRequest);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to setIgnoreOrientationRequest()", e);
+ }
+ }
+
+ /**
* @return the stable insets for the primary display.
*/
public void getStableInsets(Rect outStableInsets) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 5ad8cad..272954d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -104,6 +104,8 @@
private boolean mSupportsDarkText;
private int[] mColorPalette;
+ private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
+
public KeyguardClockSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -128,6 +130,35 @@
return mClockPlugin != null;
}
+ /**
+ * Update lock screen mode for testing different layouts
+ */
+ public void updateLockScreenMode(int mode) {
+ mLockScreenMode = mode;
+ RelativeLayout.LayoutParams statusAreaLP = (RelativeLayout.LayoutParams)
+ mKeyguardStatusArea.getLayoutParams();
+ RelativeLayout.LayoutParams clockLP = (RelativeLayout.LayoutParams)
+ mSmallClockFrame.getLayoutParams();
+
+ if (mode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+ statusAreaLP.removeRule(RelativeLayout.BELOW);
+ statusAreaLP.addRule(RelativeLayout.LEFT_OF, R.id.clock_view);
+ statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START);
+
+ clockLP.addRule(RelativeLayout.ALIGN_PARENT_END);
+ clockLP.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ } else {
+ statusAreaLP.removeRule(RelativeLayout.LEFT_OF);
+ statusAreaLP.removeRule(RelativeLayout.ALIGN_PARENT_START);
+ statusAreaLP.addRule(RelativeLayout.BELOW, R.id.clock_view);
+
+ clockLP.removeRule(RelativeLayout.ALIGN_PARENT_END);
+ clockLP.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ }
+
+ requestLayout();
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -363,6 +394,10 @@
* these cases.
*/
void setKeyguardShowingHeader(boolean hasHeader) {
+ if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+ hasHeader = false;
+ }
+
if (mShowingHeader == hasHeader) {
return;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index fe5fcc6..1562444 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -17,24 +17,32 @@
package com.android.keyguard;
import android.app.WallpaperManager;
-import android.view.View;
+import android.content.res.Resources;
+import android.text.format.DateFormat;
+import android.util.TypedValue;
import android.view.ViewGroup;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
+import com.android.systemui.R;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.util.ViewController;
+
+import java.util.Locale;
+import java.util.TimeZone;
import javax.inject.Inject;
/**
* Injectable controller for {@link KeyguardClockSwitch}.
*/
-public class KeyguardClockSwitchController {
+public class KeyguardClockSwitchController extends ViewController<KeyguardClockSwitch> {
private static final boolean CUSTOM_CLOCKS_ENABLED = true;
- private final KeyguardClockSwitch mView;
+ private final Resources mResources;
private final StatusBarStateController mStatusBarStateController;
private final SysuiColorExtractor mColorExtractor;
private final ClockManager mClockManager;
@@ -65,35 +73,15 @@
private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
- private final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
- new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- if (CUSTOM_CLOCKS_ENABLED) {
- mClockManager.addOnClockChangedListener(mClockChangedListener);
- }
- mStatusBarStateController.addCallback(mStateListener);
- mColorExtractor.addOnColorsChangedListener(mColorsListener);
- mView.updateColors(getGradientColors());
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- if (CUSTOM_CLOCKS_ENABLED) {
- mClockManager.removeOnClockChangedListener(mClockChangedListener);
- }
- mStatusBarStateController.removeCallback(mStateListener);
- mColorExtractor.removeOnColorsChangedListener(mColorsListener);
- mView.setClockPlugin(null, mStatusBarStateController.getState());
- }
- };
-
@Inject
- public KeyguardClockSwitchController(KeyguardClockSwitch keyguardClockSwitch,
+ public KeyguardClockSwitchController(
+ KeyguardClockSwitch keyguardClockSwitch,
+ @Main Resources resources,
StatusBarStateController statusBarStateController,
SysuiColorExtractor colorExtractor, ClockManager clockManager,
KeyguardSliceViewController keyguardSliceViewController) {
- mView = keyguardClockSwitch;
+ super(keyguardClockSwitch);
+ mResources = resources;
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
mClockManager = clockManager;
@@ -103,15 +91,41 @@
/**
* Attach the controller to the view it relates to.
*/
+ @Override
public void init() {
- if (mView.isAttachedToWindow()) {
- mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
- }
- mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
-
+ super.init();
mKeyguardSliceViewController.init();
}
+ @Override
+ protected void onViewAttached() {
+ if (CUSTOM_CLOCKS_ENABLED) {
+ mClockManager.addOnClockChangedListener(mClockChangedListener);
+ }
+ refreshFormat();
+ mStatusBarStateController.addCallback(mStateListener);
+ mColorExtractor.addOnColorsChangedListener(mColorsListener);
+ mView.updateColors(getGradientColors());
+ }
+
+ @Override
+ protected void onViewDetached() {
+ if (CUSTOM_CLOCKS_ENABLED) {
+ mClockManager.removeOnClockChangedListener(mClockChangedListener);
+ }
+ mStatusBarStateController.removeCallback(mStateListener);
+ mColorExtractor.removeOnColorsChangedListener(mColorsListener);
+ mView.setClockPlugin(null, mStatusBarStateController.getState());
+ }
+
+ /**
+ * Updates clock's text
+ */
+ public void onDensityOrFontScaleChanged() {
+ mView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ mResources.getDimensionPixelSize(R.dimen.widget_big_font_size));
+ }
+
/**
* Set container for big clock face appearing behind NSSL and KeyguardStatusView.
*/
@@ -119,6 +133,61 @@
mView.setBigClockContainer(bigClockContainer, mStatusBarStateController.getState());
}
+ /**
+ * Set whether or not the lock screen is showing notifications.
+ */
+ public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
+ mView.setHasVisibleNotifications(hasVisibleNotifications);
+ }
+
+ /**
+ * If we're presenting a custom clock of just the default one.
+ */
+ public boolean hasCustomClock() {
+ return mView.hasCustomClock();
+ }
+
+ /**
+ * Get the clock text size.
+ */
+ public float getClockTextSize() {
+ return mView.getTextSize();
+ }
+
+ /**
+ * Returns the preferred Y position of the clock.
+ *
+ * @param totalHeight The height available to position the clock.
+ * @return Y position of clock.
+ */
+ public int getClockPreferredY(int totalHeight) {
+ return mView.getPreferredY(totalHeight);
+ }
+
+ /**
+ * Refresh clock. Called in response to TIME_TICK broadcasts.
+ */
+ void refresh() {
+ mView.refresh();
+ }
+
+ /**
+ * Update lockscreen mode that may change clock display.
+ */
+ void updateLockScreenMode(int mode) {
+ mView.updateLockScreenMode(mode);
+ }
+
+ void updateTimeZone(TimeZone timeZone) {
+ mView.onTimeZoneChanged(timeZone);
+ }
+
+ void refreshFormat() {
+ Patterns.update(mResources);
+ mView.setFormat12Hour(Patterns.sClockView12);
+ mView.setFormat24Hour(Patterns.sClockView24);
+ }
+
private void setClockPlugin(ClockPlugin plugin) {
mView.setClockPlugin(plugin, mStatusBarStateController.getState());
}
@@ -126,4 +195,35 @@
private ColorExtractor.GradientColors getGradientColors() {
return mColorExtractor.getColors(WallpaperManager.FLAG_LOCK);
}
+
+ // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
+ // This is an optimization to ensure we only recompute the patterns when the inputs change.
+ private static final class Patterns {
+ static String sClockView12;
+ static String sClockView24;
+ static String sCacheKey;
+
+ static void update(Resources res) {
+ final Locale locale = Locale.getDefault();
+ final String clockView12Skel = res.getString(R.string.clock_12hr_format);
+ final String clockView24Skel = res.getString(R.string.clock_24hr_format);
+ final String key = locale.toString() + clockView12Skel + clockView24Skel;
+ if (key.equals(sCacheKey)) return;
+
+ sClockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel);
+ // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
+ // format. The following code removes the AM/PM indicator if we didn't want it.
+ if (!clockView12Skel.contains("a")) {
+ sClockView12 = sClockView12.replaceAll("a", "").trim();
+ }
+
+ sClockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
+
+ // Use fancy colon.
+ sClockView24 = sClockView24.replace(':', '\uee01');
+ sClockView12 = sClockView12.replace(':', '\uee01');
+
+ sCacheKey = key;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 36d5543..901a736 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -20,7 +20,7 @@
import android.app.Presentation;
import android.content.Context;
import android.graphics.Color;
-import android.graphics.Point;
+import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
@@ -127,7 +127,7 @@
Presentation presentation = mPresentations.get(displayId);
if (presentation == null) {
final Presentation newPresentation = new KeyguardPresentation(mContext, display,
- mKeyguardStatusViewComponentFactory, LayoutInflater.from(mContext));
+ mKeyguardStatusViewComponentFactory);
newPresentation.setOnDismissListener(dialog -> {
if (newPresentation.equals(mPresentations.get(displayId))) {
mPresentations.remove(displayId);
@@ -245,7 +245,6 @@
private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- private final LayoutInflater mLayoutInflater;
private KeyguardClockSwitchController mKeyguardClockSwitchController;
private View mClock;
private int mUsableWidth;
@@ -264,18 +263,16 @@
};
KeyguardPresentation(Context context, Display display,
- KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
- LayoutInflater layoutInflater) {
- super(context, display, R.style.Theme_SystemUI_KeyguardPresentation);
+ KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
+ super(context, display, R.style.Theme_SystemUI_KeyguardPresentation,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
- mLayoutInflater = layoutInflater;
- getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
setCancelable(false);
}
@Override
public void cancel() {
- // Do not allow anything to cancel KeyguardPresetation except KeyguardDisplayManager.
+ // Do not allow anything to cancel KeyguardPresentation except KeyguardDisplayManager.
}
@Override
@@ -287,14 +284,15 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Point p = new Point();
- getDisplay().getSize(p);
- mUsableWidth = VIDEO_SAFE_REGION * p.x/100;
- mUsableHeight = VIDEO_SAFE_REGION * p.y/100;
- mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200;
- mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200;
+ final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
+ .getBounds();
+ mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
+ mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
+ mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
+ mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
- setContentView(mLayoutInflater.inflate(R.layout.keyguard_presentation, null));
+ setContentView(LayoutInflater.from(getContext())
+ .inflate(R.layout.keyguard_presentation, null));
// Logic to make the lock screen fullscreen
getWindow().getDecorView().setSystemUiVisibility(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index 2470b95..3be7e0a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -59,7 +59,6 @@
private static final String TAG = "KeyguardSliceViewCtrl";
private final KeyguardSliceView mView;
- private final KeyguardStatusView mKeyguardStatusView;
private final ActivityStarter mActivityStarter;
private final ConfigurationController mConfigurationController;
private final TunerService mTunerService;
@@ -135,11 +134,10 @@
@Inject
public KeyguardSliceViewController(KeyguardSliceView keyguardSliceView,
- KeyguardStatusView keyguardStatusView, ActivityStarter activityStarter,
+ ActivityStarter activityStarter,
ConfigurationController configurationController, TunerService tunerService,
DumpManager dumpManager) {
mView = keyguardSliceView;
- mKeyguardStatusView = keyguardStatusView;
mActivityStarter = activityStarter;
mConfigurationController = configurationController;
mTunerService = tunerService;
@@ -153,8 +151,6 @@
}
mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
mView.setOnClickListener(mOnClickListener);
- // TODO: remove the line below.
- mKeyguardStatusView.setKeyguardSliceViewController(this);
}
/**
@@ -233,7 +229,5 @@
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println(" mSlice: " + mSlice);
pw.println(" mClickActions: " + mClickActions);
-
- mKeyguardStatusView.dump(fd, pw, args);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 6e11174..2036b33 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -19,16 +19,13 @@
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Color;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.Slog;
import android.util.TypedValue;
import android.view.View;
import android.widget.GridLayout;
@@ -39,15 +36,18 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Locale;
-import java.util.TimeZone;
-public class KeyguardStatusView extends GridLayout implements
- ConfigurationController.ConfigurationListener {
+/**
+ * View consisting of:
+ * - keyguard clock
+ * - logout button (on certain managed devices)
+ * - owner information (if set)
+ * - notification icons (shown on AOD)
+ */
+public class KeyguardStatusView extends GridLayout {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final String TAG = "KeyguardStatusView";
private static final int MARQUEE_DELAY_MS = 2000;
@@ -62,9 +62,7 @@
private View mNotificationIcons;
private Runnable mPendingMarqueeStart;
private Handler mHandler;
- private KeyguardSliceViewController mKeyguardSliceViewController;
- private boolean mPulsing;
private float mDarkAmount = 0;
private int mTextColor;
@@ -76,51 +74,6 @@
private int mIconTopMarginWithHeader;
private boolean mShowingHeader;
- private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
-
- @Override
- public void onTimeChanged() {
- refreshTime();
- }
-
- @Override
- public void onTimeZoneChanged(TimeZone timeZone) {
- updateTimeZone(timeZone);
- }
-
- @Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- if (showing) {
- if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
- refreshTime();
- updateOwnerInfo();
- updateLogoutView();
- }
- }
-
- @Override
- public void onStartedWakingUp() {
- setEnableMarquee(true);
- }
-
- @Override
- public void onFinishedGoingToSleep(int why) {
- setEnableMarquee(false);
- }
-
- @Override
- public void onUserSwitchComplete(int userId) {
- refreshFormat();
- updateOwnerInfo();
- updateLogoutView();
- }
-
- @Override
- public void onLogoutEnabledChanged() {
- updateLogoutView();
- }
- };
-
public KeyguardStatusView(Context context) {
this(context, null, 0);
}
@@ -137,21 +90,7 @@
onDensityOrFontScaleChanged();
}
- /**
- * If we're presenting a custom clock of just the default one.
- */
- public boolean hasCustomClock() {
- return mClockView.hasCustomClock();
- }
-
- /**
- * Set whether or not the lock screen is showing notifications.
- */
- public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
- mClockView.setHasVisibleNotifications(hasVisibleNotifications);
- }
-
- private void setEnableMarquee(boolean enabled) {
+ void setEnableMarquee(boolean enabled) {
if (DEBUG) Log.v(TAG, "Schedule setEnableMarquee: " + (enabled ? "Enable" : "Disable"));
if (enabled) {
if (mPendingMarqueeStart == null) {
@@ -198,7 +137,6 @@
boolean shouldMarquee = Dependency.get(KeyguardUpdateMonitor.class).isDeviceInteractive();
setEnableMarquee(shouldMarquee);
- refreshFormat();
updateOwnerInfo();
updateLogoutView();
updateDark();
@@ -233,60 +171,14 @@
layoutOwnerInfo();
}
- @Override
- public void onDensityOrFontScaleChanged() {
- if (mClockView != null) {
- mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- getResources().getDimensionPixelSize(R.dimen.widget_big_font_size));
- }
- if (mOwnerInfo != null) {
- mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- getResources().getDimensionPixelSize(R.dimen.widget_label_font_size));
- }
- loadBottomMargin();
- }
-
- public void dozeTimeTick() {
- refreshTime();
- mKeyguardSliceViewController.refresh();
- }
-
- private void refreshTime() {
- mClockView.refresh();
- }
-
- private void updateTimeZone(TimeZone timeZone) {
- mClockView.onTimeZoneChanged(timeZone);
- }
-
- private void refreshFormat() {
- Patterns.update(mContext);
- mClockView.setFormat12Hour(Patterns.clockView12);
- mClockView.setFormat24Hour(Patterns.clockView24);
- }
-
- public int getLogoutButtonHeight() {
+ int getLogoutButtonHeight() {
if (mLogoutView == null) {
return 0;
}
return mLogoutView.getVisibility() == VISIBLE ? mLogoutView.getHeight() : 0;
}
- public float getClockTextSize() {
- return mClockView.getTextSize();
- }
-
- /**
- * Returns the preferred Y position of the clock.
- *
- * @param totalHeight The height available to position the clock.
- * @return Y position of clock.
- */
- public int getClockPreferredY(int totalHeight) {
- return mClockView.getPreferredY(totalHeight);
- }
-
- private void updateLogoutView() {
+ void updateLogoutView() {
if (mLogoutView == null) {
return;
}
@@ -296,7 +188,16 @@
com.android.internal.R.string.global_action_logout));
}
- private void updateOwnerInfo() {
+ void onDensityOrFontScaleChanged() {
+ if (mOwnerInfo != null) {
+ mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ getResources().getDimensionPixelSize(
+ com.android.systemui.R.dimen.widget_label_font_size));
+ loadBottomMargin();
+ }
+ }
+
+ void updateOwnerInfo() {
if (mOwnerInfo == null) return;
String info = mLockPatternUtils.getDeviceOwnerInfo();
if (info == null) {
@@ -311,30 +212,36 @@
updateDark();
}
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mInfoCallback);
- Dependency.get(ConfigurationController.class).addCallback(this);
+ void setDarkAmount(float darkAmount) {
+ if (mDarkAmount == darkAmount) {
+ return;
+ }
+ mDarkAmount = darkAmount;
+ mClockView.setDarkAmount(darkAmount);
+ updateDark();
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mInfoCallback);
- Dependency.get(ConfigurationController.class).removeCallback(this);
- }
+ void updateDark() {
+ boolean dark = mDarkAmount == 1;
+ if (mLogoutView != null) {
+ mLogoutView.setAlpha(dark ? 0 : 1);
+ }
- @Override
- public void onLocaleListChanged() {
- refreshFormat();
+ if (mOwnerInfo != null) {
+ boolean hasText = !TextUtils.isEmpty(mOwnerInfo.getText());
+ mOwnerInfo.setVisibility(hasText ? VISIBLE : GONE);
+ layoutOwnerInfo();
+ }
+
+ final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
+ mKeyguardSlice.setDarkAmount(mDarkAmount);
+ mClockView.setTextColor(blendedTextColor);
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("KeyguardStatusView:");
pw.println(" mOwnerInfo: " + (mOwnerInfo == null
? "null" : mOwnerInfo.getVisibility() == VISIBLE));
- pw.println(" mPulsing: " + mPulsing);
pw.println(" mDarkAmount: " + mDarkAmount);
pw.println(" mTextColor: " + Integer.toHexString(mTextColor));
if (mLogoutView != null) {
@@ -354,64 +261,6 @@
R.dimen.widget_vertical_padding_with_header);
}
- // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
- // This is an optimization to ensure we only recompute the patterns when the inputs change.
- private static final class Patterns {
- static String clockView12;
- static String clockView24;
- static String cacheKey;
-
- static void update(Context context) {
- final Locale locale = Locale.getDefault();
- final Resources res = context.getResources();
- final String clockView12Skel = res.getString(R.string.clock_12hr_format);
- final String clockView24Skel = res.getString(R.string.clock_24hr_format);
- final String key = locale.toString() + clockView12Skel + clockView24Skel;
- if (key.equals(cacheKey)) return;
-
- clockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel);
- // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
- // format. The following code removes the AM/PM indicator if we didn't want it.
- if (!clockView12Skel.contains("a")) {
- clockView12 = clockView12.replaceAll("a", "").trim();
- }
-
- clockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
-
- // Use fancy colon.
- clockView24 = clockView24.replace(':', '\uee01');
- clockView12 = clockView12.replace(':', '\uee01');
-
- cacheKey = key;
- }
- }
-
- public void setDarkAmount(float darkAmount) {
- if (mDarkAmount == darkAmount) {
- return;
- }
- mDarkAmount = darkAmount;
- mClockView.setDarkAmount(darkAmount);
- updateDark();
- }
-
- private void updateDark() {
- boolean dark = mDarkAmount == 1;
- if (mLogoutView != null) {
- mLogoutView.setAlpha(dark ? 0 : 1);
- }
-
- if (mOwnerInfo != null) {
- boolean hasText = !TextUtils.isEmpty(mOwnerInfo.getText());
- mOwnerInfo.setVisibility(hasText ? VISIBLE : GONE);
- layoutOwnerInfo();
- }
-
- final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
- mKeyguardSlice.setDarkAmount(mDarkAmount);
- mClockView.setTextColor(blendedTextColor);
- }
-
private void layoutOwnerInfo() {
if (mOwnerInfo != null && mOwnerInfo.getVisibility() != GONE) {
// Animate owner info during wake-up transition
@@ -433,13 +282,6 @@
}
}
- public void setPulsing(boolean pulsing) {
- if (mPulsing == pulsing) {
- return;
- }
- mPulsing = pulsing;
- }
-
private boolean shouldShowLogout() {
return Dependency.get(KeyguardUpdateMonitor.class).isLogoutEnabled()
&& KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM;
@@ -454,9 +296,4 @@
Log.e(TAG, "Failed to logout user", re);
}
}
-
- // TODO: remove this method when a controller is available.
- void setKeyguardSliceViewController(KeyguardSliceViewController keyguardSliceViewController) {
- mKeyguardSliceViewController = keyguardSliceViewController;
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
new file mode 100644
index 0000000..0efb5ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+
+import android.util.Slog;
+import android.view.View;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.ViewController;
+
+import java.util.TimeZone;
+
+import javax.inject.Inject;
+
+/**
+ * Injectable controller for {@link KeyguardStatusView}.
+ */
+public class KeyguardStatusViewController extends ViewController<KeyguardStatusView> {
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ private static final String TAG = "KeyguardStatusViewController";
+
+ private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES =
+ new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+
+ private final KeyguardSliceViewController mKeyguardSliceViewController;
+ private final KeyguardClockSwitchController mKeyguardClockSwitchController;
+ private final KeyguardStateController mKeyguardStateController;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final ConfigurationController mConfigurationController;
+
+ private boolean mKeyguardStatusViewAnimating;
+
+ @Inject
+ public KeyguardStatusViewController(
+ KeyguardStatusView keyguardStatusView,
+ KeyguardSliceViewController keyguardSliceViewController,
+ KeyguardClockSwitchController keyguardClockSwitchController,
+ KeyguardStateController keyguardStateController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ ConfigurationController configurationController) {
+ super(keyguardStatusView);
+ mKeyguardSliceViewController = keyguardSliceViewController;
+ mKeyguardClockSwitchController = keyguardClockSwitchController;
+ mKeyguardStateController = keyguardStateController;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mConfigurationController = configurationController;
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ mKeyguardClockSwitchController.init();
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
+ mConfigurationController.addCallback(mConfigurationListener);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
+ mConfigurationController.removeCallback(mConfigurationListener);
+ }
+
+ /**
+ * Updates views on doze time tick.
+ */
+ public void dozeTimeTick() {
+ refreshTime();
+ mKeyguardSliceViewController.refresh();
+ }
+
+ /**
+ * The amount we're in doze.
+ */
+ public void setDarkAmount(float darkAmount) {
+ mView.setDarkAmount(darkAmount);
+ }
+
+ /**
+ * Set whether or not the lock screen is showing notifications.
+ */
+ public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
+ mKeyguardClockSwitchController.setHasVisibleNotifications(hasVisibleNotifications);
+ }
+
+ /**
+ * If we're presenting a custom clock of just the default one.
+ */
+ public boolean hasCustomClock() {
+ return mKeyguardClockSwitchController.hasCustomClock();
+ }
+
+ /**
+ * Get the height of the logout button.
+ */
+ public int getLogoutButtonHeight() {
+ return mView.getLogoutButtonHeight();
+ }
+
+ /**
+ * Set keyguard status view alpha.
+ */
+ public void setAlpha(float alpha) {
+ if (!mKeyguardStatusViewAnimating) {
+ mView.setAlpha(alpha);
+ }
+ }
+
+ /**
+ * Set pivot x.
+ */
+ public void setPivotX(float pivot) {
+ mView.setPivotX(pivot);
+ }
+
+ /**
+ * Set pivot y.
+ */
+ public void setPivotY(float pivot) {
+ mView.setPivotY(pivot);
+ }
+
+ /**
+ * Get the clock text size.
+ */
+ public float getClockTextSize() {
+ return mKeyguardClockSwitchController.getClockTextSize();
+ }
+
+ /**
+ * Returns the preferred Y position of the clock.
+ *
+ * @param totalHeight The height available to position the clock.
+ * @return Y position of clock.
+ */
+ public int getClockPreferredY(int totalHeight) {
+ return mKeyguardClockSwitchController.getClockPreferredY(totalHeight);
+ }
+
+ /**
+ * Get the height of the keyguard status view.
+ */
+ public int getHeight() {
+ return mView.getHeight();
+ }
+
+ /**
+ * Set whether the view accessibility importance mode.
+ */
+ public void setStatusAccessibilityImportance(int mode) {
+ mView.setImportantForAccessibility(mode);
+ }
+
+ /**
+ * Update position of the view with an optional animation
+ */
+ public void updatePosition(int clockTranslationX, int clockTranslationY,
+ boolean animateClock) {
+ PropertyAnimator.setProperty(mView, AnimatableProperty.X,
+ clockTranslationX, CLOCK_ANIMATION_PROPERTIES, animateClock);
+ PropertyAnimator.setProperty(mView, AnimatableProperty.Y,
+ clockTranslationY, CLOCK_ANIMATION_PROPERTIES, animateClock);
+ }
+
+ /**
+ * Set the visibility of the keyguard status view based on some new state.
+ */
+ public void setKeyguardStatusViewVisibility(
+ int statusBarState,
+ boolean keyguardFadingAway,
+ boolean goingToFullShade,
+ int oldStatusBarState) {
+ mView.animate().cancel();
+ mKeyguardStatusViewAnimating = false;
+ if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD
+ && statusBarState != KEYGUARD) || goingToFullShade) {
+ mKeyguardStatusViewAnimating = true;
+ mView.animate()
+ .alpha(0f)
+ .setStartDelay(0)
+ .setDuration(160)
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .withEndAction(
+ mAnimateKeyguardStatusViewGoneEndRunnable);
+ if (keyguardFadingAway) {
+ mView.animate()
+ .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
+ .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
+ .start();
+ }
+ } else if (oldStatusBarState == StatusBarState.SHADE_LOCKED && statusBarState == KEYGUARD) {
+ mView.setVisibility(View.VISIBLE);
+ mKeyguardStatusViewAnimating = true;
+ mView.setAlpha(0f);
+ mView.animate()
+ .alpha(1f)
+ .setStartDelay(0)
+ .setDuration(320)
+ .setInterpolator(Interpolators.ALPHA_IN)
+ .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
+ } else if (statusBarState == KEYGUARD) {
+ if (keyguardFadingAway) {
+ mKeyguardStatusViewAnimating = true;
+ mView.animate()
+ .alpha(0)
+ .translationYBy(-getHeight() * 0.05f)
+ .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
+ .setDuration(125)
+ .setStartDelay(0)
+ .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable)
+ .start();
+ } else {
+ mView.setVisibility(View.VISIBLE);
+ mView.setAlpha(1f);
+ }
+ } else {
+ mView.setVisibility(View.GONE);
+ mView.setAlpha(1f);
+ }
+ }
+
+ private void refreshTime() {
+ mKeyguardClockSwitchController.refresh();
+ }
+
+ private final ConfigurationController.ConfigurationListener mConfigurationListener =
+ new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onLocaleListChanged() {
+ refreshTime();
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ mKeyguardClockSwitchController.onDensityOrFontScaleChanged();
+ mView.onDensityOrFontScaleChanged();
+ }
+ };
+
+ private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onLockScreenModeChanged(int mode) {
+ mKeyguardClockSwitchController.updateLockScreenMode(mode);
+ }
+
+ @Override
+ public void onTimeChanged() {
+ refreshTime();
+ }
+
+ @Override
+ public void onTimeZoneChanged(TimeZone timeZone) {
+ mKeyguardClockSwitchController.updateTimeZone(timeZone);
+ }
+
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ if (showing) {
+ if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
+ refreshTime();
+ mView.updateOwnerInfo();
+ mView.updateLogoutView();
+ }
+ }
+
+ @Override
+ public void onStartedWakingUp() {
+ mView.setEnableMarquee(true);
+ }
+
+ @Override
+ public void onFinishedGoingToSleep(int why) {
+ mView.setEnableMarquee(false);
+ }
+
+ @Override
+ public void onUserSwitchComplete(int userId) {
+ mKeyguardClockSwitchController.refreshFormat();
+ mView.updateOwnerInfo();
+ mView.updateLogoutView();
+ }
+
+ @Override
+ public void onLogoutEnabledChanged() {
+ mView.updateLogoutView();
+ }
+ };
+
+ private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = () -> {
+ mKeyguardStatusViewAnimating = false;
+ mView.setVisibility(View.INVISIBLE);
+ };
+
+
+ private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = () -> {
+ mKeyguardStatusViewAnimating = false;
+ mView.setVisibility(View.GONE);
+ };
+
+ private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> {
+ mKeyguardStatusViewAnimating = false;
+ };
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index bdb34bb..1a98c20 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -100,8 +100,8 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.util.Assert;
@@ -180,6 +180,10 @@
private static final int MSG_USER_STOPPED = 340;
private static final int MSG_USER_REMOVED = 341;
private static final int MSG_KEYGUARD_GOING_AWAY = 342;
+ private static final int MSG_LOCK_SCREEN_MODE = 343;
+
+ public static final int LOCK_SCREEN_MODE_NORMAL = 0;
+ public static final int LOCK_SCREEN_MODE_LAYOUT_1 = 1;
/** Biometric authentication state: Not listening. */
private static final int BIOMETRIC_STATE_STOPPED = 0;
@@ -263,6 +267,7 @@
private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
mCallbacks = Lists.newArrayList();
private ContentObserver mDeviceProvisionedObserver;
+ private ContentObserver mLockScreenModeObserver;
private boolean mSwitchingUser;
@@ -286,6 +291,7 @@
private boolean mLockIconPressed;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private final Executor mBackgroundExecutor;
+ private int mLockScreenMode;
/**
* Short delay before restarting fingerprint authentication after a successful try. This should
@@ -1694,6 +1700,9 @@
case MSG_KEYGUARD_GOING_AWAY:
handleKeyguardGoingAway((boolean) msg.obj);
break;
+ case MSG_LOCK_SCREEN_MODE:
+ handleLockScreenMode();
+ break;
default:
super.handleMessage(msg);
break;
@@ -1796,7 +1805,7 @@
mIsAutomotive = isAutomotive();
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
mUserManager = context.getSystemService(UserManager.class);
mIsPrimaryUser = mUserManager.isPrimaryUser();
int user = ActivityManager.getCurrentUser();
@@ -1828,6 +1837,23 @@
}
}
}
+
+ updateLockScreenMode();
+ mLockScreenModeObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateLockScreenMode();
+ mHandler.sendEmptyMessage(MSG_LOCK_SCREEN_MODE);
+ }
+ };
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.SHOW_NEW_LOCKSCREEN),
+ false, mLockScreenModeObserver);
+ }
+
+ private void updateLockScreenMode() {
+ mLockScreenMode = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.SHOW_NEW_LOCKSCREEN, 0);
}
private final UserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() {
@@ -2350,6 +2376,20 @@
}
/**
+ * Handle {@link #MSG_LOCK_SCREEN_MODE}
+ */
+ private void handleLockScreenMode() {
+ Assert.isMainThread();
+ if (DEBUG) Log.d(TAG, "handleLockScreenMode(" + mLockScreenMode + ")");
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onLockScreenModeChanged(mLockScreenMode);
+ }
+ }
+ }
+
+ /**
* Handle (@line #MSG_TIMEZONE_UPDATE}
*/
private void handleTimeZoneUpdate(String timeZone) {
@@ -2668,6 +2708,8 @@
callback.onClockVisibilityChanged();
callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible);
callback.onTelephonyCapable(mTelephonyCapable);
+ callback.onLockScreenModeChanged(mLockScreenMode);
+
for (Entry<Integer, SimData> data : mSimDatas.entrySet()) {
final SimData state = data.getValue();
callback.onSimStateChanged(state.subId, state.slotId, state.simState);
@@ -2959,13 +3001,17 @@
mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver);
}
+ if (mLockScreenModeObserver != null) {
+ mContext.getContentResolver().unregisterContentObserver(mLockScreenModeObserver);
+ }
+
try {
ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver);
} catch (RemoteException e) {
Log.d(TAG, "RemoteException onDestroy. cannot unregister userSwitchObserver");
}
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
mBroadcastDispatcher.unregisterReceiver(mBroadcastAllReceiver);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 12e0ecd..3c5eceb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -317,4 +317,9 @@
*/
public void onSecondaryLockscreenRequirementChanged(int userId) { }
+ /**
+ * Called to switch lock screen layout/clock layouts
+ */
+ public void onLockScreenModeChanged(int mode) { }
+
}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
index 21ccff7..1b6476c 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
@@ -18,6 +18,7 @@
import com.android.keyguard.KeyguardClockSwitchController;
import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardStatusViewController;
import dagger.BindsInstance;
import dagger.Subcomponent;
@@ -36,4 +37,7 @@
/** Builds a {@link com.android.keyguard.KeyguardClockSwitchController}. */
KeyguardClockSwitchController getKeyguardClockSwitchController();
+
+ /** Builds a {@link com.android.keyguard.KeyguardStatusViewController}. */
+ KeyguardStatusViewController getKeyguardStatusViewController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index f24644b..9f28e09 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -47,6 +47,7 @@
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -344,6 +345,7 @@
@Inject Lazy<DisplayImeController> mDisplayImeController;
@Inject Lazy<RecordingController> mRecordingController;
@Inject Lazy<ProtoTracer> mProtoTracer;
+ @Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory;
@Inject
public Dependency() {
@@ -541,6 +543,8 @@
mProviders.put(RecordingController.class, mRecordingController::get);
+ mProviders.put(MediaOutputDialogFactory.class, mMediaOutputDialogFactory::get);
+
Dependency.setInstance(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 4657b06..f210d50 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -72,8 +72,6 @@
Key.QS_HAS_TURNED_OFF_MOBILE_DATA,
Key.TOUCHED_RINGER_TOGGLE,
Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP,
- Key.HAS_SEEN_BUBBLES_EDUCATION,
- Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION,
Key.HAS_SEEN_REVERSE_BOTTOM_SHEET,
Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT,
Key.HAS_SEEN_PRIORITY_ONBOARDING
@@ -123,8 +121,6 @@
String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData";
String TOUCHED_RINGER_TOGGLE = "TouchedRingerToggle";
String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip";
- String HAS_SEEN_BUBBLES_EDUCATION = "HasSeenBubblesOnboarding";
- String HAS_SEEN_BUBBLES_MANAGE_EDUCATION = "HasSeenBubblesManageOnboarding";
String HAS_SEEN_REVERSE_BOTTOM_SHEET = "HasSeenReverseBottomSheet";
String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
/** Tracks whether the user has seen the onboarding screen for priority conversations */
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index c4a305e..8147f66 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -317,10 +317,9 @@
updateColorInversion(value);
}
};
-
- mColorInversionSetting.setListening(true);
- mColorInversionSetting.onChange(false);
}
+ mColorInversionSetting.setListening(true);
+ mColorInversionSetting.onChange(false);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
@@ -582,6 +581,10 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
+ if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
+ Log.i(TAG, "ScreenDecorations is disabled");
+ return;
+ }
mHandler.post(() -> {
int oldRotation = mRotation;
mPendingRotationChange = false;
@@ -636,8 +639,8 @@
com.android.internal.R.dimen.rounded_corner_radius_bottom);
final boolean changed = mRoundedDefault.x != newRoundedDefault
- || mRoundedDefaultTop.x != newRoundedDefault
- || mRoundedDefaultBottom.x != newRoundedDefault;
+ || mRoundedDefaultTop.x != newRoundedDefaultTop
+ || mRoundedDefaultBottom.x != newRoundedDefaultBottom;
if (changed) {
// If config_roundedCornerMultipleRadius set as true, ScreenDecorations respect the
@@ -766,6 +769,10 @@
@Override
public void onTuningChanged(String key, String newValue) {
+ if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
+ Log.i(TAG, "ScreenDecorations is disabled");
+ return;
+ }
mHandler.post(() -> {
if (mOverlays == null) return;
if (SIZE.equals(key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index 34efa35..02f34ac 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -42,8 +42,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.CommandQueue;
import java.lang.ref.WeakReference;
@@ -66,11 +66,11 @@
@VisibleForTesting
@Inject
- SizeCompatModeActivityController(Context context, ActivityManagerWrapper am,
+ SizeCompatModeActivityController(Context context, TaskStackChangeListeners listeners,
CommandQueue commandQueue) {
super(context);
mCommandQueue = commandQueue;
- am.registerTaskStackListener(new TaskStackChangeListener() {
+ listeners.registerTaskStackListener(new TaskStackChangeListener() {
@Override
public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
// Note the callback already runs on main thread.
diff --git a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
index 2365f12..47adffc 100644
--- a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
@@ -111,9 +111,7 @@
final String providerPkg = getIntent().getStringExtra("provider_pkg");
if (providerPkg == null || mProviderPkg.equals(providerPkg)) return;
final String callingPkg = getCallingPkg();
- EventLog.writeEvent(0x534e4554, "159145361", getUid(callingPkg), String.format(
- "pkg %s (disguised as %s) attempted to request permission to show %s slices in %s",
- callingPkg, providerPkg, mProviderPkg, mCallingPkg));
+ EventLog.writeEvent(0x534e4554, "159145361", getUid(callingPkg));
}
@Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 1af3897..c6e5f09 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -31,6 +31,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.ImageView;
@@ -46,11 +47,18 @@
*/
class MagnificationModeSwitch {
- private static final int DURATION_MS = 5000;
- private static final int START_DELAY_MS = 3000;
- private final Runnable mAnimationTask;
+ @VisibleForTesting
+ static final long FADING_ANIMATION_DURATION_MS = 300;
+ @VisibleForTesting
+ static final int DEFAULT_FADE_OUT_ANIMATION_DELAY_MS = 3000;
+ private int mUiTimeout;
+ private final Runnable mFadeInAnimationTask;
+ private final Runnable mFadeOutAnimationTask;
+ @VisibleForTesting
+ boolean mIsFadeOutAnimating = false;
private final Context mContext;
+ private final AccessibilityManager mAccessibilityManager;
private final WindowManager mWindowManager;
private final ImageView mImageView;
private final PointF mLastDown = new PointF();
@@ -68,6 +76,7 @@
@VisibleForTesting
MagnificationModeSwitch(Context context, @NonNull ImageView imageView) {
mContext = context;
+ mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mWindowManager = (WindowManager) mContext.getSystemService(
Context.WINDOW_SERVICE);
mParams = createLayoutParams();
@@ -100,12 +109,19 @@
}
});
- mAnimationTask = () -> {
+ mFadeInAnimationTask = () -> {
+ mImageView.animate()
+ .alpha(1f)
+ .setDuration(FADING_ANIMATION_DURATION_MS)
+ .start();
+ };
+ mFadeOutAnimationTask = () -> {
mImageView.animate()
.alpha(0f)
- .setDuration(DURATION_MS)
+ .setDuration(FADING_ANIMATION_DURATION_MS)
.withEndAction(() -> removeButton())
.start();
+ mIsFadeOutAnimating = true;
};
}
@@ -128,7 +144,6 @@
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
- mImageView.setAlpha(1.0f);
mImageView.animate().cancel();
mLastDown.set(event.getRawX(), event.getRawY());
mLastDrag.set(event.getRawX(), event.getRawY());
@@ -169,9 +184,13 @@
if (!mIsVisible) {
return;
}
- mImageView.animate().cancel();
- mWindowManager.removeView(mImageView);
// Reset button status.
+ mImageView.removeCallbacks(mFadeInAnimationTask);
+ mImageView.removeCallbacks(mFadeOutAnimationTask);
+ mImageView.animate().cancel();
+ mIsFadeOutAnimating = false;
+ mImageView.setAlpha(0f);
+ mWindowManager.removeView(mImageView);
mIsVisible = false;
mParams.x = 0;
mParams.y = 0;
@@ -185,14 +204,19 @@
if (!mIsVisible) {
mWindowManager.addView(mImageView, mParams);
mIsVisible = true;
+ mImageView.postOnAnimation(mFadeInAnimationTask);
+ mUiTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(
+ DEFAULT_FADE_OUT_ANIMATION_DELAY_MS,
+ AccessibilityManager.FLAG_CONTENT_ICONS
+ | AccessibilityManager.FLAG_CONTENT_CONTROLS);
}
- mImageView.setAlpha(1.0f);
- // TODO(b/143852371): use accessibility timeout as a delay.
- // Dismiss the magnification switch button after the button is displayed for a period of
- // time.
- mImageView.animate().cancel();
- mImageView.removeCallbacks(mAnimationTask);
- mImageView.postDelayed(mAnimationTask, START_DELAY_MS);
+ if (mIsFadeOutAnimating) {
+ mImageView.animate().cancel();
+ mImageView.setAlpha(1f);
+ }
+ // Refresh the time slot of the fade-out task whenever this method is called.
+ mImageView.removeCallbacks(mFadeOutAnimationTask);
+ mImageView.postOnAnimationDelayed(mFadeOutAnimationTask, mUiTimeout);
}
void onConfigurationChanged(int configDiff) {
@@ -222,6 +246,7 @@
imageView.setClickable(true);
imageView.setFocusable(true);
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+ imageView.setAlpha(0f);
return imageView;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index a705ec7..e9e453b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -72,8 +72,7 @@
super(context);
mHandler = mainHandler;
mLastConfiguration = new Configuration(context.getResources().getConfiguration());
- mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
+ mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mCommandQueue = commandQueue;
mModeSwitchesController = modeSwitchesController;
final WindowMagnificationController controller = new WindowMagnificationController(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index c3474bb..340ca04 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -249,8 +249,8 @@
if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
updateDimensions();
if (isWindowVisible()) {
- mWm.removeView(mMirrorView);
- createMirrorWindow();
+ deleteWindowMagnification();
+ enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN);
}
} else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
onRotate();
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 02a672b..c1c2de1 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -47,6 +47,7 @@
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.StatusBarState;
import java.io.PrintWriter;
@@ -171,6 +172,7 @@
private final DeviceConfigHelper mDeviceConfigHelper;
private final Lazy<StatusBarStateController> mStatusBarStateController;
private final Lazy<ActivityManagerWrapper> mActivityManagerWrapper;
+ private final Lazy<TaskStackChangeListeners> mTaskStackChangeListeners;
private final Lazy<OverviewProxyService> mOverviewProxyService;
private final Lazy<SysUiState> mSysUiFlagContainer;
private final Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
@@ -207,6 +209,7 @@
DeviceConfigHelper deviceConfigHelper,
Lazy<StatusBarStateController> statusBarStateController,
Lazy<ActivityManagerWrapper> activityManagerWrapper,
+ Lazy<TaskStackChangeListeners> taskStackChangeListeners,
Lazy<OverviewProxyService> overviewProxyService,
Lazy<SysUiState> sysUiFlagContainer,
Lazy<WakefulnessLifecycle> wakefulnessLifecycle,
@@ -218,6 +221,7 @@
mDeviceConfigHelper = deviceConfigHelper;
mStatusBarStateController = statusBarStateController;
mActivityManagerWrapper = activityManagerWrapper;
+ mTaskStackChangeListeners = taskStackChangeListeners;
mOverviewProxyService = overviewProxyService;
mSysUiFlagContainer = sysUiFlagContainer;
mWakefulnessLifecycle = wakefulnessLifecycle;
@@ -245,7 +249,7 @@
ActivityManager.RunningTaskInfo runningTaskInfo =
mActivityManagerWrapper.get().getRunningTask();
mRunningTaskId = runningTaskInfo == null ? 0 : runningTaskInfo.taskId;
- mActivityManagerWrapper.get().registerTaskStackListener(mTaskStackChangeListener);
+ mTaskStackChangeListeners.get().registerTaskStackListener(mTaskStackChangeListener);
mOverviewProxyService.get().addCallback(mOverviewProxyListener);
mSysUiFlagContainer.get().addCallback(mSysUiStateCallback);
mIsAwake = mWakefulnessLifecycle.get().getWakefulness()
@@ -300,7 +304,7 @@
mContext = null;
}
mStatusBarStateController.get().removeCallback(mStatusBarStateListener);
- mActivityManagerWrapper.get().unregisterTaskStackListener(mTaskStackChangeListener);
+ mTaskStackChangeListeners.get().unregisterTaskStackListener(mTaskStackChangeListener);
mOverviewProxyService.get().removeCallback(mOverviewProxyListener);
mSysUiFlagContainer.get().removeCallback(mSysUiStateCallback);
mWakefulnessLifecycle.get().removeObserver(mWakefulnessLifecycleObserver);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index 50d559b..61951cc 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -35,6 +35,7 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -82,7 +83,6 @@
mStatusBarOptionalLazy = statusBarOptionalLazy;
mStatusBarStateController = Dependency.get(StatusBarStateController.class);
- ActivityManagerWrapper activityManagerWrapper = ActivityManagerWrapper.getInstance();
mDefaultHome = getCurrentDefaultHome();
bootCompleteCache.addListener(() -> mDefaultHome = getCurrentDefaultHome());
IntentFilter intentFilter = new IntentFilter();
@@ -95,12 +95,13 @@
mDefaultHome = getCurrentDefaultHome();
}
}, intentFilter);
- mLauncherShowing = isLauncherShowing(activityManagerWrapper.getRunningTask());
- activityManagerWrapper.registerTaskStackListener(new TaskStackChangeListener() {
- @Override
- public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
- mLauncherShowing = isLauncherShowing(taskInfo);
- }
+ mLauncherShowing = isLauncherShowing(ActivityManagerWrapper.getInstance().getRunningTask());
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(
+ new TaskStackChangeListener() {
+ @Override
+ public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+ mLauncherShowing = isLauncherShowing(taskInfo);
+ }
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 529af22..1891daf 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -15,8 +15,8 @@
*/
package com.android.systemui.bubbles;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.os.AsyncTask.Status.FINISHED;
-import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
@@ -256,7 +256,8 @@
}
/**
- * Cleanup expanded view for bubbles going into overflow.
+ * Call this to clean up the task for the bubble. Ensure this is always called when done with
+ * the bubble.
*/
void cleanupExpandedView() {
if (mExpandedView != null) {
@@ -270,8 +271,7 @@
}
/**
- * Call when the views should be removed, ensure this is called to clean up ActivityView
- * content.
+ * Call when all the views should be removed/cleaned up.
*/
void cleanupViews() {
cleanupExpandedView();
@@ -468,14 +468,6 @@
return mIntentActive;
}
- /**
- * @return the display id of the virtual display on which bubble contents is drawn.
- */
- @Override
- public int getDisplayId() {
- return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
- }
-
public InstanceId getInstanceId() {
return mInstanceId;
}
@@ -490,6 +482,14 @@
}
/**
+ * @return the task id of the task in which bubble contents is drawn.
+ */
+ @Override
+ public int getTaskId() {
+ return mExpandedView != null ? mExpandedView.getTaskId() : INVALID_TASK_ID;
+ }
+
+ /**
* Should be invoked whenever a Bubble is accessed (selected while expanded).
*/
void markAsAccessedAt(long lastAccessedMillis) {
@@ -646,14 +646,14 @@
}
private int getDimenForPackageUser(Context context, int resId, String pkg, int userId) {
- PackageManager pm = context.getPackageManager();
Resources r;
if (pkg != null) {
try {
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_SYSTEM;
}
- r = pm.getResourcesForApplicationAsUser(pkg, userId);
+ r = context.createContextAsUser(UserHandle.of(userId), /* flags */ 0)
+ .getPackageManager().getResourcesForApplication(pkg);
return r.getDimensionPixelSize(resId);
} catch (PackageManager.NameNotFoundException ex) {
// Uninstalled, don't care
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 2372529..3f94b00 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,6 +16,7 @@
package com.android.systemui.bubbles;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
@@ -27,8 +28,6 @@
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -46,6 +45,7 @@
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -70,7 +70,6 @@
import android.util.Log;
import android.util.Pair;
import android.util.SparseSetArray;
-import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -80,15 +79,18 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
import com.android.systemui.bubbles.dagger.BubbleModule;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
@@ -111,6 +113,7 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
@@ -168,8 +171,8 @@
private final ShadeController mShadeController;
private final FloatingContentCoordinator mFloatingContentCoordinator;
private final BubbleDataRepository mDataRepository;
- private BubbleLogger mLogger = new BubbleLoggerImpl();
-
+ private BubbleLogger mLogger;
+ private final Handler mMainHandler;
private BubbleData mBubbleData;
private ScrimView mBubbleScrim;
@Nullable private BubbleStackView mStackView;
@@ -241,6 +244,8 @@
private boolean mInflateSynchronously;
+ private MultiWindowTaskListener mTaskListener;
+
// TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
private final List<NotifCallback> mCallbacks = new ArrayList<>();
@@ -365,20 +370,24 @@
FeatureFlags featureFlags,
DumpManager dumpManager,
FloatingContentCoordinator floatingContentCoordinator,
- BubbleDataRepository dataRepository,
SysUiState sysUiState,
INotificationManager notificationManager,
@Nullable IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
- LauncherApps launcherApps) {
+ LauncherApps launcherApps,
+ UiEventLogger uiEventLogger,
+ @Main Handler mainHandler,
+ ShellTaskOrganizer organizer) {
+ BubbleLogger logger = new BubbleLogger(uiEventLogger);
return new BubbleController(context, notificationShadeWindowController,
- statusBarStateController, shadeController, new BubbleData(context), synchronizer,
- configurationController, interruptionStateProvider, zenModeController,
+ statusBarStateController, shadeController, new BubbleData(context, logger),
+ synchronizer, configurationController, interruptionStateProvider, zenModeController,
notifUserManager, groupManager, entryManager, notifPipeline, featureFlags,
- dumpManager, floatingContentCoordinator, dataRepository, sysUiState,
- notificationManager, statusBarService, windowManager, windowManagerShellWrapper,
- launcherApps);
+ dumpManager, floatingContentCoordinator,
+ new BubbleDataRepository(context, launcherApps), sysUiState, notificationManager,
+ statusBarService, windowManager, windowManagerShellWrapper, launcherApps, logger,
+ mainHandler, organizer);
}
/**
@@ -407,7 +416,10 @@
@Nullable IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
- LauncherApps launcherApps) {
+ LauncherApps launcherApps,
+ BubbleLogger bubbleLogger,
+ Handler mainHandler,
+ ShellTaskOrganizer organizer) {
dumpManager.registerDumpable(TAG, this);
mContext = context;
mShadeController = shadeController;
@@ -417,6 +429,8 @@
mFloatingContentCoordinator = floatingContentCoordinator;
mDataRepository = dataRepository;
mINotificationManager = notificationManager;
+ mLogger = bubbleLogger;
+ mMainHandler = mainHandler;
mZenModeController.addCallback(new ZenModeController.Callback() {
@Override
public void onZenChanged(int zen) {
@@ -480,7 +494,7 @@
statusBarStateController.addCallback(mStatusBarStateListener);
mTaskStackListener = new BubbleTaskStackListener();
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
try {
windowManagerShellWrapper.addPinnedStackListener(new BubblesImeListener());
@@ -515,6 +529,7 @@
});
mBubbleIconFactory = new BubbleIconFactory(context);
+ mTaskListener = new MultiWindowTaskListener(mMainHandler, organizer);
launcherApps.registerCallback(new LauncherApps.Callback() {
@Override
@@ -577,6 +592,12 @@
}
}
+ private void onBubbleExpandChanged(boolean shouldExpand) {
+ mSysUiState
+ .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
+ .commitUpdate(mContext.getDisplayId());
+ }
+
private void setupNEM() {
mNotificationEntryManager.addNotificationEntryListener(
new NotificationEntryListener() {
@@ -783,6 +804,11 @@
return mBubbleData.getOverflowBubbles();
}
+ @Override
+ public MultiWindowTaskListener getTaskManager() {
+ return mTaskListener;
+ }
+
/**
* BubbleStackView is lazily created by this method the first time a Bubble is added. This
* method initializes the stack view and adds it to the StatusBar just above the scrim.
@@ -791,8 +817,8 @@
if (mStackView == null) {
mStackView = new BubbleStackView(
mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator,
- mSysUiState, this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged,
- this::hideCurrentInputMethod);
+ this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged,
+ this::hideCurrentInputMethod, this::onBubbleExpandChanged);
mStackView.setStackStartPosition(mPositionFromRemovedStack);
mStackView.addView(mBubbleScrim);
if (mExpandListener != null) {
@@ -825,9 +851,8 @@
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
- // Start not focusable - we'll become focusable when expanded so the ActivityView
- // can use the IME.
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
PixelFormat.TRANSLUCENT);
@@ -843,16 +868,13 @@
mAddedToWindowManager = true;
mWindowManager.addView(mStackView, mWmLayoutParams);
} catch (IllegalStateException e) {
- // This means the stack has already been added. This shouldn't happen, since we keep
- // track of that, but just in case, update the previously added view's layout params.
+ // This means the stack has already been added. This shouldn't happen...
e.printStackTrace();
- updateWmFlags();
}
}
private void onImeVisibilityChanged(boolean imeVisible) {
mImeVisible = imeVisible;
- updateWmFlags();
}
/** Removes the BubbleStackView from the WindowManager if it's there. */
@@ -879,35 +901,6 @@
}
/**
- * Updates the BubbleStackView's WindowManager.LayoutParams, and updates the WindowManager with
- * the new params if the stack has been added.
- */
- private void updateWmFlags() {
- if (mStackView == null) {
- return;
- }
- if (isStackExpanded() && !mImeVisible) {
- // If we're expanded, and the IME isn't visible, we want to be focusable. This ensures
- // that any taps within Bubbles (including on the ActivityView) results in Bubbles
- // receiving focus and clearing it from any other windows that might have it.
- mWmLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- } else {
- // If we're collapsed, we don't want to be focusable since tapping on the stack would
- // steal focus from apps. We also don't want to be focusable if the IME is visible,
- mWmLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- }
-
- if (mAddedToWindowManager) {
- try {
- mWindowManager.updateViewLayout(mStackView, mWmLayoutParams);
- } catch (IllegalArgumentException e) {
- // If the stack is somehow not there, ignore the attempt to update it.
- e.printStackTrace();
- }
- }
- }
-
- /**
* Called by the BubbleStackView and whenever all bubbles have animated out, and none have been
* added in the meantime.
*/
@@ -1015,8 +1008,6 @@
if (listener != null) {
listener.onBubbleExpandChanged(isExpanding, key);
}
-
- updateWmFlags();
});
if (mStackView != null) {
mStackView.setExpandListener(mExpandListener);
@@ -1060,7 +1051,7 @@
@Override
public boolean isBubbleExpanded(NotificationEntry entry) {
return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null
- && mBubbleData.getSelectedBubble().getKey().equals(entry.getKey()) ? true : false;
+ && mBubbleData.getSelectedBubble().getKey().equals(entry.getKey());
}
@Override
@@ -1115,13 +1106,6 @@
}
}
- @Override
- public void performBackPressIfNeeded() {
- if (mStackView != null) {
- mStackView.performBackPressIfNeeded();
- }
- }
-
/**
* Adds or updates a bubble associated with the provided notification entry.
*
@@ -1602,19 +1586,21 @@
mStackView.updateContentDescription();
}
- @Override
- public int getExpandedDisplayId(Context context) {
+ /**
+ * The task id of the expanded view, if the stack is expanded and not occluded by the
+ * status bar, otherwise returns {@link ActivityTaskManager#INVALID_TASK_ID}.
+ */
+ private int getExpandedTaskId() {
if (mStackView == null) {
- return INVALID_DISPLAY;
+ return INVALID_TASK_ID;
}
- final boolean defaultDisplay = context.getDisplay() != null
- && context.getDisplay().getDisplayId() == DEFAULT_DISPLAY;
final BubbleViewProvider expandedViewProvider = mStackView.getExpandedBubble();
- if (defaultDisplay && expandedViewProvider != null && isStackExpanded()
+ if (expandedViewProvider != null && isStackExpanded()
+ && !mStackView.isExpansionAnimating()
&& !mNotificationShadeWindowController.getPanelExpanded()) {
- return expandedViewProvider.getDisplayId();
+ return expandedViewProvider.getTaskId();
}
- return INVALID_DISPLAY;
+ return INVALID_TASK_ID;
}
@VisibleForTesting
@@ -1648,18 +1634,17 @@
@Override
public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
- if (mStackView != null && taskInfo.displayId == Display.DEFAULT_DISPLAY) {
- if (!mStackView.isExpansionAnimating()) {
- mBubbleData.setExpanded(false);
- }
+ int expandedId = getExpandedTaskId();
+ if (expandedId != INVALID_TASK_ID && expandedId != taskInfo.taskId) {
+ mBubbleData.setExpanded(false);
}
}
@Override
- public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
+ public void onActivityRestartAttempt(RunningTaskInfo taskInfo, boolean homeTaskVisible,
boolean clearedTask, boolean wasVisible) {
for (Bubble b : mBubbleData.getBubbles()) {
- if (b.getDisplayId() == task.displayId) {
+ if (taskInfo.taskId == b.getTaskId()) {
mBubbleData.setSelectedBubble(b);
mBubbleData.setExpanded(true);
return;
@@ -1667,43 +1652,6 @@
}
}
- @Override
- public void onActivityLaunchOnSecondaryDisplayRerouted() {
- if (mStackView != null) {
- mBubbleData.setExpanded(false);
- }
- }
-
- @Override
- public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
- if (mStackView != null && taskInfo.displayId == getExpandedDisplayId(mContext)) {
- if (mImeVisible) {
- hideCurrentInputMethod();
- } else {
- mBubbleData.setExpanded(false);
- }
- }
- }
-
- @Override
- public void onSingleTaskDisplayDrawn(int displayId) {
- if (mStackView == null) {
- return;
- }
- mStackView.showExpandedViewContents(displayId);
- }
-
- @Override
- public void onSingleTaskDisplayEmpty(int displayId) {
- final BubbleViewProvider expandedBubble = mStackView != null
- ? mStackView.getExpandedBubble()
- : null;
- int expandedId = expandedBubble != null ? expandedBubble.getDisplayId() : -1;
- if (mStackView != null && mStackView.isExpanded() && expandedId == displayId) {
- mBubbleData.setExpanded(false);
- }
- mBubbleData.notifyDisplayEmpty(displayId);
- }
}
/**
@@ -1747,6 +1695,7 @@
}
/** PinnedStackListener that dispatches IME visibility updates to the stack. */
+ //TODO(b/170442945): Better way to do this / insets listener?
private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 55ecb22..b4626f2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -32,10 +32,9 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController.DismissReason;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.SysUiStatsLog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -53,10 +52,9 @@
/**
* Keeps track of active bubbles.
*/
-@SysUISingleton
public class BubbleData {
- private BubbleLoggerImpl mLogger = new BubbleLoggerImpl();
+ private BubbleLogger mLogger;
private int mCurrentUserId;
@@ -155,8 +153,9 @@
*/
private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>();
- public BubbleData(Context context) {
+ public BubbleData(Context context, BubbleLogger bubbleLogger) {
mContext = context;
+ mLogger = bubbleLogger;
mBubbles = new ArrayList<>();
mOverflowBubbles = new ArrayList<>();
mPendingBubbles = new HashMap<>();
@@ -552,22 +551,6 @@
dispatchPendingChanges();
}
- /**
- * Indicates that the provided display is no longer in use and should be cleaned up.
- *
- * @param displayId the id of the display to clean up.
- */
- void notifyDisplayEmpty(int displayId) {
- for (Bubble b : mBubbles) {
- if (b.getDisplayId() == displayId) {
- if (b.getExpandedView() != null) {
- b.getExpandedView().notifyDisplayEmpty();
- }
- return;
- }
- }
- }
-
private void dispatchPendingChanges() {
if (mListener != null && mStateChange.anythingChanged()) {
mListener.applyUpdate(mStateChange);
@@ -618,12 +601,12 @@
* @param normalX Normalized x position of the stack
* @param normalY Normalized y position of the stack
*/
- void logBubbleEvent(@Nullable BubbleViewProvider provider, int action, String packageName,
+ void logBubbleEvent(@Nullable BubbleViewProvider provider, int action, String packageName,
int bubbleCount, int bubbleIndex, float normalX, float normalY) {
if (provider == null) {
mLogger.logStackUiChanged(packageName, action, bubbleCount, normalX, normalY);
} else if (provider.getKey().equals(BubbleOverflow.KEY)) {
- if (action == SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED) {
+ if (action == FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED) {
mLogger.logShowOverflow(packageName, mCurrentUserId);
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
index f129d31..2ab9e87 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
@@ -17,6 +17,7 @@
import android.annotation.SuppressLint
import android.annotation.UserIdInt
+import android.content.Context
import android.content.pm.LauncherApps
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC
@@ -26,21 +27,16 @@
import com.android.systemui.bubbles.storage.BubbleEntity
import com.android.systemui.bubbles.storage.BubblePersistentRepository
import com.android.systemui.bubbles.storage.BubbleVolatileRepository
-import com.android.systemui.dagger.SysUISingleton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
-import javax.inject.Inject
-@SysUISingleton
-internal class BubbleDataRepository @Inject constructor(
- private val volatileRepository: BubbleVolatileRepository,
- private val persistentRepository: BubblePersistentRepository,
- private val launcherApps: LauncherApps
-) {
+internal class BubbleDataRepository(context: Context, private val launcherApps: LauncherApps) {
+ private val volatileRepository = BubbleVolatileRepository(launcherApps)
+ private val persistentRepository = BubblePersistentRepository(context)
private val ioScope = CoroutineScope(Dispatchers.IO)
private val uiScope = CoroutineScope(Dispatchers.Main)
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 3af36a9..98a2257 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,16 +16,10 @@
package com.android.systemui.bubbles;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-import static android.graphics.PixelFormat.TRANSPARENT;
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.InsetsState.ITYPE_IME;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
@@ -34,10 +28,7 @@
import android.annotation.NonNull;
import android.annotation.SuppressLint;
-import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
-import android.app.ActivityView;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -50,19 +41,13 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
-import android.hardware.display.VirtualDisplay;
-import android.os.Binder;
import android.os.Bundle;
-import android.os.RemoteException;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.Gravity;
import android.view.SurfaceControl;
-import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
-import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
@@ -84,18 +69,6 @@
*/
public class BubbleExpandedView extends LinearLayout {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
- private static final String WINDOW_TITLE = "ImeInsetsWindowWithoutContent";
-
- private enum ActivityViewStatus {
- // ActivityView is being initialized, cannot start an activity yet.
- INITIALIZING,
- // ActivityView is initialized, and ready to start an activity.
- INITIALIZED,
- // Activity runs in the ActivityView.
- ACTIVITY_STARTED,
- // ActivityView is released, so activity launching will no longer be permitted.
- RELEASED,
- }
// The triangle pointing to the expanded view
private View mPointerView;
@@ -103,16 +76,11 @@
@Nullable private int[] mExpandedViewContainerLocation;
private AlphaOptimizedButton mSettingsIcon;
+ private TaskView mTaskView;
- // Views for expanded state
- private ActivityView mActivityView;
+ private int mTaskId = INVALID_TASK_ID;
- private ActivityViewStatus mActivityViewStatus = ActivityViewStatus.INITIALIZING;
- private int mTaskId = -1;
-
- private PendingIntent mPendingIntent;
-
- private boolean mKeyboardVisible;
+ private boolean mImeVisible;
private boolean mNeedsNewHeight;
private Point mDisplaySize;
@@ -123,123 +91,104 @@
private int mPointerHeight;
private ShapeDrawable mPointerDrawable;
private int mExpandedViewPadding;
-
+ private float mCornerRadius = 0f;
@Nullable private Bubble mBubble;
+ private PendingIntent mPendingIntent;
private boolean mIsOverflow;
private Bubbles mBubbles = Dependency.get(Bubbles.class);
private WindowManager mWindowManager;
- private ActivityManager mActivityManager;
-
private BubbleStackView mStackView;
- private View mVirtualImeView;
- private WindowManager mVirtualDisplayWindowManager;
- private boolean mImeShowing = false;
- private float mCornerRadius = 0f;
/**
* Container for the ActivityView that has a solid, round-rect background that shows if the
* ActivityView hasn't loaded.
*/
- private FrameLayout mActivityViewContainer = new FrameLayout(getContext());
+ private final FrameLayout mExpandedViewContainer = new FrameLayout(getContext());
- /** The SurfaceView that the ActivityView draws to. */
- @Nullable private SurfaceView mActivitySurface;
+ private final TaskView.Listener mTaskViewListener = new TaskView.Listener() {
+ private boolean mInitialized = false;
+ private boolean mDestroyed = false;
- private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
@Override
- public void onActivityViewReady(ActivityView view) {
+ public void onInitialized() {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "onActivityViewReady: mActivityViewStatus=" + mActivityViewStatus
+ Log.d(TAG, "onActivityViewReady: destroyed=" + mDestroyed
+ + " initialized=" + mInitialized
+ " bubble=" + getBubbleKey());
}
- switch (mActivityViewStatus) {
- case INITIALIZING:
- case INITIALIZED:
- // Custom options so there is no activity transition animation
- ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
- 0 /* enterResId */, 0 /* exitResId */);
- options.setTaskAlwaysOnTop(true);
- // Soptions.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- // Post to keep the lifecycle normal
- post(() -> {
- if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "onActivityViewReady: calling startActivity, "
- + "bubble=" + getBubbleKey());
- }
- if (mActivityView == null) {
- mBubbles.removeBubble(getBubbleKey(),
- BubbleController.DISMISS_INVALID_INTENT);
- return;
- }
- try {
- if (!mIsOverflow && mBubble.hasMetadataShortcutId()
- && mBubble.getShortcutInfo() != null) {
- options.setApplyActivityFlagsForBubbles(true);
- mActivityView.startShortcutActivity(mBubble.getShortcutInfo(),
- options, null /* sourceBounds */);
- } else {
- Intent fillInIntent = new Intent();
- // Apply flags to make behaviour match documentLaunchMode=always.
- fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
- fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
- if (mBubble != null) {
- mBubble.setIntentActive();
- }
- mActivityView.startActivity(mPendingIntent, fillInIntent, options);
- }
- } catch (RuntimeException e) {
- // If there's a runtime exception here then there's something
- // wrong with the intent, we can't really recover / try to populate
- // the bubble again so we'll just remove it.
- Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
- + ", " + e.getMessage() + "; removing bubble");
- mBubbles.removeBubble(getBubbleKey(),
- BubbleController.DISMISS_INVALID_INTENT);
- }
- });
- mActivityViewStatus = ActivityViewStatus.ACTIVITY_STARTED;
- break;
- case ACTIVITY_STARTED:
- post(() -> mActivityManager.moveTaskToFront(mTaskId, 0));
- break;
+
+ if (mDestroyed || mInitialized) {
+ return;
}
+ // Custom options so there is no activity transition animation
+ ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
+ 0 /* enterResId */, 0 /* exitResId */);
+
+ // TODO: I notice inconsistencies in lifecycle
+ // Post to keep the lifecycle normal
+ post(() -> {
+ if (DEBUG_BUBBLE_EXPANDED_VIEW) {
+ Log.d(TAG, "onActivityViewReady: calling startActivity, bubble="
+ + getBubbleKey());
+ }
+ try {
+ if (!mIsOverflow && mBubble.hasMetadataShortcutId()) {
+ mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
+ options, null /* sourceBounds */);
+ } else {
+ Intent fillInIntent = new Intent();
+ // Apply flags to make behaviour match documentLaunchMode=always.
+ fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ if (mBubble != null) {
+ mBubble.setIntentActive();
+ }
+ mTaskView.startActivity(mPendingIntent, fillInIntent, options);
+ }
+ } catch (RuntimeException e) {
+ // If there's a runtime exception here then there's something
+ // wrong with the intent, we can't really recover / try to populate
+ // the bubble again so we'll just remove it.
+ Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
+ + ", " + e.getMessage() + "; removing bubble");
+ mBubbles.removeBubble(getBubbleKey(),
+ BubbleController.DISMISS_INVALID_INTENT);
+ }
+ });
+ mInitialized = true;
}
@Override
- public void onActivityViewDestroyed(ActivityView view) {
- if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "onActivityViewDestroyed: mActivityViewStatus=" + mActivityViewStatus
- + " bubble=" + getBubbleKey());
- }
- mActivityViewStatus = ActivityViewStatus.RELEASED;
+ public void onReleased() {
+ mDestroyed = true;
}
@Override
- public void onTaskCreated(int taskId, ComponentName componentName) {
+ public void onTaskCreated(int taskId, ComponentName name) {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
Log.d(TAG, "onTaskCreated: taskId=" + taskId
+ " bubble=" + getBubbleKey());
}
- // Since Bubble ActivityView applies singleTaskDisplay this is
- // guaranteed to only be called once per ActivityView. The taskId is
- // saved to use for removeTask, preventing appearance in recent tasks.
+ // The taskId is saved to use for removeTask, preventing appearance in recent tasks.
mTaskId = taskId;
+
+ // With the task org, the taskAppeared callback will only happen once the task has
+ // already drawn
+ setContentVisibility(true);
}
- /**
- * This is only called for tasks on this ActivityView, which is also set to
- * single-task mode -- meaning never more than one task on this display. If a task
- * is being removed, it's the top Activity finishing and this bubble should
- * be removed or collapsed.
- */
+ @Override
+ public void onTaskVisibilityChanged(int taskId, boolean visible) {
+ setContentVisibility(visible);
+ }
+
@Override
public void onTaskRemovalStarted(int taskId) {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
Log.d(TAG, "onTaskRemovalStarted: taskId=" + taskId
- + " mActivityViewStatus=" + mActivityViewStatus
+ " bubble=" + getBubbleKey());
}
if (mBubble != null) {
@@ -248,6 +197,13 @@
BubbleController.DISMISS_TASK_FINISHED));
}
}
+
+ @Override
+ public void onBackPressedOnTaskRoot(int taskId) {
+ if (mTaskId == taskId && mStackView.isExpanded()) {
+ mBubbles.collapseStack();
+ }
+ }
};
public BubbleExpandedView(Context context) {
@@ -266,7 +222,6 @@
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
updateDimensions();
- mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
}
void updateDimensions() {
@@ -284,9 +239,6 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "onFinishInflate: bubble=" + getBubbleKey());
- }
Resources res = getResources();
mPointerView = findViewById(R.id.pointer_view);
@@ -301,35 +253,21 @@
R.dimen.bubble_manage_button_height);
mSettingsIcon = findViewById(R.id.settings_button);
- mActivityView = new ActivityView.Builder(mContext)
- .setSingleInstance(true)
- .setDisableSurfaceViewBackgroundLayer(true)
- .setUseTrustedDisplay(true)
- .build();
-
+ mTaskView = new TaskView(mContext, mBubbles.getTaskManager());
// Set ActivityView's alpha value as zero, since there is no view content to be shown.
setContentVisibility(false);
- mActivityViewContainer.setOutlineProvider(new ViewOutlineProvider() {
+ mExpandedViewContainer.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
}
});
- mActivityViewContainer.setClipToOutline(true);
- mActivityViewContainer.addView(mActivityView);
- mActivityViewContainer.setLayoutParams(
+ mExpandedViewContainer.setClipToOutline(true);
+ mExpandedViewContainer.addView(mTaskView);
+ mExpandedViewContainer.setLayoutParams(
new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
- addView(mActivityViewContainer);
-
- if (mActivityView != null
- && mActivityView.getChildCount() > 0
- && mActivityView.getChildAt(0) instanceof SurfaceView) {
- // Retrieve the surface from the ActivityView so we can screenshot it and change its
- // z-ordering. This should always be possible, since ActivityView's constructor adds the
- // SurfaceView as its first child.
- mActivitySurface = (SurfaceView) mActivityView.getChildAt(0);
- }
+ addView(mExpandedViewContainer);
// Expanded stack layout, top to bottom:
// Expanded view container
@@ -337,33 +275,22 @@
// ==> expanded view
// ==> activity view
// ==> manage button
- bringChildToFront(mActivityView);
+ bringChildToFront(mTaskView);
bringChildToFront(mSettingsIcon);
+ mTaskView.setListener(mTaskViewListener);
applyThemeAttrs();
- setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
- // Keep track of IME displaying because we should not make any adjustments that might
- // cause a config change while the IME is displayed otherwise it'll loose focus.
- final int keyboardHeight = insets.getSystemWindowInsetBottom()
- - insets.getStableInsetBottom();
- mKeyboardVisible = keyboardHeight != 0;
- if (!mKeyboardVisible && mNeedsNewHeight) {
- updateHeight();
- }
- return view.onApplyWindowInsets(insets);
- });
-
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
setPadding(mExpandedViewPadding, mExpandedViewPadding, mExpandedViewPadding,
mExpandedViewPadding);
setOnTouchListener((view, motionEvent) -> {
- if (!usingActivityView()) {
+ if (mTaskView == null) {
return false;
}
final Rect avBounds = new Rect();
- mActivityView.getBoundsOnScreen(avBounds);
+ mTaskView.getBoundsOnScreen(avBounds);
// Consume and ignore events on the expanded view padding that are within the
// ActivityView's vertical bounds. These events are part of a back gesture, and so they
@@ -389,51 +316,58 @@
}
/**
- * Asks the ActivityView's surface to draw on top of all other views in the window. This is
- * useful for ordering surfaces during animations, but should otherwise be set to false so that
- * bubbles and menus can draw over the ActivityView.
+ * Sets whether the surface displaying app content should sit on top. This is useful for
+ * ordering surfaces during animations. When content is drawn on top of the app (e.g. bubble
+ * being dragged out, the manage menu) this is set to false, otherwise it should be true.
*/
void setSurfaceZOrderedOnTop(boolean onTop) {
- if (mActivitySurface == null) {
+ if (mTaskView == null) {
return;
}
-
- mActivitySurface.setZOrderedOnTop(onTop, true);
+ mTaskView.setZOrderedOnTop(onTop, true /* allowDynamicChange */);
}
- /** Return a GraphicBuffer with the contents of the ActivityView's underlying surface. */
+ void setImeVisible(boolean visible) {
+ mImeVisible = visible;
+ if (!mImeVisible && mNeedsNewHeight) {
+ updateHeight();
+ }
+ }
+
+ /** Return a GraphicBuffer with the contents of the task view surface. */
@Nullable
SurfaceControl.ScreenshotHardwareBuffer snapshotActivitySurface() {
- if (mActivitySurface == null) {
+ if (mTaskView == null) {
return null;
}
-
return SurfaceControl.captureLayers(
- mActivitySurface.getSurfaceControl(),
- new Rect(0, 0, mActivityView.getWidth(), mActivityView.getHeight()),
+ mTaskView.getSurfaceControl(),
+ new Rect(0, 0, mTaskView.getWidth(), mTaskView.getHeight()),
1 /* scale */);
}
- int[] getActivityViewLocationOnScreen() {
- if (mActivityView != null) {
- return mActivityView.getLocationOnScreen();
+ int[] getTaskViewLocationOnScreen() {
+ if (mTaskView != null) {
+ return mTaskView.getLocationOnScreen();
} else {
return new int[]{0, 0};
}
}
+ // TODO: Could listener be passed when we pass StackView / can we avoid setting this like this
void setManageClickListener(OnClickListener manageClickListener) {
- findViewById(R.id.settings_button).setOnClickListener(manageClickListener);
+ mSettingsIcon.setOnClickListener(manageClickListener);
}
/**
- * Updates the ActivityView's obscured touchable region. This calls onLocationChanged, which
- * results in a call to {@link BubbleStackView#subtractObscuredTouchableRegion}. This is useful
- * if a view has been added or removed from on top of the ActivityView, such as the manage menu.
+ * Updates the obscured touchable region for the task surface. This calls onLocationChanged,
+ * which results in a call to {@link BubbleStackView#subtractObscuredTouchableRegion}. This is
+ * useful if a view has been added or removed from on top of the ActivityView, such as the
+ * manage menu.
*/
void updateObscuredTouchableRegion() {
- if (mActivityView != null) {
- mActivityView.onLocationChanged();
+ if (mTaskView != null) {
+ mTaskView.onLocationChanged();
}
}
@@ -442,12 +376,12 @@
android.R.attr.dialogCornerRadius,
android.R.attr.colorBackgroundFloating});
mCornerRadius = ta.getDimensionPixelSize(0, 0);
- mActivityViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE));
+ mExpandedViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE));
ta.recycle();
- if (mActivityView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
+ if (mTaskView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
mContext.getResources())) {
- mActivityView.setCornerRadius(mCornerRadius);
+ mTaskView.setCornerRadius(mCornerRadius);
}
final int mode =
@@ -466,11 +400,8 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mKeyboardVisible = false;
+ mImeVisible = false;
mNeedsNewHeight = false;
- if (mActivityView != null) {
- setImeWindowToDisplay(0, 0);
- }
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
Log.d(TAG, "onDetachedFromWindow: bubble=" + getBubbleKey());
}
@@ -492,84 +423,23 @@
final float alpha = visibility ? 1f : 0f;
mPointerView.setAlpha(alpha);
-
- if (mActivityView != null && alpha != mActivityView.getAlpha()) {
- mActivityView.setAlpha(alpha);
- mActivityView.bringToFront();
+ if (mTaskView == null) {
+ return;
+ }
+ if (alpha != mTaskView.getAlpha()) {
+ mTaskView.setAlpha(alpha);
}
}
- @Nullable ActivityView getActivityView() {
- return mActivityView;
+ @Nullable
+ View getTaskView() {
+ return mTaskView;
}
int getTaskId() {
return mTaskId;
}
- /**
- * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
- * This should be done post-move and post-animation.
- */
- void updateInsets(WindowInsets insets) {
- if (usingActivityView()) {
- int[] screenLoc = mActivityView.getLocationOnScreen();
- final int activityViewBottom = screenLoc[1] + mActivityView.getHeight();
- final int keyboardTop = mDisplaySize.y - Math.max(insets.getSystemWindowInsetBottom(),
- insets.getDisplayCutout() != null
- ? insets.getDisplayCutout().getSafeInsetBottom()
- : 0);
- setImeWindowToDisplay(getWidth(), Math.max(activityViewBottom - keyboardTop, 0));
- }
- }
-
- private void setImeWindowToDisplay(int w, int h) {
- if (getVirtualDisplayId() == INVALID_DISPLAY) {
- return;
- }
- if (h == 0 || w == 0) {
- if (mImeShowing) {
- mVirtualImeView.setVisibility(GONE);
- mImeShowing = false;
- }
- return;
- }
- final Context virtualDisplayContext = mContext.createDisplayContext(
- getVirtualDisplay().getDisplay());
-
- if (mVirtualDisplayWindowManager == null) {
- mVirtualDisplayWindowManager =
- (WindowManager) virtualDisplayContext.getSystemService(Context.WINDOW_SERVICE);
- }
- if (mVirtualImeView == null) {
- mVirtualImeView = new View(virtualDisplayContext);
- mVirtualImeView.setVisibility(VISIBLE);
- mVirtualDisplayWindowManager.addView(mVirtualImeView,
- getVirtualImeViewAttrs(w, h));
- } else {
- mVirtualDisplayWindowManager.updateViewLayout(mVirtualImeView,
- getVirtualImeViewAttrs(w, h));
- mVirtualImeView.setVisibility(VISIBLE);
- }
-
- mImeShowing = true;
- }
-
- private WindowManager.LayoutParams getVirtualImeViewAttrs(int w, int h) {
- // To use TYPE_NAVIGATION_BAR_PANEL instead of TYPE_IME_BAR to bypass the IME window type
- // token check when adding the window.
- final WindowManager.LayoutParams attrs =
- new WindowManager.LayoutParams(w, h, TYPE_NAVIGATION_BAR_PANEL,
- FLAG_LAYOUT_NO_LIMITS | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE,
- TRANSPARENT);
- attrs.gravity = Gravity.BOTTOM;
- attrs.setTitle(WINDOW_TITLE);
- attrs.token = new Binder();
- attrs.providesInsetsTypes = new int[]{ITYPE_IME};
- attrs.alpha = 0.0f;
- return attrs;
- }
-
void setStackView(BubbleStackView stackView) {
mStackView = stackView;
}
@@ -581,7 +451,7 @@
Bundle extras = new Bundle();
extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mBubbles));
target.putExtras(extras);
- mPendingIntent = PendingIntent.getActivity(mContext, /* requestCode */ 0,
+ mPendingIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */,
target, PendingIntent.FLAG_UPDATE_CURRENT);
mSettingsIcon.setVisibility(GONE);
}
@@ -619,7 +489,7 @@
mPendingIntent = mBubble.getBubbleIntent();
if (mPendingIntent != null || mBubble.hasMetadataShortcutId()) {
setContentVisibility(false);
- mActivityView.setVisibility(VISIBLE);
+ mTaskView.setVisibility(VISIBLE);
}
}
applyThemeAttrs();
@@ -629,59 +499,42 @@
}
}
+ /**
+ * Bubbles are backed by a pending intent or a shortcut, once the activity is
+ * started we never change it / restart it on notification updates -- unless the bubbles'
+ * backing data switches.
+ *
+ * This indicates if the new bubble is backed by a different data source than what was
+ * previously shown here (e.g. previously a pending intent & now a shortcut).
+ *
+ * @param newBubble the bubble this view is being updated with.
+ * @return true if the backing content has changed.
+ */
private boolean didBackingContentChange(Bubble newBubble) {
boolean prevWasIntentBased = mBubble != null && mPendingIntent != null;
boolean newIsIntentBased = newBubble.getBubbleIntent() != null;
return prevWasIntentBased != newIsIntentBased;
}
- /**
- * Lets activity view know it should be shown / populated with activity content.
- */
- void populateExpandedView() {
- if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "populateExpandedView: "
- + "bubble=" + getBubbleKey());
- }
-
- if (usingActivityView()) {
- mActivityView.setCallback(mStateCallback);
- } else {
- Log.e(TAG, "Cannot populate expanded view.");
- }
- }
-
- boolean performBackPressIfNeeded() {
- if (!usingActivityView()) {
- return false;
- }
- mActivityView.performBackPress();
- return true;
- }
-
void updateHeight() {
- if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
- }
-
if (mExpandedViewContainerLocation == null) {
return;
}
- if (usingActivityView()) {
+ if (mBubble != null || mIsOverflow) {
float desiredHeight = mOverflowHeight;
if (!mIsOverflow) {
desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
}
float height = Math.min(desiredHeight, getMaxExpandedHeight());
- height = Math.max(height, mMinHeight);
- ViewGroup.LayoutParams lp = mActivityView.getLayoutParams();
+ height = Math.max(height, mIsOverflow ? mOverflowHeight : mMinHeight);
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mTaskView.getLayoutParams();
mNeedsNewHeight = lp.height != height;
- if (!mKeyboardVisible) {
- // If the keyboard is visible... don't adjust the height because that will cause
- // a configuration change and the keyboard will be lost.
+ if (!mImeVisible) {
+ // If the ime is visible... don't adjust the height because that will cause
+ // a configuration change and the ime will be lost.
lp.height = (int) height;
- mActivityView.setLayoutParams(lp);
+ mTaskView.setLayoutParams(lp);
mNeedsNewHeight = false;
}
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
@@ -694,12 +547,15 @@
private int getMaxExpandedHeight() {
mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
+ int expandedContainerY = mExpandedViewContainerLocation != null
+ ? mExpandedViewContainerLocation[1]
+ : 0;
int bottomInset = getRootWindowInsets() != null
? getRootWindowInsets().getStableInsetBottom()
: 0;
return mDisplaySize.y
- - mExpandedViewContainerLocation[1]
+ - expandedContainerY
- getPaddingTop()
- getPaddingBottom()
- mSettingsIconHeight
@@ -719,14 +575,12 @@
Log.d(TAG, "updateView: bubble="
+ getBubbleKey());
}
-
mExpandedViewContainerLocation = containerLocationOnScreen;
-
- if (usingActivityView()
- && mActivityView.getVisibility() == VISIBLE
- && mActivityView.isAttachedToWindow()) {
- mActivityView.onLocationChanged();
+ if (mTaskView != null
+ && mTaskView.getVisibility() == VISIBLE
+ && mTaskView.isAttachedToWindow()) {
updateHeight();
+ mTaskView.onLocationChanged();
}
}
@@ -749,65 +603,19 @@
}
/**
- * Removes and releases an ActivityView if one was previously created for this bubble.
+ * Cleans up anything related to the task and TaskView.
*/
public void cleanUpExpandedState() {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "cleanUpExpandedState: mActivityViewStatus=" + mActivityViewStatus
- + ", bubble=" + getBubbleKey());
+ Log.d(TAG, "cleanUpExpandedState: bubble=" + getBubbleKey() + " task=" + mTaskId);
}
- if (mActivityView == null) {
- return;
+ if (mTaskView != null) {
+ mTaskView.release();
}
- mActivityView.release();
- if (mTaskId != -1) {
- try {
- ActivityTaskManager.getService().removeTask(mTaskId);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to remove taskId " + mTaskId);
- }
- mTaskId = -1;
+ if (mTaskView != null) {
+ removeView(mTaskView);
+ mTaskView = null;
}
- removeView(mActivityView);
-
- mActivityView = null;
- }
-
- /**
- * Called when the last task is removed from a {@link android.hardware.display.VirtualDisplay}
- * which {@link ActivityView} uses.
- */
- void notifyDisplayEmpty() {
- if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "notifyDisplayEmpty: bubble="
- + getBubbleKey()
- + " mActivityViewStatus=" + mActivityViewStatus);
- }
- if (mActivityViewStatus == ActivityViewStatus.ACTIVITY_STARTED) {
- mActivityViewStatus = ActivityViewStatus.INITIALIZED;
- }
- }
-
- private boolean usingActivityView() {
- return (mPendingIntent != null || mBubble.hasMetadataShortcutId())
- && mActivityView != null;
- }
-
- /**
- * @return the display id of the virtual display.
- */
- public int getVirtualDisplayId() {
- if (usingActivityView()) {
- return mActivityView.getVirtualDisplayId();
- }
- return INVALID_DISPLAY;
- }
-
- private VirtualDisplay getVirtualDisplay() {
- if (usingActivityView()) {
- return mActivityView.getVirtualDisplay();
- }
- return null;
}
/**
@@ -817,7 +625,6 @@
@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.print("BubbleExpandedView");
pw.print(" taskId: "); pw.println(mTaskId);
- pw.print(" activityViewStatus: "); pw.println(mActivityViewStatus);
pw.print(" stackView: "); pw.println(mStackView);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
index 69f7828..009114f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
@@ -18,6 +18,8 @@
import static android.graphics.Paint.ANTI_ALIAS_FLAG;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+import static com.android.systemui.Interpolators.ALPHA_IN;
+import static com.android.systemui.Interpolators.ALPHA_OUT;
import android.animation.ArgbEvaluator;
import android.content.Context;
@@ -56,6 +58,11 @@
/** Max width of the flyout, in terms of percent of the screen width. */
private static final float FLYOUT_MAX_WIDTH_PERCENT = .6f;
+ /** Translation Y of fade animation. */
+ private static final float FLYOUT_FADE_Y = 40f;
+
+ private static final long FLYOUT_FADE_DURATION = 200L;
+
private final int mFlyoutPadding;
private final int mFlyoutSpaceFromBubble;
private final int mPointerSize;
@@ -104,6 +111,9 @@
/** The bounds of the flyout background, kept up to date as it transitions to the 'new' dot. */
private final RectF mBgRect = new RectF();
+ /** The y position of the flyout, relative to the top of the screen. */
+ private float mFlyoutY = 0f;
+
/**
* Percent progress in the transition from flyout to 'new' dot. These two values are the inverse
* of each other (if we're 40% transitioned to the dot, we're 60% flyout), but it makes the code
@@ -221,18 +231,33 @@
mSenderText.setTextSize(TypedValue.COMPLEX_UNIT_PX, newFontSize);
}
- /** Configures the flyout, collapsed into to dot form. */
- void setupFlyoutStartingAsDot(
- Bubble.FlyoutMessage flyoutMessage,
- PointF stackPos,
- float parentWidth,
- boolean arrowPointingLeft,
- int dotColor,
- @Nullable Runnable onLayoutComplete,
- @Nullable Runnable onHide,
- float[] dotCenter,
- boolean hideDot) {
+ /*
+ * Fade animation for consecutive flyouts.
+ */
+ void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, float stackY) {
+ fade(false /* in */);
+ updateFlyoutMessage(flyoutMessage, parentWidth);
+ // Wait for TextViews to layout with updated height.
+ post(() -> {
+ mFlyoutY = stackY + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f;
+ fade(true /* in */);
+ });
+ }
+ private void fade(boolean in) {
+ setAlpha(in ? 0f : 1f);
+ setTranslationY(in ? mFlyoutY : mFlyoutY + FLYOUT_FADE_Y);
+ animate()
+ .alpha(in ? 1f : 0f)
+ .setDuration(FLYOUT_FADE_DURATION)
+ .setInterpolator(in ? ALPHA_IN : ALPHA_OUT);
+ animate()
+ .translationY(in ? mFlyoutY : mFlyoutY - FLYOUT_FADE_Y)
+ .setDuration(FLYOUT_FADE_DURATION)
+ .setInterpolator(in ? ALPHA_IN : ALPHA_OUT);
+ }
+
+ private void updateFlyoutMessage(Bubble.FlyoutMessage flyoutMessage, float parentWidth) {
final Drawable senderAvatar = flyoutMessage.senderAvatar;
if (senderAvatar != null && flyoutMessage.isGroupChat) {
mSenderAvatar.setVisibility(VISIBLE);
@@ -256,6 +281,27 @@
mSenderText.setVisibility(GONE);
}
+ // Set the flyout TextView's max width in terms of percent, and then subtract out the
+ // padding so that the entire flyout view will be the desired width (rather than the
+ // TextView being the desired width + extra padding).
+ mMessageText.setMaxWidth(maxTextViewWidth);
+ mMessageText.setText(flyoutMessage.message);
+ }
+
+ /** Configures the flyout, collapsed into dot form. */
+ void setupFlyoutStartingAsDot(
+ Bubble.FlyoutMessage flyoutMessage,
+ PointF stackPos,
+ float parentWidth,
+ boolean arrowPointingLeft,
+ int dotColor,
+ @Nullable Runnable onLayoutComplete,
+ @Nullable Runnable onHide,
+ float[] dotCenter,
+ boolean hideDot) {
+
+ updateFlyoutMessage(flyoutMessage, parentWidth);
+
mArrowPointingLeft = arrowPointingLeft;
mDotColor = dotColor;
mOnHide = onHide;
@@ -263,24 +309,12 @@
setCollapsePercent(1f);
- // Set the flyout TextView's max width in terms of percent, and then subtract out the
- // padding so that the entire flyout view will be the desired width (rather than the
- // TextView being the desired width + extra padding).
- mMessageText.setMaxWidth(maxTextViewWidth);
- mMessageText.setText(flyoutMessage.message);
-
- // Wait for the TextView to lay out so we know its line count.
+ // Wait for TextViews to layout with updated height.
post(() -> {
- float restingTranslationY;
- // Multi line flyouts get top-aligned to the bubble.
- if (mMessageText.getLineCount() > 1) {
- restingTranslationY = stackPos.y + mBubbleIconTopPadding;
- } else {
- // Single line flyouts are vertically centered with respect to the bubble.
- restingTranslationY =
- stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f;
- }
- setTranslationY(restingTranslationY);
+ // Flyout is vertically centered with respect to the bubble.
+ mFlyoutY =
+ stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f;
+ setTranslationY(mFlyoutY);
// Calculate the translation required to position the flyout next to the bubble stack,
// with the desired padding.
@@ -300,7 +334,7 @@
final float dotPositionY = stackPos.y + mDotCenter[1] - adjustmentForScaleAway;
final float distanceFromFlyoutLeftToDotCenterX = mRestingTranslationX - dotPositionX;
- final float distanceFromLayoutTopToDotCenterY = restingTranslationY - dotPositionY;
+ final float distanceFromLayoutTopToDotCenterY = mFlyoutY - dotPositionY;
mTranslationXWhenDot = -distanceFromFlyoutLeftToDotCenterX;
mTranslationYWhenDot = -distanceFromLayoutTopToDotCenterY;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
index 86ba8c5..48c809d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
@@ -19,17 +19,22 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.internal.util.FrameworkStatsLog;
/**
- * Interface for handling bubble-specific logging.
+ * Implementation of UiEventLogger for logging bubble UI events.
+ *
+ * See UiEventReported atom in atoms.proto for more context.
*/
-public interface BubbleLogger extends UiEventLogger {
+public class BubbleLogger {
+
+ private final UiEventLogger mUiEventLogger;
/**
* Bubble UI event.
*/
@VisibleForTesting
- enum Event implements UiEventLogger.UiEventEnum {
+ public enum Event implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "User dismissed the bubble via gesture, add bubble to overflow.")
BUBBLE_OVERFLOW_ADD_USER_GESTURE(483),
@@ -70,23 +75,80 @@
}
}
+ public BubbleLogger(UiEventLogger uiEventLogger) {
+ mUiEventLogger = uiEventLogger;
+ }
+
/**
* @param b Bubble involved in this UI event
* @param e UI event
*/
- void log(Bubble b, UiEventEnum e);
+ public void log(Bubble b, UiEventLogger.UiEventEnum e) {
+ mUiEventLogger.logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
+ }
/**
- *
* @param b Bubble removed from overflow
- * @param r Reason that bubble was removed from overflow
+ * @param r Reason that bubble was removed
*/
- void logOverflowRemove(Bubble b, @BubbleController.DismissReason int r);
+ public void logOverflowRemove(Bubble b, @BubbleController.DismissReason int r) {
+ if (r == BubbleController.DISMISS_NOTIF_CANCEL) {
+ log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_CANCEL);
+ } else if (r == BubbleController.DISMISS_GROUP_CANCELLED) {
+ log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_GROUP_CANCEL);
+ } else if (r == BubbleController.DISMISS_NO_LONGER_BUBBLE) {
+ log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_NO_LONGER_BUBBLE);
+ } else if (r == BubbleController.DISMISS_BLOCKED) {
+ log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BLOCKED);
+ }
+ }
/**
- *
* @param b Bubble added to overflow
* @param r Reason that bubble was added to overflow
*/
- void logOverflowAdd(Bubble b, @BubbleController.DismissReason int r);
-}
+ public void logOverflowAdd(Bubble b, @BubbleController.DismissReason int r) {
+ if (r == BubbleController.DISMISS_AGED) {
+ log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
+ } else if (r == BubbleController.DISMISS_USER_GESTURE) {
+ log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
+ }
+ }
+
+ void logStackUiChanged(String packageName, int action, int bubbleCount, float normalX,
+ float normalY) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_UI_CHANGED,
+ packageName,
+ null /* notification channel */,
+ 0 /* notification ID */,
+ 0 /* bubble position */,
+ bubbleCount,
+ action,
+ normalX,
+ normalY,
+ false /* unread bubble */,
+ false /* on-going bubble */,
+ false /* isAppForeground (unused) */);
+ }
+
+ void logShowOverflow(String packageName, int currentUserId) {
+ mUiEventLogger.log(BubbleLogger.Event.BUBBLE_OVERFLOW_SELECTED, currentUserId,
+ packageName);
+ }
+
+ void logBubbleUiChanged(Bubble bubble, String packageName, int action, int bubbleCount,
+ float normalX, float normalY, int index) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_UI_CHANGED,
+ packageName,
+ bubble.getChannelId() /* notification channel */,
+ bubble.getNotificationId() /* notification ID */,
+ index,
+ bubbleCount,
+ action,
+ normalX,
+ normalY,
+ bubble.showInShade() /* isUnread */,
+ false /* isOngoing (unused) */,
+ false /* isAppForeground (unused) */);
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
deleted file mode 100644
index ea612af..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
+++ /dev/null
@@ -1,103 +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 com.android.systemui.bubbles;
-
-import android.os.UserHandle;
-
-import com.android.internal.logging.UiEventLoggerImpl;
-import com.android.systemui.shared.system.SysUiStatsLog;
-
-/**
- * Implementation of UiEventLogger for logging bubble UI events.
- *
- * See UiEventReported atom in atoms.proto for more context.
- */
-public class BubbleLoggerImpl extends UiEventLoggerImpl implements BubbleLogger {
-
- /**
- * @param b Bubble involved in this UI event
- * @param e UI event
- */
- public void log(Bubble b, UiEventEnum e) {
- logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
- }
-
- /**
- * @param b Bubble removed from overflow
- * @param r Reason that bubble was removed
- */
- public void logOverflowRemove(Bubble b, @BubbleController.DismissReason int r) {
- if (r == BubbleController.DISMISS_NOTIF_CANCEL) {
- log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_CANCEL);
- } else if (r == BubbleController.DISMISS_GROUP_CANCELLED) {
- log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_GROUP_CANCEL);
- } else if (r == BubbleController.DISMISS_NO_LONGER_BUBBLE) {
- log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_NO_LONGER_BUBBLE);
- } else if (r == BubbleController.DISMISS_BLOCKED) {
- log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BLOCKED);
- }
- }
-
- /**
- * @param b Bubble added to overflow
- * @param r Reason that bubble was added to overflow
- */
- public void logOverflowAdd(Bubble b, @BubbleController.DismissReason int r) {
- if (r == BubbleController.DISMISS_AGED) {
- log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
- } else if (r == BubbleController.DISMISS_USER_GESTURE) {
- log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
- }
- }
-
- void logStackUiChanged(String packageName, int action, int bubbleCount, float normalX,
- float normalY) {
- SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
- packageName,
- null /* notification channel */,
- 0 /* notification ID */,
- 0 /* bubble position */,
- bubbleCount,
- action,
- normalX,
- normalY,
- false /* unread bubble */,
- false /* on-going bubble */,
- false /* isAppForeground (unused) */);
- }
-
- void logShowOverflow(String packageName, int currentUserId) {
- super.log(BubbleLogger.Event.BUBBLE_OVERFLOW_SELECTED, currentUserId,
- packageName);
- }
-
- void logBubbleUiChanged(Bubble bubble, String packageName, int action, int bubbleCount,
- float normalX, float normalY, int index) {
- SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
- packageName,
- bubble.getChannelId() /* notification channel */,
- bubble.getNotificationId() /* notification ID */,
- index,
- bubbleCount,
- action,
- normalX,
- normalY,
- bubble.showInShade() /* isUnread */,
- false /* isOngoing (unused) */,
- false /* isAppForeground (unused) */);
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
index bf7c860..102055d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
@@ -16,6 +16,7 @@
package com.android.systemui.bubbles
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.content.Context
import android.content.res.Configuration
import android.graphics.Bitmap
@@ -37,8 +38,8 @@
private val stack: BubbleStackView
) : BubbleViewProvider {
- private lateinit var bitmap : Bitmap
- private lateinit var dotPath : Path
+ private lateinit var bitmap: Bitmap
+ private lateinit var dotPath: Path
private var bitmapSize = 0
private var iconBitmapSize = 0
@@ -167,8 +168,8 @@
return KEY
}
- override fun getDisplayId(): Int {
- return expandedView.virtualDisplayId
+ override fun getTaskId(): Int {
+ return if (expandedView != null) expandedView.getTaskId() else INVALID_TASK_ID
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index e83954b..431719f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -27,7 +27,6 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
-import android.app.ActivityView;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -74,18 +73,13 @@
import androidx.dynamicanimation.animation.SpringForce;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.systemui.Interpolators;
-import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.bubbles.animation.AnimatableScaleMatrix;
import com.android.systemui.bubbles.animation.ExpandedAnimationController;
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
import com.android.systemui.bubbles.animation.StackAnimationController;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.util.RelativeTouchListener;
import com.android.wm.shell.animation.PhysicsAnimator;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
@@ -121,6 +115,8 @@
/** Duration of the flyout alpha animations. */
private static final int FLYOUT_ALPHA_ANIMATION_DURATION = 100;
+ private static final int FADE_IN_DURATION = 320;
+
/** Percent to darken the bubbles when they're in the dismiss target. */
private static final float DARKEN_PERCENT = 0.3f;
@@ -302,7 +298,7 @@
pw.println(" expandedViewAlpha: " + expandedView.getAlpha());
pw.println(" expandedViewTaskId: " + expandedView.getTaskId());
- final ActivityView av = expandedView.getActivityView();
+ final View av = expandedView.getTaskView();
if (av != null) {
pw.println(" activityViewVis: " + av.getVisibility());
@@ -323,8 +319,6 @@
/** Callback to run when we want to unbubble the given notification's conversation. */
private Consumer<String> mUnbubbleConversationCallback;
- private SysUiState mSysUiState;
-
private boolean mViewUpdatedRequested = false;
private boolean mIsExpansionAnimating = false;
private boolean mIsBubbleSwitchAnimating = false;
@@ -332,8 +326,6 @@
/** The view to desaturate/darken when magneted to the dismiss target. */
@Nullable private View mDesaturateAndDarkenTargetView;
- private LayoutInflater mInflater;
-
private Rect mTempRect = new Rect();
private final List<Rect> mSystemGestureExclusionRects = Collections.singletonList(new Rect());
@@ -401,6 +393,11 @@
public final Consumer<Boolean> mOnImeVisibilityChanged;
/**
+ * Callback to run when the bubble expand status changes.
+ */
+ private final Consumer<Boolean> mOnBubbleExpandChanged;
+
+ /**
* Callback to run to ask BubbleController to hide the current IME.
*/
private final Runnable mHideCurrentInputMethodCallback;
@@ -660,7 +657,7 @@
viewInitialX + dx, velX, velY) <= 0;
updateBubbleIcons();
logBubbleEvent(null /* no bubble associated with bubble stack move */,
- SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
+ FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
}
mDismissView.hide();
}
@@ -742,16 +739,13 @@
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator,
- SysUiState sysUiState,
Runnable allBubblesAnimatedOutAction,
Consumer<Boolean> onImeVisibilityChanged,
- Runnable hideCurrentInputMethodCallback) {
+ Runnable hideCurrentInputMethodCallback,
+ Consumer<Boolean> onBubbleExpandChanged) {
super(context);
mBubbleData = data;
- mInflater = LayoutInflater.from(context);
-
- mSysUiState = sysUiState;
Resources res = getResources();
mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
@@ -865,21 +859,13 @@
mOnImeVisibilityChanged = onImeVisibilityChanged;
mHideCurrentInputMethodCallback = hideCurrentInputMethodCallback;
+ mOnBubbleExpandChanged = onBubbleExpandChanged;
setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
onImeVisibilityChanged.accept(insets.getInsets(WindowInsets.Type.ime()).bottom > 0);
-
if (!mIsExpanded || mIsExpansionAnimating) {
return view.onApplyWindowInsets(insets);
}
- mExpandedAnimationController.updateYPosition(
- // Update the insets after we're done translating otherwise position
- // calculation for them won't be correct.
- () -> {
- if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().updateInsets(insets);
- }
- });
return view.onApplyWindowInsets(insets);
});
@@ -974,7 +960,7 @@
animate()
.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED)
- .setDuration(CollapsedStatusBarFragment.FADE_IN_DURATION);
+ .setDuration(FADE_IN_DURATION);
}
/**
@@ -1066,7 +1052,7 @@
mBubbleData.setExpanded(false);
mContext.startActivityAsUser(intent, bubble.getUser());
logBubbleEvent(bubble,
- SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
+ FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
}
});
@@ -1082,8 +1068,7 @@
* Whether the educational view should show for the expanded view "manage" menu.
*/
private boolean shouldShowManageEdu() {
- final boolean seen = Prefs.getBoolean(mContext,
- Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false /* default */);
+ final boolean seen = getPrefBoolean(ManageEducationViewKt.PREF_MANAGED_EDUCATION);
final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext))
&& mExpandedBubble != null;
if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
@@ -1107,8 +1092,7 @@
* Whether education view should show for the collapsed stack.
*/
private boolean shouldShowStackEdu() {
- final boolean seen = Prefs.getBoolean(getContext(),
- Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION, false /* default */);
+ final boolean seen = getPrefBoolean(StackEducationViewKt.PREF_STACK_EDUCATION);
final boolean shouldShow = !seen || BubbleDebugConfig.forceShowUserEducation(mContext);
if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
Log.d(TAG, "Show stack edu: " + shouldShow);
@@ -1116,6 +1100,11 @@
return shouldShow;
}
+ private boolean getPrefBoolean(String key) {
+ return mContext.getSharedPreferences(mContext.getPackageName(), Context.MODE_PRIVATE)
+ .getBoolean(key, false /* default */);
+ }
+
/**
* @return true if education view for collapsed stack should show and was not showing before.
*/
@@ -1254,7 +1243,15 @@
mTempRect.setEmpty();
getTouchableRegion(mTempRect);
- inoutInfo.touchableRegion.set(mTempRect);
+ if (mIsExpanded && mExpandedBubble != null
+ && mExpandedBubble.getExpandedView() != null
+ && mExpandedBubble.getExpandedView().getTaskView() != null) {
+ inoutInfo.touchableRegion.set(mTempRect);
+ mExpandedBubble.getExpandedView().getTaskView().getBoundsOnScreen(mTempRect);
+ inoutInfo.touchableRegion.op(mTempRect, Region.Op.DIFFERENCE);
+ } else {
+ inoutInfo.touchableRegion.set(mTempRect);
+ }
}
@Override
@@ -1442,13 +1439,6 @@
}
/**
- * The {@link BadgedImageView} that is expanded, null if one does not exist.
- */
- View getExpandedBubbleView() {
- return mExpandedBubble != null ? mExpandedBubble.getIconView() : null;
- }
-
- /**
* The {@link Bubble} that is expanded, null if one does not exist.
*/
@Nullable
@@ -1488,7 +1478,7 @@
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
animateInFlyoutForBubble(bubble);
requestUpdate();
- logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
+ logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
}
// via BubbleData.Listener
@@ -1508,7 +1498,7 @@
bubble.cleanupViews();
}
updatePointerPosition();
- logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+ logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
return;
}
}
@@ -1526,7 +1516,7 @@
void updateBubble(Bubble bubble) {
animateInFlyoutForBubble(bubble);
requestUpdate();
- logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
+ logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
}
public void updateBubbleOrder(List<Bubble> bubbles) {
@@ -1563,7 +1553,7 @@
return;
}
- if (bubbleToSelect.getKey() == BubbleOverflow.KEY) {
+ if (bubbleToSelect.getKey().equals(BubbleOverflow.KEY)) {
mBubbleData.setShowingOverflow(true);
} else {
mBubbleData.setShowingOverflow(false);
@@ -1622,8 +1612,9 @@
requestUpdate();
logBubbleEvent(previouslySelected,
- SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
- logBubbleEvent(bubbleToSelect, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
+ FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+ logBubbleEvent(bubbleToSelect,
+ FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
notifyExpansionChanged(previouslySelected, false /* expanded */);
notifyExpansionChanged(bubbleToSelect, true /* expanded */);
});
@@ -1653,30 +1644,21 @@
hideCurrentInputMethod();
- mSysUiState
- .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
- .commitUpdate(mContext.getDisplayId());
+ mOnBubbleExpandChanged.accept(shouldExpand);
if (mIsExpanded) {
animateCollapse();
- logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+ logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
} else {
animateExpansion();
// TODO: move next line to BubbleData
- logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
- logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
+ logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
+ logBubbleEvent(mExpandedBubble,
+ FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
}
notifyExpansionChanged(mExpandedBubble, mIsExpanded);
}
- void showExpandedViewContents(int displayId) {
- if (mExpandedBubble != null
- && mExpandedBubble.getExpandedView() != null
- && mExpandedBubble.getExpandedView().getVirtualDisplayId() == displayId) {
- mExpandedBubble.setContentVisibility(true);
- }
- }
-
/**
* Asks the BubbleController to hide the IME from anywhere, whether it's focused on Bubbles or
* not.
@@ -1711,7 +1693,6 @@
updateOverflowVisibility();
updatePointerPosition();
mExpandedAnimationController.expandFromStack(() -> {
- afterExpandedViewAnimation();
if (mIsExpanded && mExpandedBubble.getExpandedView() != null) {
maybeShowManageEdu();
}
@@ -1774,11 +1755,10 @@
mExpandedViewContainerMatrix);
})
.withEndActions(() -> {
+ afterExpandedViewAnimation();
if (mExpandedBubble != null
&& mExpandedBubble.getExpandedView() != null) {
mExpandedBubble.getExpandedView()
- .setContentVisibility(true);
- mExpandedBubble.getExpandedView()
.setSurfaceZOrderedOnTop(false);
}
})
@@ -1922,7 +1902,6 @@
})
.withEndActions(() -> {
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().setContentVisibility(true);
mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
}
@@ -1958,13 +1937,6 @@
}
}
- /** Return the BubbleView at the given index from the bubble container. */
- public BadgedImageView getBubbleAt(int i) {
- return getBubbleCount() > i
- ? (BadgedImageView) mBubbleContainer.getChildAt(i)
- : null;
- }
-
/** Moves the bubbles out of the way if they're going to be over the keyboard. */
public void onImeVisibilityChanged(boolean visible, int height) {
mStackAnimationController.setImeHeight(visible ? height + mImeOffset : 0);
@@ -1986,6 +1958,9 @@
FLYOUT_IME_ANIMATION_SPRING_CONFIG)
.start();
}
+ } else if (mIsExpanded && mExpandedBubble != null
+ && mExpandedBubble.getExpandedView() != null) {
+ mExpandedBubble.getExpandedView().setImeVisible(visible);
}
}
@@ -2196,11 +2171,7 @@
return getStatusBarHeight() + mBubbleSize + mBubblePaddingTop;
}
- /**
- * Animates in the flyout for the given bubble, if available, and then hides it after some time.
- */
- @VisibleForTesting
- void animateInFlyoutForBubble(Bubble bubble) {
+ private boolean shouldShowFlyout(Bubble bubble) {
Bubble.FlyoutMessage flyoutMessage = bubble.getFlyoutMessage();
final BadgedImageView bubbleView = bubble.getIconView();
if (flyoutMessage == null
@@ -2212,11 +2183,22 @@
|| mIsGestureInProgress
|| mBubbleToExpandAfterFlyoutCollapse != null
|| bubbleView == null) {
- if (bubbleView != null) {
+ if (bubbleView != null && mFlyout.getVisibility() != VISIBLE) {
bubbleView.removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
}
// Skip the message if none exists, we're expanded or animating expansion, or we're
// about to expand a bubble from the previous tapped flyout, or if bubble view is null.
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Animates in the flyout for the given bubble, if available, and then hides it after some time.
+ */
+ @VisibleForTesting
+ void animateInFlyoutForBubble(Bubble bubble) {
+ if (!shouldShowFlyout(bubble)) {
return;
}
@@ -2234,25 +2216,22 @@
}
// Stop suppressing the dot now that the flyout has morphed into the dot.
- bubbleView.removeDotSuppressionFlag(
+ bubble.getIconView().removeDotSuppressionFlag(
BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
- mFlyout.setVisibility(INVISIBLE);
-
// Hide the stack after a delay, if needed.
updateTemporarilyInvisibleAnimation(false /* hideImmediately */);
};
- mFlyout.setVisibility(INVISIBLE);
// Suppress the dot when we are animating the flyout.
- bubbleView.addDotSuppressionFlag(
+ bubble.getIconView().addDotSuppressionFlag(
BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
// Start flyout expansion. Post in case layout isn't complete and getWidth returns 0.
post(() -> {
// An auto-expanding bubble could have been posted during the time it takes to
// layout.
- if (isExpanded()) {
+ if (isExpanded() || bubble.getIconView() == null) {
return;
}
final Runnable expandFlyoutAfterDelay = () -> {
@@ -2269,23 +2248,26 @@
mFlyout.postDelayed(mAnimateInFlyout, 200);
};
- if (bubble.getIconView() == null) {
- return;
- }
- mFlyout.setupFlyoutStartingAsDot(flyoutMessage,
- mStackAnimationController.getStackPosition(), getWidth(),
- mStackAnimationController.isStackOnLeftSide(),
- bubble.getIconView().getDotColor() /* dotColor */,
- expandFlyoutAfterDelay /* onLayoutComplete */,
- mAfterFlyoutHidden,
- bubble.getIconView().getDotCenter(),
- !bubble.showDot());
+ if (mFlyout.getVisibility() == View.VISIBLE) {
+ mFlyout.animateUpdate(bubble.getFlyoutMessage(), getWidth(),
+ mStackAnimationController.getStackPosition().y);
+ } else {
+ mFlyout.setVisibility(INVISIBLE);
+ mFlyout.setupFlyoutStartingAsDot(bubble.getFlyoutMessage(),
+ mStackAnimationController.getStackPosition(), getWidth(),
+ mStackAnimationController.isStackOnLeftSide(),
+ bubble.getIconView().getDotColor() /* dotColor */,
+ expandFlyoutAfterDelay /* onLayoutComplete */,
+ mAfterFlyoutHidden,
+ bubble.getIconView().getDotCenter(),
+ !bubble.showDot());
+ }
mFlyout.bringToFront();
});
mFlyout.removeCallbacks(mHideFlyout);
mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
- logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
+ logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
}
/** Hide the flyout immediately and cancel any pending hide runnables. */
@@ -2393,7 +2375,6 @@
final float targetY = mTempRect.bottom - mManageMenu.getHeight();
final float xOffsetForAnimation = (isLtr ? 1 : -1) * mManageMenu.getWidth() / 4f;
-
if (show) {
mManageMenu.setScaleX(0.5f);
mManageMenu.setScaleY(0.5f);
@@ -2410,6 +2391,8 @@
.withEndActions(() -> {
View child = mManageMenu.getChildAt(0);
child.requestAccessibilityFocus();
+ // Update the AV's obscured touchable region for the new visibility state.
+ mExpandedBubble.getExpandedView().updateObscuredTouchableRegion();
})
.start();
@@ -2421,12 +2404,15 @@
.spring(DynamicAnimation.SCALE_Y, 0.5f)
.spring(DynamicAnimation.TRANSLATION_X, targetX - xOffsetForAnimation)
.spring(DynamicAnimation.TRANSLATION_Y, targetY + mManageMenu.getHeight() / 4f)
- .withEndActions(() -> mManageMenu.setVisibility(View.INVISIBLE))
+ .withEndActions(() -> {
+ mManageMenu.setVisibility(View.INVISIBLE);
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ // Update the AV's obscured touchable region for the new state.
+ mExpandedBubble.getExpandedView().updateObscuredTouchableRegion();
+ }
+ })
.start();
}
-
- // Update the AV's obscured touchable region for the new menu visibility state.
- mExpandedBubble.getExpandedView().updateObscuredTouchableRegion();
}
private void updateExpandedBubble() {
@@ -2446,7 +2432,6 @@
mExpandedViewContainer.setAlpha(0f);
mExpandedViewContainer.addView(bev);
bev.setManageClickListener((view) -> showManageMenu(!mShowingManage));
- bev.populateExpandedView();
if (!mIsExpansionAnimating) {
mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
@@ -2503,7 +2488,7 @@
mAnimatingOutSurfaceContainer.setTranslationY(0);
final int[] activityViewLocation =
- mExpandedBubble.getExpandedView().getActivityViewLocationOnScreen();
+ mExpandedBubble.getExpandedView().getTaskViewLocationOnScreen();
final int[] surfaceViewLocation = mAnimatingOutSurfaceView.getLocationOnScreen();
// Translate the surface to overlap the real ActivityView.
@@ -2679,17 +2664,6 @@
getNormalizedYPosition());
}
- /**
- * Called when a back gesture should be directed to the Bubbles stack. When expanded,
- * a back key down/up event pair is forwarded to the bubble Activity.
- */
- boolean performBackPressIfNeeded() {
- if (!isExpanded() || mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
- return false;
- }
- return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
- }
-
/** For debugging only */
List<Bubble> getBubblesOnScreen() {
List<Bubble> bubbles = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTaskView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTaskView.java
deleted file mode 100644
index 06205c5..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTaskView.java
+++ /dev/null
@@ -1,181 +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 com.android.systemui.bubbles;
-
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.window.TaskEmbedder;
-import android.window.TaskOrganizerTaskEmbedder;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.view.IWindow;
-import android.view.SurfaceControl;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-
-import dalvik.system.CloseGuard;
-
-
-public class BubbleTaskView extends SurfaceView implements SurfaceHolder.Callback,
- TaskEmbedder.Host {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleTaskView" : TAG_BUBBLES;
-
- private final CloseGuard mGuard = CloseGuard.get();
- private boolean mOpened; // Protected by mGuard.
-
- private TaskEmbedder mTaskEmbedder;
- private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
- private final Rect mTmpRect = new Rect();
-
- public BubbleTaskView(Context context) {
- super(context);
-
- mTaskEmbedder = new TaskOrganizerTaskEmbedder(context, this);
- setUseAlpha();
- getHolder().addCallback(this);
-
- mOpened = true;
- mGuard.open("release");
- }
-
- public void setCallback(TaskEmbedder.Listener callback) {
- if (callback == null) {
- mTaskEmbedder.setListener(null);
- return;
- }
- mTaskEmbedder.setListener(callback);
- }
-
- public void startShortcutActivity(@NonNull ShortcutInfo shortcut,
- @NonNull ActivityOptions options, @Nullable Rect sourceBounds) {
- mTaskEmbedder.startShortcutActivity(shortcut, options, sourceBounds);
- }
-
- public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
- @NonNull ActivityOptions options) {
- mTaskEmbedder.startActivity(pendingIntent, fillInIntent, options);
- }
-
- public void onLocationChanged() {
- mTaskEmbedder.notifyBoundsChanged();
- }
-
- @Override
- public Rect getScreenBounds() {
- getBoundsOnScreen(mTmpRect);
- return mTmpRect;
- }
-
- @Override
- public void onTaskBackgroundColorChanged(TaskEmbedder ts, int bgColor) {
- setResizeBackgroundColor(bgColor);
- }
-
- @Override
- public Region getTapExcludeRegion() {
- // Not used
- return null;
- }
-
- @Override
- public Matrix getScreenToTaskMatrix() {
- // Not used
- return null;
- }
-
- @Override
- public IWindow getWindow() {
- // Not used
- return null;
- }
-
- @Override
- public Point getPositionInWindow() {
- // Not used
- return null;
- }
-
- @Override
- public boolean canReceivePointerEvents() {
- // Not used
- return false;
- }
-
- public void release() {
- if (!mTaskEmbedder.isInitialized()) {
- throw new IllegalStateException(
- "Trying to release container that is not initialized.");
- }
- performRelease();
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mGuard != null) {
- mGuard.warnIfOpen();
- performRelease();
- }
- } finally {
- super.finalize();
- }
- }
-
- private void performRelease() {
- if (!mOpened) {
- return;
- }
- getHolder().removeCallback(this);
- mTaskEmbedder.release();
- mTaskEmbedder.setListener(null);
-
- mGuard.close();
- mOpened = false;
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- if (!mTaskEmbedder.isInitialized()) {
- mTaskEmbedder.initialize(getSurfaceControl());
- } else {
- mTmpTransaction.reparent(mTaskEmbedder.getSurfaceControl(),
- getSurfaceControl()).apply();
- }
- mTaskEmbedder.start();
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- mTaskEmbedder.resizeTask(width, height);
- mTaskEmbedder.notifyBoundsChanged();
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- mTaskEmbedder.stop();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
index 916ad18..5cc24ce 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
@@ -48,5 +48,5 @@
boolean showDot();
- int getDisplayId();
+ int getTaskId();
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
index 34828b3..39c750d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
@@ -17,8 +17,6 @@
package com.android.systemui.bubbles;
import android.annotation.NonNull;
-import android.content.Context;
-import android.view.Display;
import androidx.annotation.MainThread;
@@ -57,12 +55,6 @@
*/
ScrimView getScrimForBubble();
- /**
- * @return the display id of the expanded view, if the stack is expanded and not occluded by the
- * status bar, otherwise returns {@link Display#INVALID_DISPLAY}.
- */
- int getExpandedDisplayId(Context context);
-
/** @return Bubbles for updating overflow. */
List<Bubble> getOverflowBubbles();
@@ -77,13 +69,6 @@
*/
void expandStackAndSelectBubble(NotificationEntry entry);
-
- /**
- * Directs a back gesture at the bubble stack. When opened, the current expanded bubble
- * is forwarded a back key down/up pair.
- */
- void performBackPressIfNeeded();
-
/** Promote the provided bubbles when overflow view. */
void promoteBubbleFromOverflow(Bubble bubble);
@@ -142,4 +127,7 @@
/** Set a listener to be notified of when overflow view update. */
void setOverflowListener(BubbleData.Listener listener);
+
+ /** The task listener for events in bubble tasks. **/
+ MultiWindowTaskListener getTaskManager();
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
index 26a9773..3db07c2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
@@ -25,8 +25,6 @@
import android.widget.TextView
import com.android.internal.util.ContrastColorUtil
import com.android.systemui.Interpolators
-import com.android.systemui.Prefs
-import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION
import com.android.systemui.R
/**
@@ -38,8 +36,8 @@
private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleManageEducationView"
else BubbleDebugConfig.TAG_BUBBLES
- private val ANIMATE_DURATION : Long = 200
- private val ANIMATE_DURATION_SHORT : Long = 40
+ private val ANIMATE_DURATION: Long = 200
+ private val ANIMATE_DURATION_SHORT: Long = 40
private val manageView by lazy { findViewById<View>(R.id.manage_education_view) }
private val manageButton by lazy { findViewById<Button>(R.id.manage) }
@@ -50,7 +48,7 @@
private var isHiding = false
init {
- LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this);
+ LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this)
visibility = View.GONE
elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
@@ -95,7 +93,7 @@
*
* @param show whether the user education view should show or not.
*/
- fun show(expandedView: BubbleExpandedView, rect : Rect) {
+ fun show(expandedView: BubbleExpandedView, rect: Rect) {
if (visibility == VISIBLE) return
alpha = 0f
@@ -136,10 +134,13 @@
.withEndAction {
isHiding = false
visibility = GONE
- };
+ }
}
private fun setShouldShow(shouldShow: Boolean) {
- Prefs.putBoolean(context, HAS_SEEN_BUBBLES_MANAGE_EDUCATION, !shouldShow)
+ context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
+ .edit().putBoolean(PREF_MANAGED_EDUCATION, !shouldShow).apply()
}
-}
\ No newline at end of file
+}
+
+const val PREF_MANAGED_EDUCATION: String = "HasSeenBubblesManageOnboarding"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/MultiWindowTaskListener.java b/packages/SystemUI/src/com/android/systemui/bubbles/MultiWindowTaskListener.java
new file mode 100644
index 0000000..351a268
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/MultiWindowTaskListener.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.SurfaceControl;
+import android.window.TaskOrganizer;
+import android.window.WindowContainerToken;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+
+import java.util.ArrayList;
+
+/**
+ * Manages tasks that are displayed in multi-window (e.g. bubbles). These are displayed in a
+ * {@link TaskView}.
+ *
+ * This class listens on {@link TaskOrganizer} callbacks for events. Once visible, these tasks will
+ * intercept back press events.
+ *
+ * @see android.app.WindowConfiguration#WINDOWING_MODE_MULTI_WINDOW
+ * @see TaskView
+ */
+// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
+public class MultiWindowTaskListener implements ShellTaskOrganizer.TaskListener {
+ private static final String TAG = MultiWindowTaskListener.class.getSimpleName();
+
+ private static final boolean DEBUG = false;
+
+ //TODO(b/170153209): Have shell listener allow per task registration and remove this.
+ public interface Listener {
+ void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash);
+ void onTaskVanished(RunningTaskInfo taskInfo);
+ void onTaskInfoChanged(RunningTaskInfo taskInfo);
+ void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo);
+ }
+
+ private static class TaskData {
+ final RunningTaskInfo taskInfo;
+ final Listener listener;
+
+ TaskData(RunningTaskInfo info, Listener l) {
+ taskInfo = info;
+ listener = l;
+ }
+ }
+
+ private final Handler mHandler;
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final ArrayMap<WindowContainerToken, TaskData> mTasks = new ArrayMap<>();
+
+ private ArrayMap<IBinder, Listener> mLaunchCookieToListener = new ArrayMap<>();
+
+ /**
+ * Create a listener for tasks in multi-window mode.
+ */
+ public MultiWindowTaskListener(Handler handler, ShellTaskOrganizer organizer) {
+ mHandler = handler;
+ mTaskOrganizer = organizer;
+ mTaskOrganizer.addListener(this, TASK_LISTENER_TYPE_MULTI_WINDOW);
+ }
+
+ /**
+ * @return the task organizer that is listened to.
+ */
+ public TaskOrganizer getTaskOrganizer() {
+ return mTaskOrganizer;
+ }
+
+ public void setPendingLaunchCookieListener(IBinder cookie, Listener listener) {
+ mLaunchCookieToListener.put(cookie, listener);
+ }
+
+ /**
+ * Removes a task listener previously registered when starting a new activity.
+ */
+ public void removeListener(Listener listener) {
+ if (DEBUG) {
+ Log.d(TAG, "removeListener: listener=" + listener);
+ }
+ for (int i = 0; i < mTasks.size(); i++) {
+ if (mTasks.valueAt(i).listener == listener) {
+ mTasks.removeAt(i);
+ }
+ }
+ }
+
+ @Override
+ public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+ if (DEBUG) {
+ Log.d(TAG, "onTaskAppeared: taskInfo=" + taskInfo);
+ }
+
+ // We only care about task we launch which should all have a tracking launch cookie.
+ final ArrayList<IBinder> launchCookies = taskInfo.launchCookies;
+ if (launchCookies.isEmpty()) return;
+
+ // See if this task has one of our launch cookies.
+ Listener listener = null;
+ for (int i = launchCookies.size() - 1; i >= 0; --i) {
+ final IBinder cookie = launchCookies.get(i);
+ listener = mLaunchCookieToListener.get(cookie);
+ if (listener != null) {
+ mLaunchCookieToListener.remove(cookie);
+ break;
+ }
+ }
+
+ // This is either not a task we launched or we have handled it previously.
+ if (listener == null) return;
+
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(taskInfo.token, true);
+
+ final TaskData data = new TaskData(taskInfo, listener);
+ mTasks.put(taskInfo.token, data);
+ mHandler.post(() -> data.listener.onTaskAppeared(taskInfo, leash));
+ }
+
+ @Override
+ public void onTaskVanished(RunningTaskInfo taskInfo) {
+ final TaskData data = mTasks.remove(taskInfo.token);
+ if (data == null) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "onTaskVanished: taskInfo=" + taskInfo + " listener=" + data.listener);
+ }
+ mHandler.post(() -> data.listener.onTaskVanished(taskInfo));
+ }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ final TaskData data = mTasks.get(taskInfo.token);
+ if (data == null) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "onTaskInfoChanged: taskInfo=" + taskInfo + " listener=" + data.listener);
+ }
+ mHandler.post(() -> data.listener.onTaskInfoChanged(taskInfo));
+ }
+
+ @Override
+ public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
+ final TaskData data = mTasks.get(taskInfo.token);
+ if (data == null) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "onTaskInfoChanged: taskInfo=" + taskInfo + " listener=" + data.listener);
+ }
+ mHandler.post(() -> data.listener.onBackPressedOnTaskRoot(taskInfo));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt b/packages/SystemUI/src/com/android/systemui/bubbles/RelativeTouchListener.kt
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
rename to packages/SystemUI/src/com/android/systemui/bubbles/RelativeTouchListener.kt
index 8880df9..b1291a5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/RelativeTouchListener.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.util
+package com.android.systemui.bubbles
import android.graphics.PointF
import android.os.Handler
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt
index 3e4c729..216df2e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt
@@ -24,21 +24,19 @@
import android.widget.TextView
import com.android.internal.util.ContrastColorUtil
import com.android.systemui.Interpolators
-import com.android.systemui.Prefs
-import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION
import com.android.systemui.R
/**
* User education view to highlight the collapsed stack of bubbles.
* Shown only the first time a user taps the stack.
*/
-class StackEducationView constructor(context: Context) : LinearLayout(context){
+class StackEducationView constructor(context: Context) : LinearLayout(context) {
private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView"
else BubbleDebugConfig.TAG_BUBBLES
- private val ANIMATE_DURATION : Long = 200
- private val ANIMATE_DURATION_SHORT : Long = 40
+ private val ANIMATE_DURATION: Long = 200
+ private val ANIMATE_DURATION_SHORT: Long = 40
private val view by lazy { findViewById<View>(R.id.stack_education_layout) }
private val titleTextView by lazy { findViewById<TextView>(R.id.stack_education_title) }
@@ -47,7 +45,7 @@
private var isHiding = false
init {
- LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this);
+ LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this)
visibility = View.GONE
elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
@@ -93,7 +91,7 @@
*
* @return true if user education was shown, false otherwise.
*/
- fun show(stackPosition: PointF) : Boolean{
+ fun show(stackPosition: PointF): Boolean {
if (visibility == VISIBLE) return false
setAlpha(0f)
@@ -129,6 +127,9 @@
}
private fun setShouldShow(shouldShow: Boolean) {
- Prefs.putBoolean(context, HAS_SEEN_BUBBLES_EDUCATION, !shouldShow)
+ context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
+ .edit().putBoolean(PREF_STACK_EDUCATION, !shouldShow).apply()
}
-}
\ No newline at end of file
+}
+
+const val PREF_STACK_EDUCATION: String = "HasSeenBubblesOnboarding"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java b/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
new file mode 100644
index 0000000..b78e677
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import dalvik.system.CloseGuard;
+
+/**
+ * View that can display a task.
+ */
+// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
+public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
+ MultiWindowTaskListener.Listener {
+
+ public interface Listener {
+ /** Called when the container is ready for launching activities. */
+ default void onInitialized() {}
+
+ /** Called when the container can no longer launch activities. */
+ default void onReleased() {}
+
+ /** Called when a task is created inside the container. */
+ default void onTaskCreated(int taskId, ComponentName name) {}
+
+ /** Called when a task visibility changes. */
+ default void onTaskVisibilityChanged(int taskId, boolean visible) {}
+
+ /** Called when a task is about to be removed from the stack inside the container. */
+ default void onTaskRemovalStarted(int taskId) {}
+
+ /** Called when a task is created inside the container. */
+ default void onBackPressedOnTaskRoot(int taskId) {}
+ }
+
+ private final CloseGuard mGuard = CloseGuard.get();
+
+ private final MultiWindowTaskListener mMultiWindowTaskListener;
+
+ private ActivityManager.RunningTaskInfo mTaskInfo;
+ private WindowContainerToken mTaskToken;
+ private SurfaceControl mTaskLeash;
+ private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+ private boolean mSurfaceCreated;
+ private boolean mIsInitialized;
+ private Listener mListener;
+
+ private final Rect mTmpRect = new Rect();
+ private final Rect mTmpRootRect = new Rect();
+
+ public TaskView(Context context, MultiWindowTaskListener taskListener) {
+ super(context, null, 0, 0, true /* disableBackgroundLayer */);
+
+ mMultiWindowTaskListener = taskListener;
+ setUseAlpha();
+ getHolder().addCallback(this);
+ mGuard.open("release");
+ }
+
+ /**
+ * Only one listener may be set on the view, throws an exception otherwise.
+ */
+ public void setListener(Listener listener) {
+ if (mListener != null) {
+ throw new IllegalStateException(
+ "Trying to set a listener when one has already been set");
+ }
+ mListener = listener;
+ }
+
+ /**
+ * Launch an activity represented by {@link ShortcutInfo}.
+ * <p>The owner of this container must be allowed to access the shortcut information,
+ * as defined in {@link LauncherApps#hasShortcutHostPermission()} to use this method.
+ *
+ * @param shortcut the shortcut used to launch the activity.
+ * @param options options for the activity.
+ * @param sourceBounds the rect containing the source bounds of the clicked icon to open
+ * this shortcut.
+ */
+ public void startShortcutActivity(@NonNull ShortcutInfo shortcut,
+ @NonNull ActivityOptions options, @Nullable Rect sourceBounds) {
+ prepareActivityOptions(options);
+ LauncherApps service = mContext.getSystemService(LauncherApps.class);
+ try {
+ service.startShortcut(shortcut, sourceBounds, options.toBundle());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Launch a new activity.
+ *
+ * @param pendingIntent Intent used to launch an activity.
+ * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}
+ * @param options options for the activity.
+ */
+ public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
+ @NonNull ActivityOptions options) {
+ prepareActivityOptions(options);
+ try {
+ pendingIntent.send(mContext, 0 /* code */, fillInIntent,
+ null /* onFinished */, null /* handler */, null /* requiredPermission */,
+ options.toBundle());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void prepareActivityOptions(ActivityOptions options) {
+ final Binder launchCookie = new Binder();
+ mMultiWindowTaskListener.setPendingLaunchCookieListener(launchCookie, this);
+ options.setLaunchCookie(launchCookie);
+ options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ options.setTaskAlwaysOnTop(true);
+ }
+
+ /**
+ * Call when view position or size has changed. Do not call when animating.
+ */
+ public void onLocationChanged() {
+ if (mTaskToken == null) {
+ return;
+ }
+ // Update based on the screen bounds
+ getBoundsOnScreen(mTmpRect);
+ getRootView().getBoundsOnScreen(mTmpRootRect);
+ if (!mTmpRootRect.contains(mTmpRect)) {
+ mTmpRect.offsetTo(0, 0);
+ }
+
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setBounds(mTaskToken, mTmpRect);
+ // TODO(b/151449487): Enable synchronization
+ mMultiWindowTaskListener.getTaskOrganizer().applyTransaction(wct);
+ }
+
+ /**
+ * Release this container if it is initialized.
+ */
+ public void release() {
+ performRelease();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mGuard != null) {
+ mGuard.warnIfOpen();
+ performRelease();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void performRelease() {
+ getHolder().removeCallback(this);
+ mMultiWindowTaskListener.removeListener(this);
+ resetTaskInfo();
+ mGuard.close();
+ if (mListener != null && mIsInitialized) {
+ mListener.onReleased();
+ mIsInitialized = false;
+ }
+ }
+
+ private void resetTaskInfo() {
+ mTaskInfo = null;
+ mTaskToken = null;
+ mTaskLeash = null;
+ }
+
+ private void updateTaskVisibility() {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
+ mMultiWindowTaskListener.getTaskOrganizer().applyTransaction(wct);
+ // TODO(b/151449487): Only call callback once we enable synchronization
+ if (mListener != null) {
+ mListener.onTaskVisibilityChanged(mTaskInfo.taskId, mSurfaceCreated);
+ }
+ }
+
+ @Override
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl leash) {
+ mTaskInfo = taskInfo;
+ mTaskToken = taskInfo.token;
+ mTaskLeash = leash;
+
+ if (mSurfaceCreated) {
+ // Surface is ready, so just reparent the task to this surface control
+ mTransaction.reparent(mTaskLeash, getSurfaceControl())
+ .show(mTaskLeash)
+ .apply();
+ } else {
+ // The surface has already been destroyed before the task has appeared, so go ahead and
+ // hide the task entirely
+ updateTaskVisibility();
+ }
+
+ // TODO: Synchronize show with the resize
+ onLocationChanged();
+ setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+
+ if (mListener != null) {
+ mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity);
+ }
+ }
+
+ @Override
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mTaskToken != null && mTaskToken.equals(taskInfo.token)) {
+ if (mListener != null) {
+ mListener.onTaskRemovalStarted(taskInfo.taskId);
+ }
+
+ // Unparent the task when this surface is destroyed
+ mTransaction.reparent(mTaskLeash, null).apply();
+ resetTaskInfo();
+ }
+ }
+
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ mTaskInfo.taskDescription = taskInfo.taskDescription;
+ setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+ }
+
+ @Override
+ public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mTaskToken != null && mTaskToken.equals(taskInfo.token)) {
+ if (mListener != null) {
+ mListener.onBackPressedOnTaskRoot(taskInfo.taskId);
+ }
+ }
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mSurfaceCreated = true;
+ if (mListener != null && !mIsInitialized) {
+ mIsInitialized = true;
+ mListener.onInitialized();
+ }
+ if (mTaskToken == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+ // Reparent the task when this surface is created
+ mTransaction.reparent(mTaskLeash, getSurfaceControl())
+ .show(mTaskLeash)
+ .apply();
+ updateTaskVisibility();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ if (mTaskToken == null) {
+ return;
+ }
+ onLocationChanged();
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mSurfaceCreated = false;
+ if (mTaskToken == null) {
+ // Nothing to update, task is not yet available
+ return;
+ }
+
+ // Unparent the task when this surface is destroyed
+ mTransaction.reparent(mTaskLeash, null).apply();
+ updateTaskVisibility();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 9f88ee5..7fdc019 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -58,6 +58,9 @@
/** Duration of the expand/collapse target path animation. */
public static final int EXPAND_COLLAPSE_TARGET_ANIM_DURATION = 175;
+ /** Damping ratio for expand/collapse spring. */
+ private static final float DAMPING_RATIO_MEDIUM_LOW_BOUNCY = 0.65f;
+
/** Stiffness for the expand/collapse path-following animation. */
private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 1000;
@@ -271,16 +274,14 @@
// Then, draw a line across the screen to the bubble's resting position.
path.lineTo(getBubbleLeft(index), expandedY);
} else {
- final float sideMultiplier =
- mLayout.isFirstChildXLeftOfCenter(mCollapsePoint.x) ? -1 : 1;
- final float stackedX = mCollapsePoint.x + (sideMultiplier * index * mStackOffsetPx);
+ final float stackedX = mCollapsePoint.x;
// If we're collapsing, draw a line from the bubble's current position to the side
// of the screen where the bubble will be stacked.
path.lineTo(stackedX, expandedY);
// Then, draw a line down to the stack position.
- path.lineTo(stackedX, mCollapsePoint.y);
+ path.lineTo(stackedX, mCollapsePoint.y + index * mStackOffsetPx);
}
// The lead bubble should be the bubble with the longest distance to travel when we're
@@ -510,7 +511,7 @@
@Override
SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) {
return new SpringForce()
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+ .setDampingRatio(DAMPING_RATIO_MEDIUM_LOW_BOUNCY)
.setStiffness(SpringForce.STIFFNESS_LOW);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index b7490a5..1205124 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -72,9 +72,9 @@
/**
* Values to use for the default {@link SpringForce} provided to the physics animation layout.
*/
- public static final int DEFAULT_STIFFNESS = 12000;
+ public static final int SPRING_TO_TOUCH_STIFFNESS = 12000;
public static final float IME_ANIMATION_STIFFNESS = SpringForce.STIFFNESS_LOW;
- private static final int FLING_FOLLOW_STIFFNESS = 500;
+ private static final int CHAIN_STIFFNESS = 600;
public static final float DEFAULT_BOUNCINESS = 0.9f;
private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfig =
@@ -629,7 +629,7 @@
public void moveStackFromTouch(float x, float y) {
// Begin the spring-to-touch catch up animation if needed.
if (mSpringToTouchOnNextMotionEvent) {
- springStack(x, y, DEFAULT_STIFFNESS);
+ springStack(x, y, SPRING_TO_TOUCH_STIFFNESS);
mSpringToTouchOnNextMotionEvent = false;
mFirstBubbleSpringingToTouch = true;
} else if (mFirstBubbleSpringingToTouch) {
@@ -744,15 +744,13 @@
@Override
float getOffsetForChainedPropertyAnimation(DynamicAnimation.ViewProperty property) {
- if (property.equals(DynamicAnimation.TRANSLATION_X)) {
+ if (property.equals(DynamicAnimation.TRANSLATION_Y)) {
// If we're in the dismiss target, have the bubbles pile on top of each other with no
// offset.
if (isStackStuckToTarget()) {
return 0f;
} else {
- // Offset to the left if we're on the left, or the right otherwise.
- return mLayout.isFirstChildXLeftOfCenter(mStackPosition.x)
- ? -mStackOffset : mStackOffset;
+ return mStackOffset;
}
} else {
return 0f;
@@ -762,14 +760,12 @@
@Override
SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) {
final ContentResolver contentResolver = mLayout.getContext().getContentResolver();
- final float stiffness = Settings.Secure.getFloat(contentResolver, "bubble_stiffness",
- mIsMovingFromFlinging ? FLING_FOLLOW_STIFFNESS : DEFAULT_STIFFNESS /* default */);
final float dampingRatio = Settings.Secure.getFloat(contentResolver, "bubble_damping",
DEFAULT_BOUNCINESS);
return new SpringForce()
.setDampingRatio(dampingRatio)
- .setStiffness(stiffness);
+ .setStiffness(CHAIN_STIFFNESS);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index 00ae3a3..6b5f237 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -19,13 +19,15 @@
import android.app.INotificationManager;
import android.content.Context;
import android.content.pm.LauncherApps;
+import android.os.Handler;
import android.view.WindowManager;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.bubbles.BubbleDataRepository;
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -39,6 +41,7 @@
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -68,13 +71,15 @@
FeatureFlags featureFlags,
DumpManager dumpManager,
FloatingContentCoordinator floatingContentCoordinator,
- BubbleDataRepository bubbleDataRepository,
SysUiState sysUiState,
INotificationManager notifManager,
IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
- LauncherApps launcherApps) {
+ LauncherApps launcherApps,
+ UiEventLogger uiEventLogger,
+ @Main Handler mainHandler,
+ ShellTaskOrganizer organizer) {
return BubbleController.create(
context,
notificationShadeWindowController,
@@ -91,12 +96,14 @@
featureFlags,
dumpManager,
floatingContentCoordinator,
- bubbleDataRepository,
sysUiState,
notifManager,
statusBarService,
windowManager,
windowManagerShellWrapper,
- launcherApps);
+ launcherApps,
+ uiEventLogger,
+ mainHandler,
+ organizer);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
index f447965..ce0786d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
@@ -18,16 +18,11 @@
import android.content.Context
import android.util.AtomicFile
import android.util.Log
-import com.android.systemui.dagger.SysUISingleton
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
-import javax.inject.Inject
-@SysUISingleton
-class BubblePersistentRepository @Inject constructor(
- context: Context
-) {
+class BubblePersistentRepository(context: Context) {
private val bubbleFile: AtomicFile = AtomicFile(File(context.filesDir,
"overflow_bubbles.xml"), "overflow-bubbles")
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
index c6d5732..e0a7c78 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
@@ -19,8 +19,6 @@
import android.os.UserHandle
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.bubbles.ShortcutKey
-import com.android.systemui.dagger.SysUISingleton
-import javax.inject.Inject
private const val CAPACITY = 16
@@ -28,10 +26,7 @@
* BubbleVolatileRepository holds the most updated snapshot of list of bubbles for in-memory
* manipulation.
*/
-@SysUISingleton
-class BubbleVolatileRepository @Inject constructor(
- private val launcherApps: LauncherApps
-) {
+class BubbleVolatileRepository(private val launcherApps: LauncherApps) {
/**
* An ordered set of bubbles based on their natural ordering.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index e303754..cb90b61 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -66,6 +66,7 @@
import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -263,6 +264,13 @@
return ActivityManagerWrapper.getInstance();
}
+ /** */
+ @Provides
+ @SysUISingleton
+ public TaskStackChangeListeners provideTaskStackChangeListeners() {
+ return TaskStackChangeListeners.getInstance();
+ }
+
/** Provides and initializes the {#link BroadcastDispatcher} for SystemUI */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 2c0b04f..7d5683f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -33,6 +33,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
+import com.android.systemui.media.dagger.MediaModule;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
@@ -74,9 +75,10 @@
* overridden by the System UI implementation.
*/
@Module(includes = {
- QSModule.class,
- WMShellModule.class
- })
+ MediaModule.class,
+ QSModule.class,
+ WMShellModule.class
+})
public abstract class SystemUIDefaultModule {
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 6154a4e..f470a6b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -123,6 +123,14 @@
}
/**
+ * Appends dozing event to the logs
+ * @param suppressed true if dozing is suppressed
+ */
+ public void traceDozingSuppressed(boolean suppressed) {
+ mLogger.logDozingSuppressed(suppressed);
+ }
+
+ /**
* Appends fling event to the logs
*/
public void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 46cec95..0c9e143 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -64,6 +64,14 @@
})
}
+ fun logDozingSuppressed(isDozingSuppressed: Boolean) {
+ buffer.log(TAG, INFO, {
+ bool1 = isDozingSuppressed
+ }, {
+ "DozingSuppressed=$bool1"
+ })
+ }
+
fun logFling(
expand: Boolean,
aboveThreshold: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 1e0460b..befb648 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -413,6 +413,7 @@
pw.print(" state="); pw.println(mState);
pw.print(" wakeLockHeldForCurrentState="); pw.println(mWakeLockHeldForCurrentState);
pw.print(" wakeLock="); pw.println(mWakeLock);
+ pw.print(" isDozeSuppressed="); pw.println(mDozeHost.isDozeSuppressed());
pw.println("Parts:");
for (Part p : mParts) {
p.dump(pw);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java
index 7a8b816..435859a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java
@@ -24,7 +24,7 @@
import javax.inject.Scope;
/**
- * Scope annotation for singleton items within the StatusBarComponent.
+ * Scope annotation for singleton items within the DozeComponent.
*/
@Documented
@Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index b55b29a..a330be6 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -140,8 +140,14 @@
d.setContentView(R.layout.shutdown_dialog);
d.setCancelable(false);
- int color = Utils.getColorAttrDefaultColor(mContext,
- com.android.systemui.R.attr.wallpaperTextColor);
+ int color;
+ if (mBlurUtils.supportsBlursOnWindows()) {
+ color = Utils.getColorAttrDefaultColor(mContext,
+ com.android.systemui.R.attr.wallpaperTextColor);
+ } else {
+ color = mContext.getResources().getColor(
+ com.android.systemui.R.color.global_actions_shutdown_ui_text);
+ }
ProgressBar bar = d.findViewById(R.id.progress);
bar.getIndeterminateDrawable().setTint(color);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index c281ece..7585110 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -30,8 +30,8 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
public class WorkLockActivityController {
private static final String TAG = WorkLockActivityController.class.getSimpleName();
@@ -40,16 +40,16 @@
private final IActivityTaskManager mIatm;
public WorkLockActivityController(Context context) {
- this(context, ActivityManagerWrapper.getInstance(), ActivityTaskManager.getService());
+ this(context, TaskStackChangeListeners.getInstance(), ActivityTaskManager.getService());
}
@VisibleForTesting
WorkLockActivityController(
- Context context, ActivityManagerWrapper am, IActivityTaskManager iAtm) {
+ Context context, TaskStackChangeListeners tscl, IActivityTaskManager iAtm) {
mContext = context;
mIatm = iAtm;
- am.registerTaskStackListener(mLockListener);
+ tscl.registerTaskStackListener(mLockListener);
}
private void startWorkChallengeInTask(int taskId, int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 094ece2..6fb8650 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -18,6 +18,7 @@
import android.view.View
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.dagger.MediaModule.KEYGUARD
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
@@ -25,6 +26,7 @@
import com.android.systemui.statusbar.notification.stack.MediaHeaderView
import com.android.systemui.statusbar.phone.KeyguardBypassController
import javax.inject.Inject
+import javax.inject.Named
/**
* A class that controls the media notifications on the lock screen, handles its visibility and
@@ -32,7 +34,7 @@
*/
@SysUISingleton
class KeyguardMediaController @Inject constructor(
- private val mediaHost: MediaHost,
+ @param:Named(KEYGUARD) private val mediaHost: MediaHost,
private val bypassController: KeyguardBypassController,
private val statusBarStateController: SysuiStatusBarStateController,
private val notifLockscreenUserManager: NotificationLockscreenUserManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index d80aafb..cb14f31 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -282,10 +282,12 @@
scrollXAmount = -1 * relativePos
}
if (scrollXAmount != 0) {
+ val dx = if (isRtl) -scrollXAmount else scrollXAmount
+ val newScrollX = scrollView.relativeScrollX + dx
// Delay the scrolling since scrollView calls springback which cancels
// the animation again..
mainExecutor.execute {
- scrollView.smoothScrollBy(if (isRtl) -scrollXAmount else scrollXAmount, 0)
+ scrollView.smoothScrollTo(newScrollX, scrollView.scrollY)
}
}
val currentTranslation = scrollView.getContentTranslation()
@@ -553,4 +555,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index f6571ef..c18a6a4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -42,10 +42,10 @@
import androidx.constraintlayout.widget.ConstraintSet;
import com.android.settingslib.Utils;
-import com.android.settingslib.media.MediaOutputSliceConstants;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.util.animation.TransitionLayout;
@@ -93,7 +93,7 @@
private int mAlbumArtRadius;
// This will provide the corners for the album art.
private final ViewOutlineProvider mViewOutlineProvider;
-
+ private final MediaOutputDialogFactory mMediaOutputDialogFactory;
/**
* Initialize a new control panel
* @param context
@@ -104,7 +104,8 @@
public MediaControlPanel(Context context, @Background Executor backgroundExecutor,
ActivityStarter activityStarter, MediaViewController mediaViewController,
SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager,
- KeyguardDismissUtil keyguardDismissUtil) {
+ KeyguardDismissUtil keyguardDismissUtil, MediaOutputDialogFactory
+ mediaOutputDialogFactory) {
mContext = context;
mBackgroundExecutor = backgroundExecutor;
mActivityStarter = activityStarter;
@@ -112,6 +113,7 @@
mMediaViewController = mediaViewController;
mMediaDataManagerLazy = lazyMediaDataManager;
mKeyguardDismissUtil = keyguardDismissUtil;
+ mMediaOutputDialogFactory = mediaOutputDialogFactory;
loadDimens();
mViewOutlineProvider = new ViewOutlineProvider() {
@@ -273,13 +275,7 @@
setVisibleAndAlpha(collapsedSet, R.id.media_seamless, true /*visible */);
setVisibleAndAlpha(expandedSet, R.id.media_seamless, true /*visible */);
mViewHolder.getSeamless().setOnClickListener(v -> {
- final Intent intent = new Intent()
- .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
- .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
- data.getPackageName())
- .putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, mToken);
- mActivityStarter.startActivity(intent, false, true /* dismissShade */,
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ mMediaOutputDialogFactory.create(data.getPackageName(), true);
});
ImageView iconView = mViewHolder.getSeamlessIcon();
@@ -358,7 +354,15 @@
final MediaController controller = getController();
mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller));
+ // Guts label
+ boolean isDismissible = data.isClearable();
+ mViewHolder.getSettingsText().setText(isDismissible
+ ? R.string.controls_media_close_session
+ : R.string.controls_media_active_session);
+
// Dismiss
+ mViewHolder.getDismissLabel().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
+ mViewHolder.getDismiss().setEnabled(isDismissible);
mViewHolder.getDismiss().setOnClickListener(v -> {
if (mKey != null) {
closeGuts();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index ce184aa..857c50f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -11,7 +11,7 @@
import java.util.Objects
import javax.inject.Inject
-class MediaHost @Inject constructor(
+class MediaHost constructor(
private val state: MediaHostStateHolder,
private val mediaHierarchyManager: MediaHierarchyManager,
private val mediaDataManager: MediaDataManager,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index 51dbfa7..ce72991 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -46,7 +46,7 @@
/**
* Callback representing that a media object is now expired:
* @param token Media session unique identifier
- * @param pauseTimeuot True when expired for {@code PAUSED_MEDIA_TIMEOUT}
+ * @param pauseTimeout True when expired for {@code PAUSED_MEDIA_TIMEOUT}
*/
lateinit var timeoutCallback: (String, Boolean) -> Unit
@@ -57,11 +57,10 @@
// Having an old key means that we're migrating from/to resumption. We should update
// the old listener to make sure that events will be dispatched to the new location.
val migrating = oldKey != null && key != oldKey
- var wasPlaying = false
if (migrating) {
val reusedListener = mediaListeners.remove(oldKey)
if (reusedListener != null) {
- wasPlaying = reusedListener.playing ?: false
+ val wasPlaying = reusedListener.playing ?: false
if (DEBUG) Log.d(TAG, "migrating key $oldKey to $key, for resumption")
reusedListener.mediaData = data
reusedListener.key = key
@@ -159,9 +158,8 @@
Log.v(TAG, "Execute timeout for $key")
}
timedOut = true
- if (dispatchEvents) {
- timeoutCallback(key, timedOut)
- }
+ // this event is async, so it's safe even when `dispatchEvents` is false
+ timeoutCallback(key, timedOut)
}, PAUSED_MEDIA_TIMEOUT)
} else {
expireMediaTimeout(key, "playback started - $state, $key")
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
index 11551ac..16327bd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
@@ -23,7 +23,6 @@
import android.widget.ImageView
import android.widget.SeekBar
import android.widget.TextView
-import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.R
import com.android.systemui.util.animation.TransitionLayout
@@ -61,8 +60,10 @@
val action4 = itemView.requireViewById<ImageButton>(R.id.action4)
// Settings screen
+ val settingsText = itemView.requireViewById<TextView>(R.id.remove_text)
val cancel = itemView.requireViewById<View>(R.id.cancel)
- val dismiss = itemView.requireViewById<View>(R.id.dismiss)
+ val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss)
+ val dismissLabel = dismiss.getChildAt(0)
val settings = itemView.requireViewById<View>(R.id.settings)
init {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
new file mode 100644
index 0000000..57ac9df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dagger;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.media.MediaDataManager;
+import com.android.systemui.media.MediaHierarchyManager;
+import com.android.systemui.media.MediaHost;
+import com.android.systemui.media.MediaHostStatesManager;
+
+import javax.inject.Named;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** Dagger module for the media package. */
+@Module
+public interface MediaModule {
+ String QS_PANEL = "media_qs_panel";
+ String QUICK_QS_PANEL = "media_quick_qs_panel";
+ String KEYGUARD = "media_keyguard";
+
+ /** */
+ @Provides
+ @SysUISingleton
+ @Named(QS_PANEL)
+ static MediaHost providesQSMediaHost(MediaHost.MediaHostStateHolder stateHolder,
+ MediaHierarchyManager hierarchyManager, MediaDataManager dataManager,
+ MediaHostStatesManager statesManager) {
+ return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
+ }
+
+ /** */
+ @Provides
+ @SysUISingleton
+ @Named(QUICK_QS_PANEL)
+ static MediaHost providesQuickQSMediaHost(MediaHost.MediaHostStateHolder stateHolder,
+ MediaHierarchyManager hierarchyManager, MediaDataManager dataManager,
+ MediaHostStatesManager statesManager) {
+ return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
+ }
+
+ /** */
+ @Provides
+ @SysUISingleton
+ @Named(KEYGUARD)
+ static MediaHost providesKeyguardMediaHost(MediaHost.MediaHostStateHolder stateHolder,
+ MediaHierarchyManager hierarchyManager, MediaDataManager dataManager,
+ MediaHostStatesManager statesManager) {
+ return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 3b82999..caef536 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -49,7 +49,7 @@
* Base dialog for media output UI
*/
public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
- MediaOutputController.Callback {
+ MediaOutputController.Callback, Window.Callback {
private static final String TAG = "MediaOutputDialog";
@@ -210,4 +210,12 @@
public void dismissDialog() {
dismiss();
}
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ if (!hasFocus && isShowing()) {
+ dismiss();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
index 6cbf065..df9e7a4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
@@ -47,6 +47,7 @@
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.RotationLockController;
@@ -156,7 +157,7 @@
throw e.rethrowFromSystemServer();
}
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
}
void unregisterListeners() {
@@ -171,7 +172,7 @@
throw e.rethrowFromSystemServer();
}
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
}
void addRotationCallback(Consumer<Integer> watcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index af851a7..4efe4d8 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -58,7 +58,6 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.system.QuickStepContract;
@@ -426,12 +425,6 @@
if (getDisplay() != null) {
displayId = getDisplay().getDisplayId();
}
- // Bubbles will give us a valid display id if it should get the back event
- Bubbles Bubbles = Dependency.get(Bubbles.class);
- int bubbleDisplayId = Bubbles.getExpandedDisplayId(mContext);
- if (mCode == KeyEvent.KEYCODE_BACK && bubbleDisplayId != INVALID_DISPLAY) {
- displayId = bubbleDisplayId;
- }
if (displayId != INVALID_DISPLAY) {
ev.setDisplayId(displayId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index fc9c3d9..18cc746 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -15,8 +15,6 @@
*/
package com.android.systemui.navigationbar.gestural;
-import static android.view.Display.INVALID_DISPLAY;
-
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
@@ -57,7 +55,6 @@
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -71,6 +68,7 @@
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
@@ -387,7 +385,7 @@
mGestureNavigationSettingsObserver.unregister();
mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
mPluginManager.removePluginListener(this);
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
try {
@@ -403,7 +401,7 @@
updateDisplaySize();
mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
mContext.getMainThreadHandler());
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
runnable -> (mContext.getMainThreadHandler()).post(runnable),
mOnPropertiesChangedListener);
@@ -733,13 +731,7 @@
KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
- // Bubbles will give us a valid display id if it should get the back event
- final int bubbleDisplayId = Dependency.get(Bubbles.class).getExpandedDisplayId(mContext);
- if (bubbleDisplayId != INVALID_DISPLAY) {
- ev.setDisplayId(bubbleDisplayId);
- } else {
- ev.setDisplayId(mContext.getDisplay().getDisplayId());
- }
+ ev.setDisplayId(mContext.getDisplay().getDisplayId());
InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index 284f41a..fbbda5f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -16,6 +16,8 @@
package com.android.systemui.navigationbar.gestural;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
@@ -334,6 +336,7 @@
.getDimension(R.dimen.navigation_edge_action_drag_threshold);
setVisibility(GONE);
+ boolean isPrimaryDisplay = mContext.getDisplayId() == DEFAULT_DISPLAY;
mRegionSamplingHelper = new RegionSamplingHelper(this,
new RegionSamplingHelper.SamplingCallback() {
@Override
@@ -345,8 +348,14 @@
public Rect getSampledRegion(View sampledView) {
return mSamplingRect;
}
+
+ @Override
+ public boolean isSamplingEnabled() {
+ return isPrimaryDisplay;
+ }
});
mRegionSamplingHelper.setWindowVisible(true);
+ mShowProtection = !isPrimaryDisplay;
}
@Override
@@ -366,11 +375,6 @@
updateIsDark(animate);
}
- private void setShowProtection(boolean showProtection) {
- mShowProtection = showProtection;
- invalidate();
- }
-
@Override
public void setIsLeftPanel(boolean isLeftPanel) {
mIsLeftPanel = isLeftPanel;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index ca3e4cfb..efe4609 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs;
+import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
import static com.android.systemui.util.Utils.useQsMediaPlayer;
@@ -158,7 +159,7 @@
DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher,
QSLogger qsLogger,
- MediaHost mediaHost,
+ @Named(QS_PANEL) MediaHost mediaHost,
UiEventLogger uiEventLogger,
UserTracker userTracker
) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index ea036f6..82cb863 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs;
+import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
import android.content.Context;
@@ -70,7 +71,7 @@
DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher,
QSLogger qsLogger,
- MediaHost mediaHost,
+ @Named(QUICK_QS_PANEL) MediaHost mediaHost,
UiEventLogger uiEventLogger,
UserTracker userTracker
) {
@@ -108,6 +109,7 @@
}
@Override
+
protected void initMediaHostState() {
mMediaHost.setExpansion(0.0f);
mMediaHost.setShowsOnlyActiveMedia(true);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 8ff96c8..953de60 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -21,6 +21,7 @@
import android.os.Handler;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.media.dagger.MediaModule;
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSTileHost;
@@ -38,7 +39,7 @@
* Module for QS dependencies
*/
// TODO: Add other QS classes
-@Module
+@Module(includes = {MediaModule.class})
public interface QSModule {
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 5279a20..ddf30ad 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -95,7 +95,6 @@
import com.android.wm.shell.onehanded.OneHandedEvents;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.phone.PipUtils;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.FileDescriptor;
@@ -151,7 +150,6 @@
private int mConnectionBackoffAttempts;
private boolean mBound;
private boolean mIsEnabled;
- private boolean mHasPipFeature;
private int mCurrentBoundedUserId = -1;
private float mNavBarButtonAlpha;
private boolean mInputFocusTransferStarted;
@@ -377,9 +375,7 @@
@Override
public void setShelfHeight(boolean visible, int shelfHeight) {
- if (!verifyCaller("setShelfHeight") || !mHasPipFeature) {
- Log.w(TAG_OPS,
- "ByPass setShelfHeight, FEATURE_PICTURE_IN_PICTURE:" + mHasPipFeature);
+ if (!verifyCaller("setShelfHeight")) {
return;
}
final long token = Binder.clearCallingIdentity();
@@ -405,9 +401,7 @@
@Override
public void notifySwipeToHomeFinished() {
- if (!verifyCaller("notifySwipeToHomeFinished") || !mHasPipFeature) {
- Log.w(TAG_OPS, "ByPass notifySwipeToHomeFinished, FEATURE_PICTURE_IN_PICTURE:"
- + mHasPipFeature);
+ if (!verifyCaller("notifySwipeToHomeFinished")) {
return;
}
final long token = Binder.clearCallingIdentity();
@@ -422,9 +416,7 @@
@Override
public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
- if (!verifyCaller("setPinnedStackAnimationListener") || !mHasPipFeature) {
- Log.w(TAG_OPS, "ByPass setPinnedStackAnimationListener, FEATURE_PICTURE_IN_PICTURE:"
- + mHasPipFeature);
+ if (!verifyCaller("setPinnedStackAnimationListener")) {
return;
}
mIPinnedStackAnimationListener = listener;
@@ -509,7 +501,7 @@
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams,
int launcherRotation, int shelfHeight) {
- if (!verifyCaller("startSwipePipToHome") || !mHasPipFeature) {
+ if (!verifyCaller("startSwipePipToHome")) {
return null;
}
final long binderToken = Binder.clearCallingIdentity();
@@ -525,7 +517,7 @@
@Override
public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
- if (!verifyCaller("stopSwipePipToHome") || !mHasPipFeature) {
+ if (!verifyCaller("stopSwipePipToHome")) {
return;
}
final long binderToken = Binder.clearCallingIdentity();
@@ -650,7 +642,6 @@
super(broadcastDispatcher);
mContext = context;
mPipOptional = pipOptional;
- mHasPipFeature = PipUtils.hasSystemFeature(mContext);
mStatusBarOptionalLazy = statusBarOptionalLazy;
mHandler = new Handler();
mNavBarControllerLazy = navBarControllerLazy;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index 9c5a3de..ccf2598 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -67,6 +67,7 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.io.PrintWriter;
import java.util.Collections;
@@ -365,7 +366,7 @@
mOverviewProxyListenerRegistered = true;
}
if (!mTaskListenerRegistered) {
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskListener);
mTaskListenerRegistered = true;
}
}
@@ -377,7 +378,7 @@
mOverviewProxyListenerRegistered = false;
}
if (mTaskListenerRegistered) {
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskListener);
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskListener);
mTaskListenerRegistered = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 1a9abb9..e6f43c1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -132,7 +132,7 @@
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setVideoEncodingProfileLevel(
MediaCodecInfo.CodecProfileLevel.AVCProfileHigh,
- MediaCodecInfo.CodecProfileLevel.AVCLevel42);
+ MediaCodecInfo.CodecProfileLevel.AVCLevel3);
mMediaRecorder.setVideoSize(screenWidth, screenHeight);
mMediaRecorder.setVideoFrameRate(refereshRate);
mMediaRecorder.setVideoEncodingBitRate(vidBitRate);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 2b4fa2a..7d69753 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -45,6 +45,7 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.LayerDrawable;
import android.media.MediaActionSound;
@@ -69,6 +70,7 @@
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
@@ -104,7 +106,6 @@
public Bitmap image;
public Consumer<Uri> finisher;
public GlobalScreenshot.ActionsReadyListener mActionsReadyListener;
- public int errorMsgResId;
void clearImage() {
image = null;
@@ -184,6 +185,7 @@
private final WindowManager.LayoutParams mWindowLayoutParams;
private final Display mDisplay;
private final DisplayMetrics mDisplayMetrics;
+ private final AccessibilityManager mAccessibilityManager;
private View mScreenshotLayout;
private ScreenshotSelectorView mScreenshotSelectorView;
@@ -193,6 +195,8 @@
private ImageView mActionsContainerBackground;
private HorizontalScrollView mActionsContainer;
private LinearLayout mActionsView;
+ private ScreenshotActionChip mShareChip;
+ private ScreenshotActionChip mEditChip;
private ImageView mBackgroundProtection;
private FrameLayout mDismissButton;
@@ -214,6 +218,14 @@
private int mLeftInset;
private int mRightInset;
+ private ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>();
+ private PendingInteraction mPendingInteraction;
+ private enum PendingInteraction {
+ PREVIEW,
+ EDIT,
+ SHARE
+ }
+
// standard material ease
private final Interpolator mFastOutSlowIn;
@@ -242,6 +254,7 @@
mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
mUiEventLogger = uiEventLogger;
+ mAccessibilityManager = AccessibilityManager.getInstance(mContext);
reloadAssets();
Configuration config = mContext.getResources().getConfiguration();
@@ -319,8 +332,17 @@
Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
Consumer<Uri> finisher, Runnable onComplete) {
// TODO: use task Id, userId, topComponent for smart handler
-
mOnCompleteRunnable = onComplete;
+
+ if (screenshot == null) {
+ Log.e(TAG, "Got null bitmap from screenshot message");
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ finisher.accept(null);
+ mOnCompleteRunnable.run();
+ return;
+ }
+
if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
} else {
@@ -537,6 +559,9 @@
mOnCompleteRunnable.run();
});
+ mShareChip = mActionsContainer.findViewById(R.id.screenshot_share_chip);
+ mEditChip = mActionsContainer.findViewById(R.id.screenshot_edit_chip);
+
mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
mScreenshotSelectorView = mScreenshotLayout.findViewById(R.id.global_screenshot_selector);
mScreenshotLayout.setFocusable(true);
@@ -567,12 +592,30 @@
.build();
final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
SurfaceControl.captureDisplay(captureArgs);
- final Bitmap screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
+ Bitmap screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
+
+ if (screenshot == null) {
+ Log.e(TAG, "Screenshot bitmap was null");
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ finisher.accept(null);
+ mOnCompleteRunnable.run();
+ return;
+ }
+
saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, true);
}
private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
Insets screenInsets, boolean showFlash) {
+ if (mAccessibilityManager.isEnabled()) {
+ AccessibilityEvent event =
+ new AccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ event.setContentDescription(
+ mContext.getResources().getString(R.string.screenshot_saving_title));
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+
if (mScreenshotLayout.isAttachedToWindow()) {
// if we didn't already dismiss for another reason
if (mDismissAnimation == null || !mDismissAnimation.isRunning()) {
@@ -583,14 +626,6 @@
mScreenBitmap = screenshot;
- if (mScreenBitmap == null) {
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- finisher.accept(null);
- mOnCompleteRunnable.run();
- return;
- }
-
if (!isUserSetupComplete()) {
// User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
// and sharing shouldn't be exposed to the user.
@@ -632,7 +667,7 @@
if (imageData.uri == null) {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
+ R.string.screenshot_failed_to_save_text);
} else {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
@@ -735,11 +770,11 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- createScreenshotActionsShadeAnimation(imageData).start();
+ setChipIntents(imageData);
}
});
} else {
- createScreenshotActionsShadeAnimation(imageData).start();
+ setChipIntents(imageData);
}
});
}
@@ -752,7 +787,7 @@
if (imageData.uri == null) {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
+ R.string.screenshot_failed_to_save_text);
} else {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
}
@@ -874,19 +909,14 @@
mScreenshotAnimatedView.setVisibility(View.GONE);
mScreenshotPreview.setVisibility(View.VISIBLE);
mScreenshotLayout.forceLayout();
+ createScreenshotActionsShadeAnimation().start();
}
});
return dropInAnimation;
}
- private ValueAnimator createScreenshotActionsShadeAnimation(SavedImageData imageData) {
- LayoutInflater inflater = LayoutInflater.from(mContext);
- mActionsView.removeAllViews();
- mScreenshotLayout.invalidate();
- mScreenshotLayout.requestLayout();
- mScreenshotLayout.getViewTreeObserver().dispatchOnGlobalLayout();
-
+ private ValueAnimator createScreenshotActionsShadeAnimation() {
// By default the activities won't be able to start immediately; override this to keep
// the same behavior as if started from a notification
try {
@@ -896,61 +926,35 @@
ArrayList<ScreenshotActionChip> chips = new ArrayList<>();
- for (Notification.Action smartAction : imageData.smartActions) {
- ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
- R.layout.global_screenshot_action_chip, mActionsView, false);
- actionChip.setText(smartAction.title);
- actionChip.setIcon(smartAction.getIcon(), false);
- actionChip.setPendingIntent(smartAction.actionIntent,
- () -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED);
- dismissScreenshot("chip tapped", false);
- mOnCompleteRunnable.run();
- });
- mActionsView.addView(actionChip);
- chips.add(actionChip);
- }
-
- ScreenshotActionChip shareChip = (ScreenshotActionChip) inflater.inflate(
- R.layout.global_screenshot_action_chip, mActionsView, false);
- shareChip.setText(imageData.shareAction.title);
- shareChip.setIcon(imageData.shareAction.getIcon(), true);
- shareChip.setPendingIntent(imageData.shareAction.actionIntent, () -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED);
- dismissScreenshot("chip tapped", false);
- mOnCompleteRunnable.run();
+ mShareChip.setText(mContext.getString(com.android.internal.R.string.share));
+ mShareChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true);
+ mShareChip.setOnClickListener(v -> {
+ mShareChip.setIsPending(true);
+ mEditChip.setIsPending(false);
+ mPendingInteraction = PendingInteraction.SHARE;
});
- mActionsView.addView(shareChip);
- chips.add(shareChip);
+ chips.add(mShareChip);
- ScreenshotActionChip editChip = (ScreenshotActionChip) inflater.inflate(
- R.layout.global_screenshot_action_chip, mActionsView, false);
- editChip.setText(imageData.editAction.title);
- editChip.setIcon(imageData.editAction.getIcon(), true);
- editChip.setPendingIntent(imageData.editAction.actionIntent, () -> {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED);
- dismissScreenshot("chip tapped", false);
- mOnCompleteRunnable.run();
+ mEditChip.setText(mContext.getString(com.android.internal.R.string.screenshot_edit));
+ mEditChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), true);
+ mEditChip.setOnClickListener(v -> {
+ mEditChip.setIsPending(true);
+ mShareChip.setIsPending(false);
+ mPendingInteraction = PendingInteraction.EDIT;
});
- mActionsView.addView(editChip);
- chips.add(editChip);
+ chips.add(mEditChip);
mScreenshotPreview.setOnClickListener(v -> {
- try {
- imageData.editAction.actionIntent.send();
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED);
- dismissScreenshot("screenshot preview tapped", false);
- mOnCompleteRunnable.run();
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Intent cancelled", e);
- }
+ mShareChip.setIsPending(false);
+ mEditChip.setIsPending(false);
+ mPendingInteraction = PendingInteraction.PREVIEW;
});
- mScreenshotPreview.setContentDescription(imageData.editAction.title);
// remove the margin from the last chip so that it's correctly aligned with the end
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)
- mActionsView.getChildAt(mActionsView.getChildCount() - 1).getLayoutParams();
- params.setMarginEnd(0);
+ mActionsView.getChildAt(0).getLayoutParams();
+ params.setMarginStart(0);
+ mActionsView.getChildAt(0).setLayoutParams(params);
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS);
@@ -983,6 +987,63 @@
return animator;
}
+ private void setChipIntents(SavedImageData imageData) {
+ mShareChip.setPendingIntent(imageData.shareAction.actionIntent, () -> {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED);
+ dismissScreenshot("chip tapped", false);
+ mOnCompleteRunnable.run();
+ });
+
+ mEditChip.setPendingIntent(imageData.editAction.actionIntent, () -> {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED);
+ dismissScreenshot("chip tapped", false);
+ mOnCompleteRunnable.run();
+ });
+
+ mScreenshotPreview.setOnClickListener(v -> {
+ try {
+ imageData.editAction.actionIntent.send();
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED);
+ dismissScreenshot("screenshot preview tapped", false);
+ mOnCompleteRunnable.run();
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Intent cancelled", e);
+ }
+ });
+
+ if (mPendingInteraction != null) {
+ switch(mPendingInteraction) {
+ case PREVIEW:
+ mScreenshotPreview.callOnClick();
+ break;
+ case SHARE:
+ mShareChip.callOnClick();
+ break;
+ case EDIT:
+ mEditChip.callOnClick();
+ break;
+ }
+ } else {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+
+ for (Notification.Action smartAction : imageData.smartActions) {
+ ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
+ R.layout.global_screenshot_action_chip, mActionsView, false);
+ actionChip.setText(smartAction.title);
+ actionChip.setIcon(smartAction.getIcon(), false);
+ actionChip.setPendingIntent(smartAction.actionIntent,
+ () -> {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED);
+ dismissScreenshot("chip tapped", false);
+ mOnCompleteRunnable.run();
+ });
+ actionChip.setAlpha(1);
+ mActionsView.addView(actionChip);
+ mSmartChips.add(actionChip);
+ }
+ }
+ }
+
private AnimatorSet createScreenshotDismissAnimation() {
ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
alphaAnim.setStartDelay(SCREENSHOT_DISMISS_ALPHA_OFFSET_MS);
@@ -1025,9 +1086,16 @@
mDismissButton.setVisibility(View.GONE);
mScreenshotPreview.setVisibility(View.GONE);
mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
- mScreenshotPreview.setContentDescription(
- mContext.getResources().getString(R.string.screenshot_preview_description));
mScreenshotPreview.setOnClickListener(null);
+ mShareChip.setOnClickListener(null);
+ mEditChip.setOnClickListener(null);
+ mShareChip.setIsPending(false);
+ mEditChip.setIsPending(false);
+ mPendingInteraction = null;
+ for (ScreenshotActionChip chip : mSmartChips) {
+ mActionsView.removeView(chip);
+ }
+ mSmartChips.clear();
mScreenshotLayout.setAlpha(1);
mDismissButton.setTranslationY(0);
mActionsContainer.setTranslationY(0);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index df1d789..f0ea597 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -217,13 +217,11 @@
mParams.mActionsReadyListener.onActionsReady(mImageData);
mParams.finisher.accept(mImageData.uri);
mParams.image = null;
- mParams.errorMsgResId = 0;
} catch (Exception e) {
// IOException/UnsupportedOperationException may be thrown if external storage is
// not mounted
Slog.e(TAG, "unable to save screenshot", e);
mParams.clearImage();
- mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
mImageData.reset();
mParams.mActionsReadyListener.onActionsReady(mImageData);
mParams.finisher.accept(null);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
index a488702..3370946 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
@@ -16,7 +16,6 @@
package com.android.systemui.screenshot;
-import android.annotation.ColorInt;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.drawable.Icon;
@@ -35,9 +34,9 @@
private static final String TAG = "ScreenshotActionChip";
- private ImageView mIcon;
- private TextView mText;
- private @ColorInt int mIconColor;
+ private ImageView mIconView;
+ private TextView mTextView;
+ private boolean mIsPending = false;
public ScreenshotActionChip(Context context) {
this(context, null);
@@ -54,25 +53,29 @@
public ScreenshotActionChip(
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
-
- mIconColor = context.getColor(R.color.global_screenshot_button_icon);
}
@Override
protected void onFinishInflate() {
- mIcon = findViewById(R.id.screenshot_action_chip_icon);
- mText = findViewById(R.id.screenshot_action_chip_text);
+ mIconView = findViewById(R.id.screenshot_action_chip_icon);
+ mTextView = findViewById(R.id.screenshot_action_chip_text);
+ }
+
+ @Override
+ public void setPressed(boolean pressed) {
+ // override pressed state to true if there is an action pending
+ super.setPressed(mIsPending || pressed);
}
void setIcon(Icon icon, boolean tint) {
- mIcon.setImageIcon(icon);
+ mIconView.setImageIcon(icon);
if (!tint) {
- mIcon.setImageTintList(null);
+ mIconView.setImageTintList(null);
}
}
void setText(CharSequence text) {
- mText.setText(text);
+ mTextView.setText(text);
}
void setPendingIntent(PendingIntent intent, Runnable finisher) {
@@ -85,4 +88,9 @@
}
});
}
+
+ void setIsPending(boolean isPending) {
+ mIsPending = isPending;
+ setPressed(mIsPending);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 0184fa7..dcee9fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -140,6 +140,8 @@
private static final int MSG_SUPPRESS_AMBIENT_DISPLAY = 56 << MSG_SHIFT;
private static final int MSG_REQUEST_WINDOW_MAGNIFICATION_CONNECTION = 57 << MSG_SHIFT;
private static final int MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND = 58 << MSG_SHIFT;
+ //TODO(b/169175022) Update name and when feature name is locked.
+ private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE = 59 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -258,6 +260,11 @@
default void showAssistDisclosure() { }
default void startAssist(Bundle args) { }
default void onCameraLaunchGestureDetected(int source) { }
+
+ /**
+ * Notifies SysUI that the emergency action gesture was detected.
+ */
+ default void onEmergencyActionLaunchGestureDetected() { }
default void showPictureInPictureMenu() { }
default void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) { }
@@ -730,6 +737,14 @@
}
@Override
+ public void onEmergencyActionLaunchGestureDetected() {
+ synchronized (mLock) {
+ mHandler.removeMessages(MSG_EMERGENCY_ACTION_LAUNCH_GESTURE);
+ mHandler.obtainMessage(MSG_EMERGENCY_ACTION_LAUNCH_GESTURE).sendToTarget();
+ }
+ }
+
+ @Override
public void addQsTile(ComponentName tile) {
synchronized (mLock) {
mHandler.obtainMessage(MSG_ADD_QS_TILE, tile).sendToTarget();
@@ -1186,6 +1201,10 @@
mCallbacks.get(i).onCameraLaunchGestureDetected(msg.arg1);
}
break;
+ case MSG_EMERGENCY_ACTION_LAUNCH_GESTURE:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onEmergencyActionLaunchGestureDetected();
+ }
case MSG_SHOW_PICTURE_IN_PICTURE_MENU:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).showPictureInPictureMenu();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
index ac3523b..1b1a51b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar;
import android.content.Context;
-import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
@@ -36,10 +35,9 @@
import com.android.settingslib.media.InfoMediaManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
-import com.android.settingslib.media.MediaOutputSliceConstants;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.Dependency;
-import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -51,7 +49,7 @@
*/
public class MediaTransferManager {
private final Context mContext;
- private final ActivityStarter mActivityStarter;
+ private final MediaOutputDialogFactory mMediaOutputDialogFactory;
private MediaDevice mDevice;
private List<View> mViews = new ArrayList<>();
private LocalMediaManager mLocalMediaManager;
@@ -74,12 +72,7 @@
ViewParent parent = view.getParent();
StatusBarNotification statusBarNotification =
getRowForParent(parent).getEntry().getSbn();
- final Intent intent = new Intent()
- .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
- .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
- statusBarNotification.getPackageName());
- mActivityStarter.startActivity(intent, false, true /* dismissShade */,
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ mMediaOutputDialogFactory.create(statusBarNotification.getPackageName(), true);
return true;
}
};
@@ -107,7 +100,7 @@
public MediaTransferManager(Context context) {
mContext = context;
- mActivityStarter = Dependency.get(ActivityStarter.class);
+ mMediaOutputDialogFactory = Dependency.get(MediaOutputDialogFactory.class);
LocalBluetoothManager lbm = Dependency.get(LocalBluetoothManager.class);
InfoMediaManager imm = new InfoMediaManager(mContext, null, null, lbm);
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 433c8b0..ddfa18e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -27,10 +27,10 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationContentView
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
@@ -85,38 +85,43 @@
for (entry in activeConversationEntries) {
if (rankingMap.getRanking(entry.sbn.key, ranking) && ranking.isConversation) {
val important = ranking.channel.isImportantConversation
- val layouts = entry.row?.layouts?.asSequence()
+ var changed = false
+ entry.row?.layouts?.asSequence()
?.flatMap(::getLayouts)
?.mapNotNull { it as? ConversationLayout }
- ?: emptySequence()
- var changed = false
- for (layout in layouts) {
- if (important == layout.isImportantConversation) {
- continue
- }
- changed = true
- if (important && entry.isMarkedForUserTriggeredMovement) {
- // delay this so that it doesn't animate in until after
- // the notif has been moved in the shade
- mainHandler.postDelayed({
- layout.setIsImportantConversation(
- important, true /* animate */)
- }, IMPORTANCE_ANIMATION_DELAY.toLong())
- } else {
- layout.setIsImportantConversation(important)
- }
- }
+ ?.filterNot { it.isImportantConversation == important }
+ ?.forEach { layout ->
+ changed = true
+ if (important && entry.isMarkedForUserTriggeredMovement) {
+ // delay this so that it doesn't animate in until after
+ // the notif has been moved in the shade
+ mainHandler.postDelayed(
+ {
+ layout.setIsImportantConversation(
+ important,
+ true)
+ },
+ IMPORTANCE_ANIMATION_DELAY.toLong())
+ } else {
+ layout.setIsImportantConversation(important, false)
+ }
+ }
if (changed) {
notificationGroupManager.updateIsolation(entry)
+ // ensure that the conversation icon isn't hidden
+ // (ex: if it was showing in the shelf)
+ entry.row?.updateIconVisibilities()
}
}
}
}
override fun onEntryInflated(entry: NotificationEntry) {
- if (!entry.ranking.isConversation) return
+ if (!entry.ranking.isConversation) {
+ return
+ }
fun updateCount(isExpanded: Boolean) {
- if (isExpanded && (!notifPanelCollapsed || entry.isPinnedAndExpanded())) {
+ if (isExpanded && (!notifPanelCollapsed || entry.isPinnedAndExpanded)) {
resetCount(entry.key)
entry.row?.let(::resetBadgeUi)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index e1e77b0..7c3b791 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -618,6 +618,7 @@
NotificationEntry entry = mPendingNotifications.get(key);
if (entry != null) {
entry.setSbn(notification);
+ entry.setRanking(ranking);
} else {
entry = new NotificationEntry(
notification,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 0e7a558..789e78e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -329,11 +329,11 @@
return mRanking.canBubble();
}
- public @Nullable List<Notification.Action> getSmartActions() {
+ public @NonNull List<Notification.Action> getSmartActions() {
return mRanking.getSmartActions();
}
- public @Nullable List<CharSequence> getSmartReplies() {
+ public @NonNull List<CharSequence> getSmartReplies() {
return mRanking.getSmartReplies();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 133ddfe..6da4d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -41,8 +41,6 @@
*/
@SysUISingleton
public class RankingCoordinator implements Coordinator {
- private static final String TAG = "RankingNotificationCoordinator";
-
private final StatusBarStateController mStatusBarStateController;
private final HighPriorityProvider mHighPriorityProvider;
private final NodeController mSilentHeaderController;
@@ -65,7 +63,7 @@
mStatusBarStateController.addCallback(mStatusBarStateCallback);
pipeline.addPreGroupFilter(mSuspendedFilter);
- pipeline.addPreGroupFilter(mDozingFilter);
+ pipeline.addPreGroupFilter(mDndVisualEffectsFilter);
}
public NotifSectioner getAlertingSectioner() {
@@ -114,10 +112,10 @@
}
};
- private final NotifFilter mDozingFilter = new NotifFilter("IsDozingFilter") {
+ private final NotifFilter mDndVisualEffectsFilter = new NotifFilter(
+ "DndSuppressingVisualEffects") {
@Override
public boolean shouldFilterOut(NotificationEntry entry, long now) {
- // Dozing + DND Settings from Ranking object
if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) {
return true;
}
@@ -130,7 +128,7 @@
new StatusBarStateController.StateListener() {
@Override
public void onDozingChanged(boolean isDozing) {
- mDozingFilter.invalidateList();
+ mDndVisualEffectsFilter.invalidateList();
}
};
}
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 f1727ec..094e866 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
@@ -124,6 +124,7 @@
private float mAppearAnimationTranslation;
private int mNormalColor;
private boolean mIsBelowSpeedBump;
+ private long mLastActionUpTime;
private float mNormalBackgroundVisibilityAmount;
private float mDimmedBackgroundFadeInAmount = -1;
@@ -225,6 +226,22 @@
return super.onInterceptTouchEvent(ev);
}
+ /** Sets the last action up time this view was touched. */
+ void setLastActionUpTime(long eventTime) {
+ mLastActionUpTime = eventTime;
+ }
+
+ /**
+ * Returns the last action up time. The last time will also be cleared because the source of
+ * action is not only from touch event. That prevents the caller from utilizing the time with
+ * unrelated event. The time can be 0 if the event is unavailable.
+ */
+ public long getAndResetLastActionUpTime() {
+ long lastActionUpTime = mLastActionUpTime;
+ mLastActionUpTime = 0;
+ return lastActionUpTime;
+ }
+
protected boolean disallowSingleClick(MotionEvent ev) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index dd30c89..41ce51c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.row;
+import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
@@ -92,6 +93,9 @@
mBlockNextTouch = false;
return true;
}
+ if (ev.getAction() == MotionEvent.ACTION_UP) {
+ mView.setLastActionUpTime(SystemClock.uptimeMillis());
+ }
if (mNeedsDimming && !mAccessibilityManager.isTouchExplorationEnabled()
&& mView.isInteractive()) {
if (mNeedsDimming && !mView.isDimmed()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 113c115..ba88f62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -497,7 +497,7 @@
/**
* Returns whether this row is considered non-blockable (i.e. it's a non-blockable system notif
- * or is in a whitelist).
+ * or is in an allowList).
*/
public boolean getIsNonblockable() {
// If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once
@@ -1478,8 +1478,9 @@
}
}
- private void updateIconVisibilities() {
- // The shelficon is never hidden for children in groups
+ /** Refreshes the visibility of notification icons */
+ public void updateIconVisibilities() {
+ // The shelf icon is never hidden for children in groups
boolean visible = !isChildInGroup() && mShelfIconVisible;
for (NotificationContentView l : mLayouts) {
l.setShelfIconVisible(visible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index fe70c81..17f326b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -147,6 +147,8 @@
// hiding the conversationIcon will already do that via its listener.
return
}
+ } else {
+ conversationIconView.isForceHidden = false
}
super.setShelfIconVisible(visible)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index efb2469..a5667bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -456,6 +456,7 @@
return;
}
mSuppressed = suppressed;
+ mDozeLog.traceDozingSuppressed(mSuppressed);
for (Callback callback : mCallbacks) {
callback.onDozeSuppressedChanged(suppressed);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index a3f14ba..ef4108b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -23,6 +23,7 @@
import android.util.MathUtils;
import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -122,6 +123,8 @@
*/
private int mUnlockedStackScrollerPadding;
+ private int mLockScreenMode;
+
/**
* Refreshes the dimension values.
*/
@@ -171,6 +174,13 @@
result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
}
+ /**
+ * Update lock screen mode for testing different layouts
+ */
+ public void onLockScreenModeChanged(int mode) {
+ mLockScreenMode = mode;
+ }
+
public float getMinStackScrollerPadding() {
return mBypassEnabled ? mUnlockedStackScrollerPadding
: mMinTopMargin + mKeyguardStatusHeight + mClockNotificationsMargin;
@@ -185,6 +195,9 @@
}
private int getExpandedPreferredClockY() {
+ if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+ return mMinTopMargin;
+ }
return (mHasCustomClock && (!mHasVisibleNotifs || mBypassEnabled)) ? getPreferredClockY()
: getExpandedClockPosition();
}
@@ -228,6 +241,13 @@
clockYDark = MathUtils.lerp(clockYBouncer, clockYDark, shadeExpansion);
float darkAmount = mBypassEnabled && !mHasCustomClock ? 1.0f : mDarkAmount;
+
+ // TODO(b/12836565) - prototyping only adjustment
+ if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+ // This will keep the clock at the top for AOD
+ darkAmount = 0f;
+ }
+
return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mEmptyDragAmount);
}
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 3f636ff..47b16c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -70,6 +70,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardClockSwitchController;
import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
@@ -202,9 +203,6 @@
private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
private static final Rect EMPTY_RECT = new Rect();
- private static final AnimationProperties
- CLOCK_ANIMATION_PROPERTIES =
- new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
private final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT = AnimatableProperty.from(
"KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
(notificationPanelView, aFloat) -> setKeyguardHeadsUpShowingAmount(aFloat),
@@ -221,6 +219,11 @@
new KeyguardUpdateMonitorCallback() {
@Override
+ public void onLockScreenModeChanged(int mode) {
+ mClockPositionAlgorithm.onLockScreenModeChanged(mode);
+ }
+
+ @Override
public void onBiometricAuthenticated(int userId,
BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
@@ -275,7 +278,7 @@
private ViewGroup mBigClockContainer;
private QS mQs;
private FrameLayout mQsFrame;
- private KeyguardStatusView mKeyguardStatusView;
+ private KeyguardStatusViewController mKeyguardStatusViewController;
private View mQsNavbarScrim;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
private boolean mAnimateNextPositionUpdate;
@@ -339,7 +342,6 @@
private boolean mIsLaunchTransitionRunning;
private Runnable mLaunchAnimationEndRunnable;
private boolean mOnlyAffordanceInThisMotion;
- private boolean mKeyguardStatusViewAnimating;
private ValueAnimator mQsSizeChangeAnimator;
private boolean mQsScrimEnabled = true;
@@ -601,16 +603,8 @@
private void onFinishInflate() {
loadDimens();
mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
- mKeyguardStatusView = mView.findViewById(R.id.keyguard_status_view);
-
- KeyguardClockSwitchController keyguardClockSwitchController =
- mKeyguardStatusViewComponentFactory
- .build(mKeyguardStatusView)
- .getKeyguardClockSwitchController();
- keyguardClockSwitchController.init();
mBigClockContainer = mView.findViewById(R.id.big_clock_container);
- keyguardClockSwitchController.setBigClockContainer(mBigClockContainer);
-
+ updateViewControllers(mView.findViewById(R.id.keyguard_status_view));
mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
R.id.notification_stack_scroller);
@@ -684,11 +678,24 @@
R.dimen.heads_up_status_bar_padding);
}
+ private void updateViewControllers(KeyguardStatusView keyguardStatusView) {
+ // Re-associate the KeyguardStatusViewController
+ KeyguardStatusViewComponent statusViewComponent =
+ mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
+ mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
+ mKeyguardStatusViewController.init();
+
+ // Re-associate the clock container with the keyguard clock switch.
+ KeyguardClockSwitchController keyguardClockSwitchController =
+ statusViewComponent.getKeyguardClockSwitchController();
+ keyguardClockSwitchController.setBigClockContainer(mBigClockContainer);
+ }
+
/**
* Returns if there's a custom clock being presented.
*/
public boolean hasCustomClock() {
- return mKeyguardStatusView.hasCustomClock();
+ return mKeyguardStatusViewController.hasCustomClock();
}
private void setStatusBar(StatusBar bar) {
@@ -725,21 +732,16 @@
private void reInflateViews() {
// Re-inflate the status view group.
- int index = mView.indexOfChild(mKeyguardStatusView);
- mView.removeView(mKeyguardStatusView);
- mKeyguardStatusView = (KeyguardStatusView) mInjectionInflationController.injectable(
+ KeyguardStatusView keyguardStatusView = mView.findViewById(R.id.keyguard_status_view);
+ int index = mView.indexOfChild(keyguardStatusView);
+ mView.removeView(keyguardStatusView);
+ keyguardStatusView = (KeyguardStatusView) mInjectionInflationController.injectable(
LayoutInflater.from(mView.getContext())).inflate(
R.layout.keyguard_status_view, mView, false);
- mView.addView(mKeyguardStatusView, index);
+ mView.addView(keyguardStatusView, index);
- // Re-associate the clock container with the keyguard clock switch.
mBigClockContainer.removeAllViews();
- KeyguardClockSwitchController keyguardClockSwitchController =
- mKeyguardStatusViewComponentFactory
- .build(mKeyguardStatusView)
- .getKeyguardClockSwitchController();
- keyguardClockSwitchController.init();
- keyguardClockSwitchController.setBigClockContainer(mBigClockContainer);
+ updateViewControllers(keyguardStatusView);
// Update keyguard bottom area
index = mView.indexOfChild(mKeyguardBottomArea);
@@ -759,7 +761,11 @@
mKeyguardStatusBar.onThemeChanged();
}
- setKeyguardStatusViewVisibility(mBarState, false, false);
+ mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+ mBarState,
+ false,
+ false,
+ mBarState);
setKeyguardBottomAreaVisibility(mBarState, false);
if (mOnReinflationListener != null) {
mOnReinflationListener.run();
@@ -853,23 +859,23 @@
} else {
int totalHeight = mView.getHeight();
int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
- int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight);
+ int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight);
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
final boolean hasVisibleNotifications = !bypassEnabled
&& mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0;
- mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications);
+ mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications);
mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding,
mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
getExpandedFraction(),
- totalHeight, (int) (mKeyguardStatusView.getHeight() - mShelfHeight / 2.0f
- - mDarkIconSize / 2.0f), clockPreferredY, hasCustomClock(),
+ totalHeight,
+ (int) (mKeyguardStatusViewController.getHeight()
+ - mShelfHeight / 2.0f - mDarkIconSize / 2.0f),
+ clockPreferredY, hasCustomClock(),
hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
bypassEnabled, getUnlockedStackScrollerPadding());
mClockPositionAlgorithm.run(mClockPositionResult);
- PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
- mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
- PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
- mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
+ mKeyguardStatusViewController.updatePosition(
+ mClockPositionResult.clockX, mClockPositionResult.clockY, animateClock);
updateNotificationTranslucency();
updateClock();
stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
@@ -905,7 +911,7 @@
float availableSpace =
mNotificationStackScrollLayoutController.getHeight() - minPadding - shelfSize
- Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding)
- - mKeyguardStatusView.getLogoutButtonHeight();
+ - mKeyguardStatusViewController.getLogoutButtonHeight();
int count = 0;
ExpandableView previousView = null;
for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) {
@@ -1000,9 +1006,7 @@
}
private void updateClock() {
- if (!mKeyguardStatusViewAnimating) {
- mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha);
- }
+ mKeyguardStatusViewController.setAlpha(mClockPositionResult.clockAlpha);
}
public void animateToFullShade(long delay) {
@@ -1600,29 +1604,6 @@
}
}
- private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() {
- @Override
- public void run() {
- mKeyguardStatusViewAnimating = false;
- mKeyguardStatusView.setVisibility(View.INVISIBLE);
- }
- };
-
- private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() {
- @Override
- public void run() {
- mKeyguardStatusViewAnimating = false;
- mKeyguardStatusView.setVisibility(View.GONE);
- }
- };
-
- private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = new Runnable() {
- @Override
- public void run() {
- mKeyguardStatusViewAnimating = false;
- }
- };
-
private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
@Override
public void run() {
@@ -1700,46 +1681,6 @@
}
}
- private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway,
- boolean goingToFullShade) {
- mKeyguardStatusView.animate().cancel();
- mKeyguardStatusViewAnimating = false;
- if ((!keyguardFadingAway && mBarState == KEYGUARD
- && statusBarState != KEYGUARD) || goingToFullShade) {
- mKeyguardStatusViewAnimating = true;
- mKeyguardStatusView.animate().alpha(0f).setStartDelay(0).setDuration(
- 160).setInterpolator(Interpolators.ALPHA_OUT).withEndAction(
- mAnimateKeyguardStatusViewGoneEndRunnable);
- if (keyguardFadingAway) {
- mKeyguardStatusView.animate().setStartDelay(
- mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration(
- mKeyguardStateController.getShortenedFadingAwayDuration()).start();
- }
- } else if (mBarState == StatusBarState.SHADE_LOCKED
- && statusBarState == KEYGUARD) {
- mKeyguardStatusView.setVisibility(View.VISIBLE);
- mKeyguardStatusViewAnimating = true;
- mKeyguardStatusView.setAlpha(0f);
- mKeyguardStatusView.animate().alpha(1f).setStartDelay(0).setDuration(
- 320).setInterpolator(Interpolators.ALPHA_IN).withEndAction(
- mAnimateKeyguardStatusViewVisibleEndRunnable);
- } else if (statusBarState == KEYGUARD) {
- if (keyguardFadingAway) {
- mKeyguardStatusViewAnimating = true;
- mKeyguardStatusView.animate().alpha(0).translationYBy(
- -getHeight() * 0.05f).setInterpolator(
- Interpolators.FAST_OUT_LINEAR_IN).setDuration(125).setStartDelay(
- 0).withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable).start();
- } else {
- mKeyguardStatusView.setVisibility(View.VISIBLE);
- mKeyguardStatusView.setAlpha(1f);
- }
- } else {
- mKeyguardStatusView.setVisibility(View.GONE);
- mKeyguardStatusView.setAlpha(1f);
- }
- }
-
private void updateQsState() {
mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded);
mNotificationStackScrollLayoutController.setScrollingEnabled(
@@ -2070,7 +2011,7 @@
private int getMaxPanelHeightBypass() {
int position =
mClockPositionAlgorithm.getExpandedClockPosition()
- + mKeyguardStatusView.getHeight();
+ + mKeyguardStatusViewController.getHeight();
if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0) {
position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f;
}
@@ -2151,7 +2092,7 @@
int
minKeyguardPanelBottom =
mClockPositionAlgorithm.getExpandedClockPosition()
- + mKeyguardStatusView.getHeight()
+ + mKeyguardStatusViewController.getHeight()
+ mNotificationStackScrollLayoutController.getIntrinsicContentHeight();
return Math.max(maxHeight, minKeyguardPanelBottom);
} else {
@@ -2599,7 +2540,7 @@
}
public void onScreenTurningOn() {
- mKeyguardStatusView.dozeTimeTick();
+ mKeyguardStatusViewController.dozeTimeTick();
}
@Override
@@ -2984,7 +2925,6 @@
mAnimateNextPositionUpdate = false;
}
mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse);
- mKeyguardStatusView.setPulsing(pulsing);
}
public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
@@ -2996,14 +2936,14 @@
public void dozeTimeTick() {
mKeyguardBottomArea.dozeTimeTick();
- mKeyguardStatusView.dozeTimeTick();
+ mKeyguardStatusViewController.dozeTimeTick();
if (mInterpolatedDarkAmount > 0) {
positionClockAndNotifications();
}
}
public void setStatusAccessibilityImportance(int mode) {
- mKeyguardStatusView.setImportantForAccessibility(mode);
+ mKeyguardStatusViewController.setStatusAccessibilityImportance(mode);
}
/**
@@ -3063,8 +3003,11 @@
* security view of the bouncer.
*/
public void onBouncerPreHideAnimation() {
- setKeyguardStatusViewVisibility(mBarState, true /* keyguardFadingAway */,
- false /* goingToFullShade */);
+ mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+ mBarState,
+ true /* keyguardFadingAway */,
+ false /* goingToFullShade */,
+ mBarState);
}
/**
@@ -3634,7 +3577,11 @@
int oldState = mBarState;
boolean keyguardShowing = statusBarState == KEYGUARD;
- setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade);
+ mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+ statusBarState,
+ keyguardFadingAway,
+ goingToFullShade,
+ mBarState);
setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
mBarState = statusBarState;
@@ -3685,7 +3632,7 @@
public void onDozeAmountChanged(float linearAmount, float amount) {
mInterpolatedDarkAmount = amount;
mLinearDarkAmount = linearAmount;
- mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount);
+ mKeyguardStatusViewController.setDarkAmount(mInterpolatedDarkAmount);
mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
positionClockAndNotifications();
}
@@ -3731,9 +3678,10 @@
setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth());
// Update Clock Pivot
- mKeyguardStatusView.setPivotX(mView.getWidth() / 2);
- mKeyguardStatusView.setPivotY(
- (FONT_HEIGHT - CAP_HEIGHT) / 2048f * mKeyguardStatusView.getClockTextSize());
+ mKeyguardStatusViewController.setPivotX(mView.getWidth() / 2);
+ mKeyguardStatusViewController.setPivotY(
+ (FONT_HEIGHT - CAP_HEIGHT) / 2048f
+ * mKeyguardStatusViewController.getClockTextSize());
// Calculate quick setting heights.
int oldMaxHeight = mQsMaxExpansionHeight;
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 e7c29b6..88a387d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -69,6 +69,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.PointF;
@@ -152,6 +153,7 @@
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.emergency.EmergencyGesture;
import com.android.systemui.fragments.ExtensionFragmentListener;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -819,7 +821,7 @@
updateScrimController();
};
-
+ mActivityIntentHelper = new ActivityIntentHelper(mContext);
DateTimeView.setReceiverHandler(timeTickHandler);
}
@@ -833,8 +835,6 @@
mBubblesOptional.get().setExpandListener(mBubbleExpandListener);
}
- mActivityIntentHelper = new ActivityIntentHelper(mContext);
-
mColorExtractor.addOnColorsChangedListener(this);
mStatusBarStateController.addCallback(this,
SysuiStatusBarStateController.RANK_STATUS_BAR);
@@ -3539,8 +3539,6 @@
if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
if (mNotificationPanelViewController.canPanelBeCollapsed()) {
mShadeController.animateCollapsePanels();
- } else if (mBubblesOptional.isPresent()) {
- mBubblesOptional.get().performBackPressIfNeeded();
}
return true;
}
@@ -3982,6 +3980,27 @@
}
}
+ @Override
+ public void onEmergencyActionLaunchGestureDetected() {
+ // TODO (b/169793384) Polish the panic gesture to be just like its older brother, camera.
+ Intent emergencyIntent = new Intent(EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
+ PackageManager pm = mContext.getPackageManager();
+ ResolveInfo resolveInfo = pm.resolveActivity(emergencyIntent, /*flags=*/0);
+ if (resolveInfo == null) {
+ Log.wtf(TAG, "Couldn't find an app to process the emergency intent.");
+ return;
+ }
+
+ if (mVibrator != null && mVibrator.hasVibrator()) {
+ mVibrator.vibrate(500L);
+ }
+
+ emergencyIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name));
+ emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(emergencyIntent, /*dismissShade=*/true);
+ }
+
boolean isCameraAllowedByAdmin() {
if (mDevicePolicyManager.getCameraDisabled(null,
mLockscreenUserManager.getCurrentUserId())) {
@@ -4284,6 +4303,19 @@
}
public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) {
+ return getDefaultActivityOptions(animationAdapter).toBundle();
+ }
+
+ public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter,
+ boolean isKeyguardShowing, long eventTime) {
+ ActivityOptions options = getDefaultActivityOptions(animationAdapter);
+ options.setSourceInfo(isKeyguardShowing ? ActivityOptions.SourceInfo.TYPE_LOCKSCREEN
+ : ActivityOptions.SourceInfo.TYPE_NOTIFICATION, eventTime);
+ return options.toBundle();
+ }
+
+ public static ActivityOptions getDefaultActivityOptions(
+ @Nullable RemoteAnimationAdapter animationAdapter) {
ActivityOptions options;
if (animationAdapter != null) {
options = ActivityOptions.makeRemoteAnimation(animationAdapter);
@@ -4293,7 +4325,7 @@
// 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.toBundle();
+ return options;
}
void visibilityChanged(boolean visible) {
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 aa01642..256ee20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -30,6 +30,7 @@
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -40,7 +41,6 @@
import android.text.TextUtils;
import android.util.EventLog;
import android.view.RemoteAnimationAdapter;
-import android.view.View;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.NotificationVisibility;
@@ -402,7 +402,7 @@
PendingIntent intent,
Intent fillInIntent,
NotificationEntry entry,
- View row,
+ ExpandableNotificationRow row,
boolean wasOccluded,
boolean isActivityIntent) {
RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(row,
@@ -414,8 +414,11 @@
.registerRemoteAnimationForNextActivityStart(
intent.getCreatorPackage(), adapter);
}
+ long eventTime = row.getAndResetLastActionUpTime();
+ Bundle options = eventTime > 0 ? getActivityOptions(adapter,
+ mKeyguardStateController.isShowing(), eventTime) : getActivityOptions(adapter);
int launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
- null, null, getActivityOptions(adapter));
+ null, null, options);
mMainThreadHandler.post(() -> {
mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt
index 5ec57e2..6a3a69c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt
@@ -204,32 +204,29 @@
}
// Apps didn't provide any smart replies / actions, use those from NAS (if any).
if (smartReplies == null && smartActions == null) {
- smartReplies = entry.smartReplies
- ?.takeIf { it.isNotEmpty() }
- ?.let { entryReplies -> freeformRemoteInputActionPair
- ?.takeIf {
- it.second.allowGeneratedReplies && it.second.actionIntent != null
- }?.let { freeformPair -> SmartReplies(
- entryReplies,
- freeformPair.first,
- freeformPair.second.actionIntent,
- true /* fromAssistant */)
- }
- }
- smartActions = entry.smartActions
- ?.takeIf {
- it.isNotEmpty() && notification.allowSystemGeneratedContextualActions
- }?.let { entryActions ->
- val systemGeneratedActions: List<Notification.Action> = when {
- activityManagerWrapper.isLockTaskKioskModeActive ->
- // Filter actions if we're in kiosk-mode - we don't care about
- // screen pinning mode, since notifications aren't shown there
- // anyway.
- filterAllowlistedLockTaskApps(entryActions)
- else -> entryActions
- }
- SmartActions(systemGeneratedActions, true /* fromAssistant */)
- }
+ val entryReplies = entry.smartReplies
+ val entryActions = entry.smartActions
+ if (entryReplies.isNotEmpty()
+ && freeformRemoteInputActionPair != null
+ && freeformRemoteInputActionPair.second.allowGeneratedReplies
+ && freeformRemoteInputActionPair.second.actionIntent != null) {
+ smartReplies = SmartReplies(
+ entryReplies,
+ freeformRemoteInputActionPair.first,
+ freeformRemoteInputActionPair.second.actionIntent,
+ true /* fromAssistant */)
+ }
+ if (entryActions.isNotEmpty()
+ && notification.allowSystemGeneratedContextualActions) {
+ val systemGeneratedActions: List<Notification.Action> = when {
+ activityManagerWrapper.isLockTaskKioskModeActive ->
+ // Filter actions if we're in kiosk-mode - we don't care about screen
+ // pinning mode, since notifications aren't shown there anyway.
+ filterAllowlistedLockTaskApps(entryActions)
+ else -> entryActions
+ }
+ smartActions = SmartActions(systemGeneratedActions, true /* fromAssistant */)
+ }
}
return SmartRepliesAndActions(smartReplies, smartActions)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 2081cfe..735338f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -90,6 +90,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialog;
import com.android.systemui.plugins.VolumeDialogController;
@@ -519,6 +520,7 @@
Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
Intent intent = new Intent(Settings.Panel.ACTION_VOLUME);
dismissH(DISMISS_REASON_SETTINGS_CLICKED);
+ Dependency.get(MediaOutputDialogFactory.class).dismiss();
Dependency.get(ActivityStarter.class).startActivity(intent,
true /* dismissShade */);
});
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 247baf8..28343ed 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -26,6 +26,7 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipUiEventLogger;
@@ -45,20 +46,24 @@
*/
@Module
public abstract class TvPipModule {
-
@SysUISingleton
@Provides
- static Pip providePipController(Context context,
+ static Optional<Pip> providePip(
+ Context context,
PipBoundsHandler pipBoundsHandler,
PipTaskOrganizer pipTaskOrganizer,
WindowManagerShellWrapper windowManagerShellWrapper) {
- return new PipController(context, pipBoundsHandler, pipTaskOrganizer,
- windowManagerShellWrapper);
+ return Optional.of(
+ new PipController(
+ context,
+ pipBoundsHandler,
+ pipTaskOrganizer,
+ windowManagerShellWrapper));
}
@SysUISingleton
@Provides
- static PipControlsViewController providePipControlsViewContrller(
+ static PipControlsViewController providePipControlsViewController(
PipControlsView pipControlsView, PipController pipController,
LayoutInflater layoutInflater, Handler handler) {
return new PipControlsViewController(pipControlsView, pipController, layoutInflater,
@@ -86,12 +91,19 @@
@SysUISingleton
@Provides
+ static PipBoundsState providePipBoundsState() {
+ return new PipBoundsState();
+ }
+
+ @SysUISingleton
+ @Provides
static PipTaskOrganizer providePipTaskOrganizer(Context context,
+ PipBoundsState pipBoundsState,
PipBoundsHandler pipBoundsHandler,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
- return new PipTaskOrganizer(context, pipBoundsHandler,
+ return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler,
pipSurfaceTransactionHelper, splitScreenOptional, displayController,
pipUiEventLogger, shellTaskOrganizer);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index 56efffc..7e1a2e8 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -52,6 +52,8 @@
transactionPool);
}
+ @SysUISingleton
+ @Provides
static SplitScreen provideSplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController displayImeController, @Main Handler handler,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index a948103..3c4c3fc 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -55,9 +55,9 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -104,7 +104,7 @@
private final DisplayImeController mDisplayImeController;
private final InputConsumerController mInputConsumerController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final ActivityManagerWrapper mActivityManagerWrapper;
+ private final TaskStackChangeListeners mTaskStackChangeListeners;
private final NavigationModeController mNavigationModeController;
private final ScreenLifecycle mScreenLifecycle;
private final SysUiState mSysUiState;
@@ -125,7 +125,7 @@
ConfigurationController configurationController,
InputConsumerController inputConsumerController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- ActivityManagerWrapper activityManagerWrapper,
+ TaskStackChangeListeners taskStackChangeListeners,
DisplayImeController displayImeController,
NavigationModeController navigationModeController,
ScreenLifecycle screenLifecycle,
@@ -140,7 +140,7 @@
mConfigurationController = configurationController;
mInputConsumerController = inputConsumerController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mActivityManagerWrapper = activityManagerWrapper;
+ mTaskStackChangeListeners = taskStackChangeListeners;
mDisplayImeController = displayImeController;
mNavigationModeController = navigationModeController;
mScreenLifecycle = screenLifecycle;
@@ -167,9 +167,6 @@
@VisibleForTesting
void initPip(Pip pip) {
- if (!PipUtils.hasSystemFeature(mContext)) {
- return;
- }
mCommandQueue.addCallback(new CommandQueue.Callbacks() {
@Override
public void showPictureInPictureMenu() {
@@ -205,7 +202,7 @@
});
// Handle for system task stack changes.
- mActivityManagerWrapper.registerTaskStackListener(
+ mTaskStackChangeListeners.registerTaskStackListener(
new TaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
@@ -276,7 +273,7 @@
};
mKeyguardUpdateMonitor.registerCallback(mSplitScreenKeyguardCallback);
- mActivityManagerWrapper.registerTaskStackListener(
+ mTaskStackChangeListeners.registerTaskStackListener(
new TaskStackChangeListener() {
@Override
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
@@ -388,7 +385,7 @@
}
});
- mActivityManagerWrapper.registerTaskStackListener(
+ mTaskStackChangeListeners.registerTaskStackListener(
new TaskStackChangeListener() {
@Override
public void onTaskCreated(int taskId, ComponentName componentName) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 09678b5..ac6e5de 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -41,7 +41,6 @@
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedController;
-import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
@@ -117,7 +116,7 @@
@SysUISingleton
@Provides
- static PipSurfaceTransactionHelper providesPipSurfaceTransactionHelper(Context context) {
+ static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
return new PipSurfaceTransactionHelper(context);
}
@@ -159,9 +158,6 @@
}
@BindsOptionalOf
- abstract Pip optionalPip();
-
- @BindsOptionalOf
abstract SplitScreen optionalSplitScreen();
@BindsOptionalOf
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index ae7b108..81cb1f4 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -32,6 +32,7 @@
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipUiEventLogger;
@@ -67,22 +68,6 @@
@SysUISingleton
@Provides
- static Pip providePipController(Context context,
- DisplayController displayController,
- PipAppOpsListener pipAppOpsListener,
- PipBoundsHandler pipBoundsHandler,
- PipMediaController pipMediaController,
- PipMenuActivityController pipMenuActivityController,
- PipTaskOrganizer pipTaskOrganizer,
- PipTouchHandler pipTouchHandler,
- WindowManagerShellWrapper windowManagerShellWrapper) {
- return new PipController(context, displayController,
- pipAppOpsListener, pipBoundsHandler, pipMediaController, pipMenuActivityController,
- pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper);
- }
-
- @SysUISingleton
- @Provides
static SplitScreen provideSplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController displayImeController, @Main Handler handler,
@@ -94,36 +79,57 @@
@SysUISingleton
@Provides
- static PipBoundsHandler providesPipBoundsHandler(Context context) {
+ static Optional<Pip> providePip(Context context, DisplayController displayController,
+ PipAppOpsListener pipAppOpsListener, PipBoundsHandler pipBoundsHandler,
+ PipBoundsState pipBoundsState, PipMediaController pipMediaController,
+ PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer,
+ PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper) {
+ return Optional.ofNullable(PipController.create(context, displayController,
+ pipAppOpsListener, pipBoundsHandler, pipBoundsState, pipMediaController,
+ pipMenuActivityController, pipTaskOrganizer, pipTouchHandler,
+ windowManagerShellWrapper));
+ }
+
+ @SysUISingleton
+ @Provides
+ static PipBoundsState providePipBoundsState() {
+ return new PipBoundsState();
+ }
+
+ @SysUISingleton
+ @Provides
+ static PipBoundsHandler providePipBoundsHandler(Context context) {
return new PipBoundsHandler(context);
}
@SysUISingleton
@Provides
- static PipMenuActivityController providesPipMenuActivityController(Context context,
+ static PipMenuActivityController providePipMenuActivityController(Context context,
PipMediaController pipMediaController, PipTaskOrganizer pipTaskOrganizer) {
return new PipMenuActivityController(context, pipMediaController, pipTaskOrganizer);
}
@SysUISingleton
@Provides
- static PipTouchHandler providesPipTouchHandler(Context context,
+ static PipTouchHandler providePipTouchHandler(Context context,
PipMenuActivityController menuActivityController, PipBoundsHandler pipBoundsHandler,
+ PipBoundsState pipBoundsState,
PipTaskOrganizer pipTaskOrganizer,
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger) {
return new PipTouchHandler(context, menuActivityController, pipBoundsHandler,
- pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger);
+ pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger);
}
@SysUISingleton
@Provides
- static PipTaskOrganizer providesPipTaskOrganizer(Context context,
+ static PipTaskOrganizer providePipTaskOrganizer(Context context,
+ PipBoundsState pipBoundsState,
PipBoundsHandler pipBoundsHandler,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
- return new PipTaskOrganizer(context, pipBoundsHandler,
+ return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler,
pipSurfaceTransactionHelper, splitScreenOptional, displayController,
pipUiEventLogger, shellTaskOrganizer);
}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index db87845..d541c8f 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -70,6 +70,13 @@
android:exported="false"
android:resizeableActivity="true" />
+ <activity android:name="com.android.systemui.emergency.EmergencyActivityTest"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.systemui.action.LAUNCH_EMERGENCY"/>
+ </intent-filter>
+ </activity>
+
<activity
android:name="com.android.systemui.globalactions.GlobalActionsImeTest$TestActivity"
android:excludeFromRecents="true"
@@ -82,6 +89,10 @@
<activity android:name="com.android.systemui.screenshot.ScrollViewActivity"
android:exported="false" />
+
+ <activity android:name="com.android.systemui.screenshot.RecyclerViewActivity"
+ android:exported="false" />
+
<provider
android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 3aa6ec0..094230d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.res.Resources;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.view.View;
@@ -64,6 +65,8 @@
ColorExtractor.GradientColors mGradientColors;
@Mock
KeyguardSliceViewController mKeyguardSliceViewController;
+ @Mock
+ Resources mResources;
private KeyguardClockSwitchController mController;
@@ -72,9 +75,13 @@
MockitoAnnotations.initMocks(this);
when(mView.isAttachedToWindow()).thenReturn(true);
-
+ when(mResources.getString(anyInt())).thenReturn("h:mm");
mController = new KeyguardClockSwitchController(
- mView, mStatusBarStateController, mColorExtractor, mClockManager,
+ mView,
+ mResources,
+ mStatusBarStateController,
+ mColorExtractor,
+ mClockManager,
mKeyguardSliceViewController);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
index ae159c7..62906f3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
@@ -20,9 +20,11 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.hardware.display.DisplayManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.AttributeSet;
+import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
@@ -104,9 +106,10 @@
@Test
public void testInflation_doesntCrash() {
- KeyguardPresentation keyguardPresentation = new KeyguardPresentation(mContext,
- mContext.getDisplayNoVerify(), mKeyguardStatusViewComponentFactory,
- mLayoutInflater);
+ final Display display = mContext.getSystemService(DisplayManager.class).getDisplay(
+ Display.DEFAULT_DISPLAY);
+ KeyguardPresentation keyguardPresentation = new KeyguardPresentation(mContext, display,
+ mKeyguardStatusViewComponentFactory);
keyguardPresentation.onCreate(null /*savedInstanceState */);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
index b7bcaa3..9f8f6c1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
@@ -44,9 +44,7 @@
@RunWithLooper(setAsMainLooper = true)
public class KeyguardSliceViewControllerTest extends SysuiTestCase {
@Mock
- private KeyguardSliceView mView;;
- @Mock
- private KeyguardStatusView mKeyguardStatusView;
+ private KeyguardSliceView mView;
@Mock
private TunerService mTunerService;
@Mock
@@ -63,7 +61,7 @@
when(mView.isAttachedToWindow()).thenReturn(true);
when(mView.getContext()).thenReturn(mContext);
mController = new KeyguardSliceViewController(
- mView, mKeyguardStatusView, mActivityStarter, mConfigurationController,
+ mView, mActivityStarter, mConfigurationController,
mTunerService, mDumpManager);
mController.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
new file mode 100644
index 0000000..752a744
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import static org.mockito.Mockito.verify;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class KeyguardStatusViewControllerTest extends SysuiTestCase {
+
+ @Mock
+ private KeyguardStatusView mKeyguardStatusView;
+ @Mock
+ private KeyguardSliceViewController mKeyguardSliceViewController;
+ @Mock
+ private KeyguardClockSwitchController mKeyguardClockSwitchController;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ ConfigurationController mConfigurationController;
+
+ private KeyguardStatusViewController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mController = new KeyguardStatusViewController(
+ mKeyguardStatusView,
+ mKeyguardSliceViewController,
+ mKeyguardClockSwitchController,
+ mKeyguardStateController,
+ mKeyguardUpdateMonitor,
+ mConfigurationController);
+ }
+
+ @Test
+ public void dozeTimeTick_updatesSlice() {
+ mController.dozeTimeTick();
+ verify(mKeyguardSliceViewController).refresh();
+ }
+
+ @Test
+ public void dozeTimeTick_updatesClock() {
+ mController.dozeTimeTick();
+ verify(mKeyguardClockSwitchController).refresh();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
deleted file mode 100644
index 79ec4f2..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.keyguard;
-
-import static org.mockito.Mockito.verify;
-
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.LayoutInflater;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-
-@SmallTest
-@RunWithLooper
-@RunWith(AndroidTestingRunner.class)
-public class KeyguardStatusViewTest extends SysuiTestCase {
-
- @Mock
- KeyguardSliceViewController mKeyguardSliceViewController;
-
- @Mock
- KeyguardClockSwitch mClockView;
- @InjectMocks
- KeyguardStatusView mKeyguardStatusView;
-
- @Before
- public void setUp() {
- allowTestableLooperAsMainThread();
- LayoutInflater layoutInflater = LayoutInflater.from(getContext());
- mKeyguardStatusView =
- (KeyguardStatusView) layoutInflater.inflate(R.layout.keyguard_status_view, null);
- org.mockito.MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void dozeTimeTick_updatesSlice() {
- mKeyguardStatusView.dozeTimeTick();
- verify(mKeyguardSliceViewController).refresh();
- }
-
- @Test
- public void dozeTimeTick_updatesClock() {
- mKeyguardStatusView.dozeTimeTick();
- verify(mClockView).refresh();
- }
-
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 353fe62..35fe1ba 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -168,7 +168,7 @@
verify(mMockListener2).onClockChanged(captor2.capture());
assertThat(captor1.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS);
assertThat(captor2.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS);
- assertThat(captor1.getValue()).isNotSameAs(captor2.getValue());
+ assertThat(captor1.getValue()).isNotSameInstanceAs(captor2.getValue());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index ab805f4..0c87f59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -621,6 +621,44 @@
assertEquals(mScreenDecorations.mRoundedDefault, new Point(5, 5));
}
+ @Test
+ public void testOnlyRoundedCornerRadiusTop() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_top, 10);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
+
+ mScreenDecorations.start();
+ assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefault);
+ assertEquals(new Point(10, 10), mScreenDecorations.mRoundedDefaultTop);
+ assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefaultBottom);
+ }
+
+ @Test
+ public void testOnlyRoundedCornerRadiusBottom() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_top, 0);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.dimen.rounded_corner_radius_bottom, 20);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
+
+ mScreenDecorations.start();
+ assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefault);
+ assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefaultTop);
+ assertEquals(new Point(20, 20), mScreenDecorations.mRoundedDefaultBottom);
+ }
+
@Test
public void testBoundingRectsToRegion() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
index 1638ea1..71a0434 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
@@ -29,8 +29,8 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SizeCompatModeActivityController.RestartActivityButton;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.CommandQueue;
import org.junit.Before;
@@ -50,7 +50,7 @@
private SizeCompatModeActivityController mController;
private TaskStackChangeListener mTaskStackListener;
- private @Mock ActivityManagerWrapper mMockAm;
+ private @Mock TaskStackChangeListeners mMockTaskListeners;
private @Mock RestartActivityButton mMockButton;
private @Mock IBinder mMockActivityToken;
@@ -59,7 +59,7 @@
MockitoAnnotations.initMocks(this);
doReturn(true).when(mMockButton).show();
- mController = new SizeCompatModeActivityController(mContext, mMockAm,
+ mController = new SizeCompatModeActivityController(mContext, mMockTaskListeners,
new CommandQueue(mContext)) {
@Override
RestartActivityButton createRestartButton(Context context) {
@@ -69,7 +69,7 @@
ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
ArgumentCaptor.forClass(TaskStackChangeListener.class);
- verify(mMockAm).registerTaskStackListener(listenerCaptor.capture());
+ verify(mMockTaskListeners).registerTaskStackListener(listenerCaptor.capture());
mTaskStackListener = listenerCaptor.getValue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index 0a51b26..3da1f29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -24,6 +24,8 @@
import static android.view.MotionEvent.ACTION_UP;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK;
+import static com.android.systemui.accessibility.MagnificationModeSwitch.DEFAULT_FADE_OUT_ANIMATION_DELAY_MS;
+import static com.android.systemui.accessibility.MagnificationModeSwitch.FADING_ANIMATION_DURATION_MS;
import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId;
import static junit.framework.Assert.assertEquals;
@@ -32,10 +34,10 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -48,6 +50,7 @@
import android.view.ViewConfiguration;
import android.view.ViewPropertyAnimator;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
@@ -69,8 +72,13 @@
@RunWith(AndroidTestingRunner.class)
public class MagnificationModeSwitchTest extends SysuiTestCase {
+ private static final float FADE_IN_ALPHA = 1f;
+ private static final float FADE_OUT_ALPHA = 0f;
+
private ImageView mSpyImageView;
@Mock
+ private AccessibilityManager mAccessibilityManager;
+ @Mock
private WindowManager mWindowManager;
@Mock
private ViewPropertyAnimator mViewPropertyAnimator;
@@ -86,50 +94,70 @@
wm.getMaximumWindowMetrics()
).when(mWindowManager).getMaximumWindowMetrics();
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+ mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
mSpyImageView = Mockito.spy(new ImageView(mContext));
- doAnswer(invocation -> null).when(mSpyImageView).setOnTouchListener(
- mTouchListenerCaptor.capture());
- initMockImageViewAndAnimator();
+ resetMockImageViewAndAnimator();
mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView);
}
@Test
- public void removeButton_removeView() {
+ public void removeButton_buttonIsShowing_removeView() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
mMagnificationModeSwitch.removeButton();
verify(mWindowManager).removeView(mSpyImageView);
- // First invocation is in showButton.
- verify(mViewPropertyAnimator, times(2)).cancel();
+ verify(mViewPropertyAnimator).cancel();
}
@Test
public void showWindowModeButton_fullscreenMode_addViewAndSetImageResource() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- verify(mSpyImageView).setAlpha(1.0f);
verify(mSpyImageView).setImageResource(
getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
- assertShowButtonAnimation();
- ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
- verify(mViewPropertyAnimator).withEndAction(captor.capture());
verify(mWindowManager).addView(eq(mSpyImageView), any(WindowManager.LayoutParams.class));
+ assertShowFadingAnimation(FADE_IN_ALPHA);
+ assertShowFadingAnimation(FADE_OUT_ALPHA);
+ }
- captor.getValue().run();
+ @Test
+ public void showMagnificationButton_a11yTimeout_autoFadeOut() {
+ final int a11yTimeout = 12345;
+ when(mAccessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenReturn(
+ a11yTimeout);
- // First invocation is in showButton.
- verify(mViewPropertyAnimator, times(2)).cancel();
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
+ verify(mAccessibilityManager).getRecommendedTimeoutMillis(
+ DEFAULT_FADE_OUT_ANIMATION_DELAY_MS, AccessibilityManager.FLAG_CONTENT_ICONS
+ | AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ final ArgumentCaptor<Runnable> fadeOutCaptor = ArgumentCaptor.forClass(Runnable.class);
+ final ArgumentCaptor<Long> fadeOutDelay = ArgumentCaptor.forClass(Long.class);
+ verify(mSpyImageView).postOnAnimationDelayed(fadeOutCaptor.capture(),
+ fadeOutDelay.capture());
+ assertEquals(a11yTimeout, (long) fadeOutDelay.getValue());
+
+ // Verify the end action after fade-out.
+ fadeOutCaptor.getValue().run();
+ final ArgumentCaptor<Runnable> endActionCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mViewPropertyAnimator).withEndAction(endActionCaptor.capture());
+
+ endActionCaptor.getValue().run();
+
+ verify(mViewPropertyAnimator).cancel();
verify(mWindowManager).removeView(mSpyImageView);
}
@Test
- public void onConfigurationChanged_setImageResource() {
+ public void onConfigurationChanged_buttonIsShowing_setImageResource() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ resetMockImageViewAndAnimator();
+
mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
- verify(mSpyImageView, times(2)).setImageResource(
+ verify(mSpyImageView).setImageResource(
getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN));
}
@@ -162,7 +190,6 @@
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
listener.onTouch(mSpyImageView, MotionEvent.obtain(
0, 0, ACTION_DOWN, 100, 100, 0));
- verify(mSpyImageView).setAlpha(1.0f);
verify(mViewPropertyAnimator).cancel();
listener.onTouch(mSpyImageView, MotionEvent.obtain(
@@ -173,9 +200,8 @@
resetMockImageViewAndAnimator();
listener.onTouch(mSpyImageView, MotionEvent.obtain(
0, ViewConfiguration.getTapTimeout() + 10, ACTION_UP, 100 + offset, 100, 0));
- verify(mSpyImageView).setAlpha(1.0f);
assertModeUnchanged(previousMode);
- assertShowButtonAnimation();
+ assertShowFadingAnimation(FADE_OUT_ALPHA);
}
@Test
@@ -193,9 +219,8 @@
resetMockImageViewAndAnimator();
listener.onTouch(mSpyImageView, MotionEvent.obtain(
0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100, 100, 0));
- verify(mSpyImageView).setAlpha(1.0f);
assertModeUnchanged(previousMode);
- assertShowButtonAnimation();
+ assertShowFadingAnimation(FADE_OUT_ALPHA);
}
@Test
@@ -216,9 +241,8 @@
resetMockImageViewAndAnimator();
listener.onTouch(mSpyImageView, MotionEvent.obtain(
0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100 + offset, 100, 0));
- verify(mSpyImageView).setAlpha(1.0f);
assertModeUnchanged(previousMode);
- assertShowButtonAnimation();
+ assertShowFadingAnimation(FADE_OUT_ALPHA);
}
@Test
@@ -249,37 +273,55 @@
verifyTapAction(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
}
+ @Test
+ public void showButton_showFadeOutAnimation_fadeOutAnimationCanceled() {
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ assertShowFadingAnimation(FADE_OUT_ALPHA);
+ resetMockImageViewAndAnimator();
+
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+ verify(mViewPropertyAnimator).cancel();
+ assertEquals(1f, mSpyImageView.getAlpha());
+ assertShowFadingAnimation(FADE_OUT_ALPHA);
+ }
+
private void assertModeUnchanged(int expectedMode) {
final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
assertEquals(expectedMode, actualMode);
}
- private void assertShowButtonAnimation() {
- verify(mViewPropertyAnimator).cancel();
- verify(mViewPropertyAnimator).setDuration(anyLong());
- verify(mViewPropertyAnimator).alpha(anyFloat());
+ private void assertShowFadingAnimation(float alpha) {
+ ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ if (alpha == FADE_IN_ALPHA) { // Fade-in
+ verify(mSpyImageView).postOnAnimation(runnableCaptor.capture());
+ } else { // Fade-out
+ verify(mSpyImageView).postOnAnimationDelayed(runnableCaptor.capture(), anyLong());
+ }
+ resetMockAnimator();
+
+ runnableCaptor.getValue().run();
+
+ verify(mViewPropertyAnimator).setDuration(eq(FADING_ANIMATION_DURATION_MS));
+ verify(mViewPropertyAnimator).alpha(alpha);
verify(mViewPropertyAnimator).start();
}
- private void initMockImageViewAndAnimator() {
+ private void resetMockImageViewAndAnimator() {
+ Mockito.reset(mSpyImageView);
+ doAnswer(invocation -> null).when(mSpyImageView).setOnTouchListener(
+ mTouchListenerCaptor.capture());
+ resetMockAnimator();
+ }
+
+ private void resetMockAnimator() {
+ Mockito.reset(mViewPropertyAnimator);
when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator);
when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator);
when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn(
mViewPropertyAnimator);
-
when(mSpyImageView.animate()).thenReturn(mViewPropertyAnimator);
- doAnswer(invocation -> {
- Runnable run = invocation.getArgument(0);
- run.run();
- return null;
- }).when(mSpyImageView).postDelayed(any(), anyLong());
- }
-
- private void resetMockImageViewAndAnimator() {
- Mockito.reset(mViewPropertyAnimator);
- Mockito.reset(mSpyImageView);
- initMockImageViewAndAnimator();
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 5f2fd69..f1606c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -224,11 +224,12 @@
@Test
- public void onDensityChanged_enabled_updateDimensionsAndLayout() {
+ public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
Float.NaN);
Mockito.reset(mWindowManager);
+ Mockito.reset(mMirrorWindowControl);
});
mInstrumentation.runOnMainSync(() -> {
@@ -237,7 +238,9 @@
verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
verify(mWindowManager).removeView(any());
+ verify(mMirrorWindowControl).destroyControl();
verify(mWindowManager).addView(any(), any());
+ verify(mMirrorWindowControl).showControl();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 08ccd854..b082d17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -91,6 +91,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -194,6 +195,8 @@
@Mock private WindowManagerShellWrapper mWindowManagerShellWrapper;
+ @Mock private BubbleLogger mBubbleLogger;
+
private BubbleData mBubbleData;
private TestableLooper mTestableLooper;
@@ -249,7 +252,7 @@
mock(HeadsUpManager.class),
mock(Handler.class)
);
- mBubbleData = new BubbleData(mContext);
+ mBubbleData = new BubbleData(mContext, mBubbleLogger);
when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
mBubbleController = new TestableBubbleController(
mContext,
@@ -273,7 +276,10 @@
mStatusBarService,
mWindowManager,
mWindowManagerShellWrapper,
- mLauncherApps);
+ mLauncherApps,
+ mBubbleLogger,
+ mock(Handler.class),
+ mock(ShellTaskOrganizer.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
// Get a reference to the BubbleController's entry listener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 6e2c7e5..0c872db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.bubbles;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -94,6 +95,8 @@
private PendingIntent mExpandIntent;
@Mock
private PendingIntent mDeleteIntent;
+ @Mock
+ private BubbleLogger mBubbleLogger;
@Captor
private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
@@ -133,7 +136,7 @@
mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener, mPendingIntentCanceledListener);
mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener, mPendingIntentCanceledListener);
- mBubbleData = new BubbleData(getContext());
+ mBubbleData = new BubbleData(getContext(), mBubbleLogger);
// Used by BubbleData to set lastAccessedTime
when(mTimeSource.currentTimeMillis()).thenReturn(1000L);
@@ -801,47 +804,48 @@
private void assertBubbleAdded(Bubble expected) {
BubbleData.Update update = mUpdateCaptor.getValue();
- assertThat(update.addedBubble).named("addedBubble").isEqualTo(expected);
+ assertWithMessage("addedBubble").that(update.addedBubble).isEqualTo(expected);
}
private void assertBubbleRemoved(Bubble expected, @BubbleController.DismissReason int reason) {
BubbleData.Update update = mUpdateCaptor.getValue();
- assertThat(update.removedBubbles).named("removedBubbles")
+ assertWithMessage("removedBubbles").that(update.removedBubbles)
.isEqualTo(ImmutableList.of(Pair.create(expected, reason)));
}
private void assertOrderNotChanged() {
BubbleData.Update update = mUpdateCaptor.getValue();
- assertThat(update.orderChanged).named("orderChanged").isFalse();
+ assertWithMessage("orderChanged").that(update.orderChanged).isFalse();
}
private void assertOrderChangedTo(Bubble... order) {
BubbleData.Update update = mUpdateCaptor.getValue();
- assertThat(update.orderChanged).named("orderChanged").isTrue();
- assertThat(update.bubbles).named("bubble order").isEqualTo(ImmutableList.copyOf(order));
+ assertWithMessage("orderChanged").that(update.orderChanged).isTrue();
+ assertWithMessage("bubble order").that(update.bubbles)
+ .isEqualTo(ImmutableList.copyOf(order));
}
private void assertSelectionNotChanged() {
BubbleData.Update update = mUpdateCaptor.getValue();
- assertThat(update.selectionChanged).named("selectionChanged").isFalse();
+ assertWithMessage("selectionChanged").that(update.selectionChanged).isFalse();
}
private void assertSelectionChangedTo(Bubble bubble) {
BubbleData.Update update = mUpdateCaptor.getValue();
- assertThat(update.selectionChanged).named("selectionChanged").isTrue();
- assertThat(update.selectedBubble).named("selectedBubble").isEqualTo(bubble);
+ assertWithMessage("selectionChanged").that(update.selectionChanged).isTrue();
+ assertWithMessage("selectedBubble").that(update.selectedBubble).isEqualTo(bubble);
}
private void assertSelectionCleared() {
BubbleData.Update update = mUpdateCaptor.getValue();
- assertThat(update.selectionChanged).named("selectionChanged").isTrue();
- assertThat(update.selectedBubble).named("selectedBubble").isNull();
+ assertWithMessage("selectionChanged").that(update.selectionChanged).isTrue();
+ assertWithMessage("selectedBubble").that(update.selectedBubble).isNull();
}
private void assertExpandedChangedTo(boolean expected) {
BubbleData.Update update = mUpdateCaptor.getValue();
- assertThat(update.expandedChanged).named("expandedChanged").isTrue();
- assertThat(update.expanded).named("expanded").isEqualTo(expected);
+ assertWithMessage("expandedChanged").that(update.expandedChanged).isTrue();
+ assertWithMessage("expanded").that(update.expanded).isEqualTo(expected);
}
private void assertOverflowChangedTo(ImmutableList<Bubble> bubbles) {
@@ -913,4 +917,4 @@
setCurrentTime(time);
mBubbleData.setExpanded(shouldBeExpanded);
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/MultiWindowTaskListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/MultiWindowTaskListenerTest.java
new file mode 100644
index 0000000..e7bf86e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/MultiWindowTaskListenerTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.wm.shell.ShellTaskOrganizer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
+public class MultiWindowTaskListenerTest extends SysuiTestCase {
+
+ @Mock
+ ShellTaskOrganizer mOrganizer;
+ @Mock
+ MultiWindowTaskListener.Listener mPendingListener;
+ @Mock
+ SurfaceControl mLeash;
+ @Mock
+ ActivityManager.RunningTaskInfo mTaskInfo;
+ @Mock
+ WindowContainerToken mToken;
+
+ Handler mHandler;
+ MultiWindowTaskListener mTaskListener;
+ TestableLooper mTestableLooper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mTestableLooper = TestableLooper.get(this);
+ mHandler = new Handler(mTestableLooper.getLooper());
+
+ mTaskInfo = new ActivityManager.RunningTaskInfo();
+ mTaskInfo.token = mToken;
+
+ mTaskListener = new MultiWindowTaskListener(mHandler, mOrganizer);
+ }
+
+ private void addTaskAndVerify() {
+ final Binder cookie = new Binder();
+ mTaskInfo.addLaunchCookie(cookie);
+ mTaskListener.setPendingLaunchCookieListener(cookie, mPendingListener);
+ mTaskListener.onTaskAppeared(mTaskInfo, mLeash);
+ mTestableLooper.processAllMessages();
+ verify(mPendingListener).onTaskAppeared(eq(mTaskInfo), eq(mLeash));
+ }
+
+ @Test
+ public void testListenForMultiWindowMode() {
+ mTaskListener = new MultiWindowTaskListener(mHandler, mOrganizer);
+ verify(mOrganizer).addListener(eq(mTaskListener), eq(TASK_LISTENER_TYPE_MULTI_WINDOW));
+ }
+
+ @Test
+ public void testRemovePendingListener() {
+ addTaskAndVerify();
+ reset(mPendingListener);
+
+ mTaskListener.removeListener(mPendingListener);
+
+ // If it was removed, our pendingListener shouldn't get triggered:
+ mTaskListener.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskListener.onTaskInfoChanged(mTaskInfo);
+ mTaskListener.onBackPressedOnTaskRoot(mTaskInfo);
+ mTaskListener.onTaskVanished(mTaskInfo);
+
+ mTestableLooper.processAllMessages();
+ verify(mPendingListener, never()).onTaskAppeared(any(), any());
+ verify(mPendingListener, never()).onTaskInfoChanged(any());
+ verify(mPendingListener, never()).onBackPressedOnTaskRoot(any());
+ verify(mPendingListener, never()).onTaskVanished(any());
+ }
+
+ @Test
+ public void testOnTaskAppeared() {
+ addTaskAndVerify();
+ verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mToken), eq(true));
+ }
+
+ @Test
+ public void testOnTaskAppeared_nullListener() {
+ mTaskListener.onTaskAppeared(mTaskInfo, mLeash);
+ mTestableLooper.processAllMessages();
+
+ verify(mOrganizer, never()).setInterceptBackPressedOnTaskRoot(any(), anyBoolean());
+ verify(mPendingListener, never()).onTaskAppeared(any(), any());
+ }
+
+ @Test
+ public void testOnTaskVanished() {
+ addTaskAndVerify();
+ mTaskListener.onTaskVanished(mTaskInfo);
+ mTestableLooper.processAllMessages();
+
+ verify(mPendingListener).onTaskVanished(eq(mTaskInfo));
+ }
+
+ @Test
+ public void testOnTaskVanished_neverAdded() {
+ mTaskListener.onTaskVanished(mTaskInfo);
+ mTestableLooper.processAllMessages();
+
+ verify(mPendingListener, never()).onTaskVanished(any());
+ }
+
+ @Test
+ public void testOnTaskInfoChanged() {
+ addTaskAndVerify();
+ mTaskListener.onTaskInfoChanged(mTaskInfo);
+ mTestableLooper.processAllMessages();
+
+ verify(mPendingListener).onTaskInfoChanged(eq(mTaskInfo));
+ }
+
+ @Test
+ public void testOnTaskInfoChanged_neverAdded() {
+ mTaskListener.onTaskInfoChanged(mTaskInfo);
+ mTestableLooper.processAllMessages();
+
+ verify(mPendingListener, never()).onTaskInfoChanged(any());
+ }
+
+ @Test
+ public void testOnBackPressedOnTaskRoot() {
+ addTaskAndVerify();
+ mTaskListener.onBackPressedOnTaskRoot(mTaskInfo);
+ mTestableLooper.processAllMessages();
+
+ verify(mPendingListener).onBackPressedOnTaskRoot(eq(mTaskInfo));
+ }
+
+ @Test
+ public void testOnBackPressedOnTaskRoot_neverAdded() {
+ mTaskListener.onBackPressedOnTaskRoot(mTaskInfo);
+ mTestableLooper.processAllMessages();
+
+ verify(mPendingListener, never()).onBackPressedOnTaskRoot(any());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 1eaa6a4..cbacd53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -91,6 +91,7 @@
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.InjectionInflationController;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -188,6 +189,8 @@
private LauncherApps mLauncherApps;
@Mock
private WindowManagerShellWrapper mWindowManagerShellWrapper;
+ @Mock
+ private BubbleLogger mBubbleLogger;
private BubbleData mBubbleData;
@@ -251,7 +254,7 @@
mock(HeadsUpManager.class),
mock(Handler.class)
);
- mBubbleData = new BubbleData(mContext);
+ mBubbleData = new BubbleData(mContext, mBubbleLogger);
when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
mBubbleController = new TestableBubbleController(
mContext,
@@ -275,7 +278,10 @@
mStatusBarService,
mWindowManager,
mWindowManagerShellWrapper,
- mLauncherApps);
+ mLauncherApps,
+ mBubbleLogger,
+ mock(Handler.class),
+ mock(ShellTaskOrganizer.class));
mBubbleController.addNotifCallback(mNotifCallback);
mBubbleController.setExpandListener(mBubbleExpandListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
new file mode 100644
index 0000000..63d60f9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceSession;
+import android.window.WindowContainerToken;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.wm.shell.ShellTaskOrganizer;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
+public class TaskViewTest extends SysuiTestCase {
+
+ @Mock
+ TaskView.Listener mViewListener;
+ @Mock
+ ActivityManager.RunningTaskInfo mTaskInfo;
+ @Mock
+ WindowContainerToken mToken;
+ @Mock
+ ShellTaskOrganizer mOrganizer;
+ @Mock
+ MultiWindowTaskListener mTaskListener;
+
+ SurfaceSession mSession;
+ SurfaceControl mLeash;
+
+ Context mContext;
+ TaskView mTaskView;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mLeash = new SurfaceControl.Builder(mSession)
+ .setName("test")
+ .build();
+
+ mContext = getContext();
+
+ when(mTaskListener.getTaskOrganizer()).thenReturn(mOrganizer);
+ mTaskInfo = new ActivityManager.RunningTaskInfo();
+ mTaskInfo.token = mToken;
+ mTaskInfo.taskId = 314;
+ mTaskInfo.taskDescription = mock(ActivityManager.TaskDescription.class);
+
+ mTaskView = new TaskView(mContext, mTaskListener);
+ mTaskView.setListener(mViewListener);
+ }
+
+ @After
+ public void tearDown() {
+ if (mTaskView != null) {
+ mTaskView.release();
+ }
+ }
+
+ @Test
+ public void testSetPendingListener_throwsException() {
+ TaskView taskView = new TaskView(mContext, mTaskListener);
+ taskView.setListener(mViewListener);
+ try {
+ taskView.setListener(mViewListener);
+ } catch (IllegalStateException e) {
+ // pass
+ return;
+ }
+ fail("Expected IllegalStateException");
+ }
+
+ @Test
+ public void testStartActivity() {
+ ActivityOptions options = ActivityOptions.makeBasic();
+ mTaskView.startActivity(mock(PendingIntent.class), null, options);
+
+ verify(mTaskListener).setPendingLaunchCookieListener(any(), eq(mTaskView));
+ assertThat(options.getLaunchWindowingMode()).isEqualTo(WINDOWING_MODE_MULTI_WINDOW);
+ assertThat(options.getTaskAlwaysOnTop()).isTrue();
+ }
+
+ @Test
+ public void testOnTaskAppeared_noSurface() {
+ mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+
+ verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
+ verify(mViewListener, never()).onInitialized();
+ // If there's no surface the task should be made invisible
+ verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
+ }
+
+ @Test
+ public void testOnTaskAppeared_withSurface() {
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+
+ verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
+ verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testSurfaceCreated_noTask() {
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+
+ verify(mViewListener).onInitialized();
+ // No task, no visibility change
+ verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testSurfaceCreated_withTask() {
+ mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+
+ verify(mViewListener).onInitialized();
+ verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(true));
+ }
+
+ @Test
+ public void testSurfaceDestroyed_noTask() {
+ SurfaceHolder sh = mock(SurfaceHolder.class);
+ mTaskView.surfaceCreated(sh);
+ mTaskView.surfaceDestroyed(sh);
+
+ verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testSurfaceDestroyed_withTask() {
+ SurfaceHolder sh = mock(SurfaceHolder.class);
+ mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskView.surfaceCreated(sh);
+ reset(mViewListener);
+ mTaskView.surfaceDestroyed(sh);
+
+ verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
+ }
+
+ @Test
+ public void testOnReleased() {
+ mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ mTaskView.release();
+
+ verify(mTaskListener).removeListener(eq(mTaskView));
+ verify(mViewListener).onReleased();
+ }
+
+ @Test
+ public void testOnTaskVanished() {
+ mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ mTaskView.onTaskVanished(mTaskInfo);
+
+ verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
+ }
+
+ @Test
+ public void testOnBackPressedOnTaskRoot() {
+ mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+ mTaskView.onBackPressedOnTaskRoot(mTaskInfo);
+
+ verify(mViewListener).onBackPressedOnTaskRoot(eq(mTaskInfo.taskId));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index 87ea22a..27c6fc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -19,6 +19,7 @@
import android.app.INotificationManager;
import android.content.Context;
import android.content.pm.LauncherApps;
+import android.os.Handler;
import android.view.WindowManager;
import com.android.internal.statusbar.IStatusBarService;
@@ -35,10 +36,10 @@
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.FloatingContentCoordinator;
-
/**
* Testable BubbleController subclass that immediately synchronizes surfaces.
*/
@@ -66,14 +67,18 @@
IStatusBarService statusBarService,
WindowManager windowManager,
WindowManagerShellWrapper windowManagerShellWrapper,
- LauncherApps launcherApps) {
+ LauncherApps launcherApps,
+ BubbleLogger bubbleLogger,
+ Handler mainHandler,
+ ShellTaskOrganizer shellTaskOrganizer) {
super(context,
notificationShadeWindowController, statusBarStateController, shadeController,
data, Runnable::run, configurationController, interruptionStateProvider,
zenModeController, lockscreenUserManager, groupManager, entryManager,
notifPipeline, featureFlags, dumpManager, floatingContentCoordinator,
dataRepository, sysUiState, notificationManager, statusBarService,
- windowManager, windowManagerShellWrapper, launcherApps);
+ windowManager, windowManagerShellWrapper, launcherApps, bubbleLogger,
+ mainHandler, shellTaskOrganizer);
setInflateSynchronously(true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java
new file mode 100644
index 0000000..a52a598
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.emergency;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.systemui.R;
+
+/**
+ * Test activity for resolving {@link EmergencyGesture#ACTION_LAUNCH_EMERGENCY} action.
+ */
+public class EmergencyActivityTest extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
index 3439fe5..cd5740d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
@@ -41,8 +41,8 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import org.junit.Before;
import org.junit.Test;
@@ -62,7 +62,7 @@
private static final int TASK_ID = 444;
private @Mock Context mContext;
- private @Mock ActivityManagerWrapper mActivityManager;
+ private @Mock TaskStackChangeListeners mTaskStackChangeListeners;
private @Mock IActivityTaskManager mIActivityTaskManager;
private WorkLockActivityController mController;
@@ -78,10 +78,10 @@
// Construct controller. Save the TaskStackListener for injecting events.
final ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
ArgumentCaptor.forClass(TaskStackChangeListener.class);
- mController = new WorkLockActivityController(mContext, mActivityManager,
+ mController = new WorkLockActivityController(mContext, mTaskStackChangeListeners,
mIActivityTaskManager);
- verify(mActivityManager).registerTaskStackListener(listenerCaptor.capture());
+ verify(mTaskStackChangeListeners).registerTaskStackListener(listenerCaptor.capture());
mTaskStackListener = listenerCaptor.getValue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 81139f19..af677c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -38,6 +38,7 @@
import androidx.lifecycle.LiveData
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.dialog.MediaOutputDialogFactory
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.util.animation.TransitionLayout
@@ -92,6 +93,7 @@
@Mock private lateinit var mediaDataManager: MediaDataManager
@Mock private lateinit var expandedSet: ConstraintSet
@Mock private lateinit var collapsedSet: ConstraintSet
+ @Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory
private lateinit var appIcon: ImageView
private lateinit var appName: TextView
private lateinit var albumView: ImageView
@@ -109,9 +111,11 @@
private lateinit var action2: ImageButton
private lateinit var action3: ImageButton
private lateinit var action4: ImageButton
+ private lateinit var settingsText: TextView
private lateinit var settings: View
private lateinit var cancel: View
- private lateinit var dismiss: View
+ private lateinit var dismiss: FrameLayout
+ private lateinit var dismissLabel: View
private lateinit var session: MediaSession
private val device = MediaDeviceData(true, null, DEVICE_NAME)
@@ -126,7 +130,8 @@
whenever(mediaViewController.collapsedLayout).thenReturn(collapsedSet)
player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController,
- seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil)
+ seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil,
+ mediaOutputDialogFactory)
whenever(seekBarViewModel.progress).thenReturn(seekBarData)
// Mock out a view holder for the player to attach to.
@@ -168,12 +173,16 @@
whenever(holder.action3).thenReturn(action3)
action4 = ImageButton(context)
whenever(holder.action4).thenReturn(action4)
+ settingsText = TextView(context)
+ whenever(holder.settingsText).thenReturn(settingsText)
settings = View(context)
whenever(holder.settings).thenReturn(settings)
cancel = View(context)
whenever(holder.cancel).thenReturn(cancel)
- dismiss = View(context)
+ dismiss = FrameLayout(context)
whenever(holder.dismiss).thenReturn(dismiss)
+ dismissLabel = View(context)
+ whenever(holder.dismissLabel).thenReturn(dismissLabel)
// Create media session
val metadataBuilder = MediaMetadata.Builder().apply {
@@ -327,6 +336,7 @@
notificationKey = KEY)
player.bind(state, mediaKey)
+ assertThat(dismiss.isEnabled).isEqualTo(true)
dismiss.callOnClick()
val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java)
verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean())
@@ -334,4 +344,16 @@
captor.value.onDismiss()
verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong())
}
+
+ @Test
+ fun dismissButtonDisabled() {
+ val mediaKey = "key for dismissal"
+ player.attach(holder)
+ val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
+ emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null,
+ isClearable = false, notificationKey = KEY)
+ player.bind(state, mediaKey)
+
+ assertThat(dismiss.isEnabled).isEqualTo(false)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
index 9b6dd05..3494bd6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
@@ -32,15 +32,11 @@
import static com.android.systemui.navigationbar.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_OVERVIEW_BUTTON_LONGPRESS;
import static com.android.systemui.navigationbar.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_OVERVIEW_BUTTON_TAP;
-import static junit.framework.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.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.hardware.input.InputManager;
import android.testing.AndroidTestingRunner;
@@ -151,19 +147,4 @@
verify(mUiEventLogger, times(1)).log(expected);
}
}
-
- @Test
- public void testBubbleEvents_bubbleExpanded() {
- when(mBubbles.getExpandedDisplayId(mContext)).thenReturn(3);
-
- int action = KeyEvent.ACTION_DOWN;
- int flags = 0;
- int code = KeyEvent.KEYCODE_BACK;
- mKeyButtonView.setCode(code);
- mKeyButtonView.sendEvent(action, flags);
-
- verify(mInputManager, times(1)).injectInputEvent(mInputEventCaptor.capture(),
- anyInt());
- assertEquals(3, mInputEventCaptor.getValue().getDisplayId());
- }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RecyclerViewActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RecyclerViewActivity.java
new file mode 100644
index 0000000..fde56ba
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RecyclerViewActivity.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.widget.LinearLayoutManager;
+import com.android.internal.widget.RecyclerView;
+import com.android.internal.widget.RecyclerView.LayoutParams;
+
+import java.util.Random;
+
+public class RecyclerViewActivity extends Activity {
+ public static final int CHILD_VIEW_HEIGHT = 300;
+ private static final int CHILD_VIEWS = 12;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ RecyclerView recyclerView = new RecyclerView(this);
+ recyclerView.setLayoutManager(new LinearLayoutManager(this));
+ recyclerView.setAdapter(new TestAdapter());
+ recyclerView.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ setContentView(recyclerView);
+ }
+
+ static final class TestViewHolder extends RecyclerView.ViewHolder {
+ TestViewHolder(View itemView) {
+ super(itemView);
+ }
+ }
+
+ static final class TestAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+ private final Random mRandom = new Random();
+
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new TestViewHolder(new TextView(parent.getContext()));
+ }
+
+ @Override
+ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+ TextView view = (TextView) holder.itemView;
+ view.setText("Child #" + position);
+ view.setTextColor(Color.WHITE);
+ view.setTextSize(30f);
+ view.setBackgroundColor(
+ Color.rgb(mRandom.nextFloat(), mRandom.nextFloat(), mRandom.nextFloat()));
+ view.setMinHeight(CHILD_VIEW_HEIGHT);
+ }
+
+ @Override
+ public int getItemCount() {
+ return CHILD_VIEWS;
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
index 72e6df2..724ea02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
@@ -80,7 +80,7 @@
val background2 = processor.processArtwork(context, artwork)!!
// THEN the two bitmaps are the same
// Note: This is currently broken and trying to use caching causes issues
- assertThat(background1).isNotSameAs(background2)
+ assertThat(background1).isNotSameInstanceAs(background2)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
index d0dfb171..524ead2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
@@ -34,6 +34,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -64,6 +65,7 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mDependency.injectMockDependency(MediaOutputDialogFactory.class);
allowTestableLooperAsMainThread();
when(mBindStage.getStageParams(any())).thenReturn(new RowContentBindParams());
mDynamicChildBindController =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 3e1616c..0be9f7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -21,6 +21,8 @@
import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
@@ -346,7 +348,7 @@
setSmartActions(mEntry.getKey(), null);
mEntryManager.updateNotificationRanking(mRankingMap);
- assertNull(mEntry.getSmartActions());
+ assertThat(mEntry.getSmartActions()).isEmpty();
}
@Test
@@ -378,6 +380,36 @@
}
@Test
+ public void testUpdatePendingNotification_rankingUpdated() {
+ // GIVEN a notification with ranking is pending
+ final Ranking originalRanking = mEntry.getRanking();
+ mEntryManager.mPendingNotifications.put(mEntry.getKey(), mEntry);
+
+ // WHEN the same notification has been updated with a new ranking
+ final int newRank = 2345;
+ doAnswer(invocationOnMock -> {
+ Ranking ranking = (Ranking)
+ invocationOnMock.getArguments()[1];
+ ranking.populate(
+ mEntry.getKey(),
+ newRank, /* this changed!! */
+ false,
+ 0,
+ 0,
+ IMPORTANCE_DEFAULT,
+ null, null,
+ null, null, null, true,
+ Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
+ false, null, null, false, false, false, null, 0, false);
+ return true;
+ }).when(mRankingMap).getRanking(eq(mEntry.getKey()), any(Ranking.class));
+ mEntryManager.addNotification(mSbn, mRankingMap);
+
+ // THEN ranking for the entry has been updated with new ranking
+ assertEquals(newRank, mEntry.getRanking().getRank());
+ }
+
+ @Test
public void testLifetimeExtenders_ifNotificationIsRetainedItIsntRemoved() {
// GIVEN an entry manager with a notification
mEntryManager.addActiveNotificationForTest(mEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
index 79fa436..5898664 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
@@ -118,7 +118,7 @@
val people = viewModel.people.toList()
assertThat(people.size).isEqualTo(1)
assertThat(people[0].name).isEqualTo("name")
- assertThat(people[0].icon).isSameAs(fakePerson.avatar)
+ assertThat(people[0].icon).isSameInstanceAs(fakePerson.avatar)
people[0].onClick()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index c2091da..d08b2b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -25,8 +25,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.AppOpsManager;
-import android.util.ArraySet;
import android.view.NotificationHeaderView;
import android.view.View;
import android.view.ViewPropertyAnimator;
@@ -37,6 +35,7 @@
import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import org.junit.Before;
import org.junit.Test;
@@ -51,6 +50,8 @@
@Before
@UiThreadTest
public void setup() {
+ mDependency.injectMockDependency(MediaOutputDialogFactory.class);
+
mView = new NotificationContentView(mContext, null);
ExpandableNotificationRow row = new ExpandableNotificationRow(mContext, null);
ExpandableNotificationRow mockRow = spy(row);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 1255b6d..8a5afe6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.media.MediaFeatureFlag;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
@@ -152,6 +153,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mDependency.injectMockDependency(SmartReplyController.class);
+ mDependency.injectMockDependency(MediaOutputDialogFactory.class);
mHandler = Handler.createAsync(TestableLooper.get(this).getLooper());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index fb37ed5..847e0a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -48,6 +48,7 @@
import com.android.systemui.bubbles.Bubbles;
import com.android.systemui.bubbles.BubblesTestActivity;
import com.android.systemui.media.MediaFeatureFlag;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -115,6 +116,7 @@
mTestLooper = testLooper;
dependency.injectMockDependency(NotificationMediaManager.class);
dependency.injectMockDependency(NotificationShadeWindowController.class);
+ dependency.injectMockDependency(MediaOutputDialogFactory.class);
mStatusBarStateController = mock(StatusBarStateController.class);
mGroupMembershipManager = new NotificationGroupManagerLegacy(
mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 7ee27c9..cb56f1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -53,6 +53,7 @@
import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardClockSwitchController;
import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.R;
@@ -190,6 +191,8 @@
@Mock
private KeyguardClockSwitchController mKeyguardClockSwitchController;
@Mock
+ private KeyguardStatusViewController mKeyguardStatusViewController;
+ @Mock
private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -246,6 +249,8 @@
.thenReturn(mKeyguardStatusViewComponent);
when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
.thenReturn(mKeyguardClockSwitchController);
+ when(mKeyguardStatusViewComponent.getKeyguardStatusViewController())
+ .thenReturn(mKeyguardStatusViewController);
mNotificationPanelViewController = new NotificationPanelViewController(mView,
mResources,
mInjectionInflationController,
@@ -350,7 +355,7 @@
mAccessibiltyDelegate.onInitializeAccessibilityNodeInfo(mView, nodeInfo);
List<AccessibilityNodeInfo.AccessibilityAction> actionList = nodeInfo.getActionList();
- assertThat(actionList).containsAllIn(
+ assertThat(actionList).containsAtLeastElementsIn(
new AccessibilityNodeInfo.AccessibilityAction[] {
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD,
AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP}
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 a6ea9966a..d6a7acb 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
@@ -24,6 +24,7 @@
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.fail;
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -34,6 +35,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,6 +45,7 @@
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
+import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.fingerprint.FingerprintManager;
@@ -84,6 +87,7 @@
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.emergency.EmergencyGesture;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
@@ -146,6 +150,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -873,6 +878,19 @@
verify(mDozeServiceHost).setDozeSuppressed(false);
}
+ @Test
+ public void onEmergencyActionLaunchGesture_launchesEmergencyIntent() {
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ StatusBar statusBarSpy = spy(mStatusBar);
+
+ statusBarSpy.onEmergencyActionLaunchGestureDetected();
+
+ verify(statusBarSpy).startActivity(intentCaptor.capture(), eq(true));
+ Intent sentIntent = intentCaptor.getValue();
+ assertEquals(sentIntent.getAction(), EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
+
+ }
+
public static class TestableNotificationInterruptStateProviderImpl extends
NotificationInterruptStateProviderImpl {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 76fe3bf..a58f1fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -16,17 +16,12 @@
package com.android.systemui.wmshell;
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
-
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.TestableContext;
import androidx.test.runner.AndroidJUnit4;
@@ -36,9 +31,9 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tracing.ProtoTracer;
@@ -68,7 +63,7 @@
@Mock CommandQueue mCommandQueue;
@Mock ConfigurationController mConfigurationController;
@Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock ActivityManagerWrapper mActivityManagerWrapper;
+ @Mock TaskStackChangeListeners mTaskStackChangeListeners;
@Mock DisplayImeController mDisplayImeController;
@Mock InputConsumerController mMockInputConsumerController;
@Mock NavigationModeController mNavigationModeController;
@@ -88,13 +83,12 @@
mInputConsumerController = InputConsumerController.getPipInputConsumer();
mWMShell = new WMShell(mContext, mCommandQueue, mConfigurationController,
- mInputConsumerController, mKeyguardUpdateMonitor, mActivityManagerWrapper,
+ mInputConsumerController, mKeyguardUpdateMonitor, mTaskStackChangeListeners,
mDisplayImeController, mNavigationModeController, mScreenLifecycle, mSysUiState,
Optional.of(mPip), Optional.of(mSplitScreen), Optional.of(mOneHanded),
mTaskOrganizer, mProtoTracer);
when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler);
-
}
@Test
@@ -112,31 +106,11 @@
}
@Test
- public void nonPipDevice_shouldNotInitPip() {
- final TestableContext nonPipContext = getNonPipFeatureContext();
- final WMShell nonPipWMShell = new WMShell(nonPipContext, mCommandQueue,
- mConfigurationController, mMockInputConsumerController, mKeyguardUpdateMonitor,
- mActivityManagerWrapper, mDisplayImeController, mNavigationModeController,
- mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
- Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer);
- nonPipWMShell.initPip(mPip);
-
- verify(mCommandQueue, never()).addCallback(any());
- verify(mKeyguardUpdateMonitor, never()).registerCallback(any());
- verify(mConfigurationController, never()).addCallback(any());
- verify(mSysUiState, never()).addCallback(any());
- verify(mActivityManagerWrapper, never()).registerTaskStackListener(any());
- verify(mMockInputConsumerController, never()).setInputListener(any());
- verify(mMockInputConsumerController, never()).setRegistrationListener(any());
- verify(mPip, never()).registerSessionListenerForCurrentUser();
- }
-
- @Test
public void initSplitScreen_registersCallbacks() {
mWMShell.initSplitScreen(mSplitScreen);
verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
- verify(mActivityManagerWrapper).registerTaskStackListener(
+ verify(mTaskStackChangeListeners).registerTaskStackListener(
any(TaskStackChangeListener.class));
}
@@ -149,18 +123,11 @@
verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class));
verify(mNavigationModeController).addListener(
any(NavigationModeController.ModeChangedListener.class));
- verify(mActivityManagerWrapper).registerTaskStackListener(
+ verify(mTaskStackChangeListeners).registerTaskStackListener(
any(TaskStackChangeListener.class));
verify(mOneHanded).registerGestureCallback(any(
OneHandedGestureHandler.OneHandedGestureEventCallback.class));
verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class));
}
-
- TestableContext getNonPipFeatureContext() {
- TestableContext spiedContext = spy(mContext);
- when(mMockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
- when(spiedContext.getPackageManager()).thenReturn(mMockPackageManager);
- return spiedContext;
- }
}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index 645b000..0b223f4 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,7 +33,6 @@
* @hide
*/
@SystemApi
-@TestApi
public final class TetheredClient implements Parcelable {
@NonNull
private final MacAddress mMacAddress;
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index db84368..13b05a8 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -23,7 +23,6 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.content.Context;
import android.os.Bundle;
import android.os.ConditionVariable;
@@ -55,7 +54,6 @@
* @hide
*/
@SystemApi
-@TestApi
public class TetheringManager {
private static final String TAG = TetheringManager.class.getSimpleName();
private static final int DEFAULT_TIMEOUT_MS = 60_000;
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index 6276c4e..9fc1d7e 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -20,6 +20,10 @@
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
import static android.net.util.PrefixUtils.asIpPrefix;
+import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
+import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
+import static com.android.net.module.util.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH;
+
import static java.util.Arrays.asList;
import android.content.Context;
@@ -32,14 +36,16 @@
import android.util.ArraySet;
import android.util.SparseArray;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import java.net.Inet4Address;
import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
@@ -58,10 +64,6 @@
public class PrivateAddressCoordinator {
public static final int PREFIX_LENGTH = 24;
- private static final int MAX_UBYTE = 256;
- private static final int BYTE_MASK = 0xff;
- private static final byte DEFAULT_ID = (byte) 42;
-
// Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
// address may be requested before coordinator get current upstream notification. To ensure
// coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared
@@ -69,22 +71,22 @@
// mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprecatedUpstreams().
private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap;
private final ArraySet<IpServer> mDownstreams;
- // IANA has reserved the following three blocks of the IP address space for private intranets:
- // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
- // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers.
- private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16";
private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24";
private static final String LEGACY_BLUETOOTH_IFACE_ADDRESS = "192.168.44.1/24";
- private final IpPrefix mTetheringPrefix;
+ private final List<IpPrefix> mTetheringPrefixes;
private final ConnectivityManager mConnectivityMgr;
private final TetheringConfiguration mConfig;
// keyed by downstream type(TetheringManager.TETHERING_*).
private final SparseArray<LinkAddress> mCachedAddresses;
public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
+ this(context, config, new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16"))));
+ }
+
+ public PrivateAddressCoordinator(Context context, TetheringConfiguration config,
+ List<IpPrefix> prefixPools) {
mDownstreams = new ArraySet<>();
mUpstreamPrefixMap = new ArrayMap<>();
- mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX);
mConnectivityMgr = (ConnectivityManager) context.getSystemService(
Context.CONNECTIVITY_SERVICE);
mConfig = config;
@@ -92,6 +94,8 @@
// Reserved static addresses for bluetooth and wifi p2p.
mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS));
mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS));
+
+ mTetheringPrefixes = prefixPools;
}
/**
@@ -132,7 +136,6 @@
private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) {
for (IpServer downstream : mDownstreams) {
final IpPrefix target = getDownstreamPrefix(downstream);
- if (target == null) continue;
for (IpPrefix source : prefixes) {
if (isConflictPrefix(source, target)) {
@@ -176,55 +179,152 @@
final LinkAddress cachedAddress = mCachedAddresses.get(ipServer.interfaceType());
if (useLastAddress && cachedAddress != null
&& !isConflictWithUpstream(asIpPrefix(cachedAddress))) {
+ mDownstreams.add(ipServer);
return cachedAddress;
}
- // Address would be 192.168.[subAddress]/24.
- final byte[] bytes = mTetheringPrefix.getRawAddress();
- final int subAddress = getRandomSubAddr();
- final int subNet = (subAddress >> 8) & BYTE_MASK;
- bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff);
- for (int i = 0; i < MAX_UBYTE; i++) {
- final int newSubNet = (subNet + i) & BYTE_MASK;
- bytes[2] = (byte) newSubNet;
-
- final InetAddress addr;
- try {
- addr = InetAddress.getByAddress(bytes);
- } catch (UnknownHostException e) {
- throw new IllegalStateException("Invalid address, shouldn't happen.", e);
+ for (IpPrefix prefixRange : mTetheringPrefixes) {
+ final LinkAddress newAddress = chooseDownstreamAddress(prefixRange);
+ if (newAddress != null) {
+ mDownstreams.add(ipServer);
+ mCachedAddresses.put(ipServer.interfaceType(), newAddress);
+ return newAddress;
}
-
- if (isConflict(new IpPrefix(addr, PREFIX_LENGTH))) continue;
-
- mDownstreams.add(ipServer);
- final LinkAddress newAddress = new LinkAddress(addr, PREFIX_LENGTH);
- mCachedAddresses.put(ipServer.interfaceType(), newAddress);
- return newAddress;
}
// No available address.
return null;
}
- private boolean isConflict(final IpPrefix prefix) {
- // Check whether this prefix is in use or conflict with any current upstream network.
- return isDownstreamPrefixInUse(prefix) || isConflictWithUpstream(prefix);
+ private int getPrefixBaseAddress(final IpPrefix prefix) {
+ return inet4AddressToIntHTH((Inet4Address) prefix.getAddress());
}
- /** Get random sub address value. Return value is in 0 ~ 0xffff. */
- @VisibleForTesting
- public int getRandomSubAddr() {
- return ((new Random()).nextInt()) & 0xffff; // subNet is in 0 ~ 0xffff.
+ /**
+ * Check whether input prefix conflict with upstream prefixes or in-use downstream prefixes.
+ * If yes, return one of them.
+ */
+ private IpPrefix getConflictPrefix(final IpPrefix prefix) {
+ final IpPrefix upstream = getConflictWithUpstream(prefix);
+ if (upstream != null) return upstream;
+
+ return getInUseDownstreamPrefix(prefix);
}
- private byte getSanitizedAddressSuffix(final int source, byte... excluded) {
- final byte subId = (byte) (source & BYTE_MASK);
- for (byte value : excluded) {
- if (subId == value) return DEFAULT_ID;
+ // Get the next non-conflict sub prefix. E.g: To get next sub prefix from 10.0.0.0/8, if the
+ // previously selected prefix is 10.20.42.0/24(subPrefix: 0.20.42.0) and the conflicting prefix
+ // is 10.16.0.0/20 (10.16.0.0 ~ 10.16.15.255), then the max address under subPrefix is
+ // 0.16.15.255 and the next subPrefix is 0.16.16.255/24 (0.16.15.255 + 0.0.1.0).
+ // Note: the sub address 0.0.0.255 here is fine to be any value that it will be replaced as
+ // selected random sub address later.
+ private int getNextSubPrefix(final IpPrefix conflictPrefix, final int prefixRangeMask) {
+ final int suffixMask = ~prefixLengthToV4NetmaskIntHTH(conflictPrefix.getPrefixLength());
+ // The largest offset within the prefix assignment block that still conflicts with
+ // conflictPrefix.
+ final int maxConflict =
+ (getPrefixBaseAddress(conflictPrefix) | suffixMask) & ~prefixRangeMask;
+
+ final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH);
+ // Pick a sub prefix a full prefix (1 << (32 - PREFIX_LENGTH) addresses) greater than
+ // maxConflict. This ensures that the selected prefix never overlaps with conflictPrefix.
+ // There is no need to mask the result with PREFIX_LENGTH bits because this is done by
+ // findAvailablePrefixFromRange when it constructs the prefix.
+ return maxConflict + (1 << (32 - PREFIX_LENGTH));
+ }
+
+ private LinkAddress chooseDownstreamAddress(final IpPrefix prefixRange) {
+ // The netmask of the prefix assignment block (e.g., 0xfff00000 for 172.16.0.0/12).
+ final int prefixRangeMask = prefixLengthToV4NetmaskIntHTH(prefixRange.getPrefixLength());
+
+ // The zero address in the block (e.g., 0xac100000 for 172.16.0.0/12).
+ final int baseAddress = getPrefixBaseAddress(prefixRange);
+
+ // The subnet mask corresponding to PREFIX_LENGTH.
+ final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH);
+
+ // The offset within prefixRange of a randomly-selected prefix of length PREFIX_LENGTH.
+ // This may not be the prefix of the address returned by this method:
+ // - If it is already in use, the method will return an address in another prefix.
+ // - If all prefixes within prefixRange are in use, the method will return null. For
+ // example, for a /24 prefix within 172.26.0.0/12, this will be a multiple of 256 in
+ // [0, 1048576). In other words, a random 32-bit number with mask 0x000fff00.
+ //
+ // prefixRangeMask is required to ensure no wrapping. For example, consider:
+ // - prefixRange 127.0.0.0/8
+ // - randomPrefixStart 127.255.255.0
+ // - A conflicting prefix of 127.255.254.0/23
+ // In this case without prefixRangeMask, getNextSubPrefix would return 128.0.0.0, which
+ // means the "start < end" check in findAvailablePrefixFromRange would not reject the prefix
+ // because Java doesn't have unsigned integers, so 128.0.0.0 = 0x80000000 = -2147483648
+ // is less than 127.0.0.0 = 0x7f000000 = 2130706432.
+ //
+ // Additionally, it makes debug output easier to read by making the numbers smaller.
+ final int randomPrefixStart = getRandomInt() & ~prefixRangeMask & prefixMask;
+
+ // A random offset within the prefix. Used to determine the local address once the prefix
+ // is selected. It does not result in an IPv4 address ending in .0, .1, or .255
+ // For a PREFIX_LENGTH of 255, this is a number between 2 and 254.
+ final int subAddress = getSanitizedSubAddr(~prefixMask);
+
+ // Find a prefix length PREFIX_LENGTH between randomPrefixStart and the end of the block,
+ // such that the prefix does not conflict with any upstream.
+ IpPrefix downstreamPrefix = findAvailablePrefixFromRange(
+ randomPrefixStart, (~prefixRangeMask) + 1, baseAddress, prefixRangeMask);
+ if (downstreamPrefix != null) return getLinkAddress(downstreamPrefix, subAddress);
+
+ // If that failed, do the same, but between 0 and randomPrefixStart.
+ downstreamPrefix = findAvailablePrefixFromRange(
+ 0, randomPrefixStart, baseAddress, prefixRangeMask);
+
+ return getLinkAddress(downstreamPrefix, subAddress);
+ }
+
+ private LinkAddress getLinkAddress(final IpPrefix prefix, final int subAddress) {
+ if (prefix == null) return null;
+
+ final InetAddress address = intToInet4AddressHTH(getPrefixBaseAddress(prefix) | subAddress);
+ return new LinkAddress(address, PREFIX_LENGTH);
+ }
+
+ private IpPrefix findAvailablePrefixFromRange(final int start, final int end,
+ final int baseAddress, final int prefixRangeMask) {
+ int newSubPrefix = start;
+ while (newSubPrefix < end) {
+ final InetAddress address = intToInet4AddressHTH(baseAddress | newSubPrefix);
+ final IpPrefix prefix = new IpPrefix(address, PREFIX_LENGTH);
+
+ final IpPrefix conflictPrefix = getConflictPrefix(prefix);
+
+ if (conflictPrefix == null) return prefix;
+
+ newSubPrefix = getNextSubPrefix(conflictPrefix, prefixRangeMask);
}
- return subId;
+ return null;
+ }
+
+ /** Get random int which could be used to generate random address. */
+ @VisibleForTesting
+ public int getRandomInt() {
+ return (new Random()).nextInt();
+ }
+
+ /** Get random subAddress and avoid selecting x.x.x.0, x.x.x.1 and x.x.x.255 address. */
+ private int getSanitizedSubAddr(final int subAddrMask) {
+ final int randomSubAddr = getRandomInt() & subAddrMask;
+ // If prefix length > 30, the selecting speace would be less than 4 which may be hard to
+ // avoid 3 consecutive address.
+ if (PREFIX_LENGTH > 30) return randomSubAddr;
+
+ // TODO: maybe it is not necessary to avoid .0, .1 and .255 address because tethering
+ // address would not be conflicted. This code only works because PREFIX_LENGTH is not longer
+ // than 24
+ final int candidate = randomSubAddr & 0xff;
+ if (candidate == 0 || candidate == 1 || candidate == 255) {
+ return (randomSubAddr & 0xfffffffc) + 2;
+ }
+
+ return randomSubAddr;
}
/** Release downstream record for IpServer. */
@@ -237,14 +337,18 @@
mUpstreamPrefixMap.clear();
}
- private boolean isConflictWithUpstream(final IpPrefix source) {
+ private IpPrefix getConflictWithUpstream(final IpPrefix prefix) {
for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
final List<IpPrefix> list = mUpstreamPrefixMap.valueAt(i);
- for (IpPrefix target : list) {
- if (isConflictPrefix(source, target)) return true;
+ for (IpPrefix upstream : list) {
+ if (isConflictPrefix(prefix, upstream)) return upstream;
}
}
- return false;
+ return null;
+ }
+
+ private boolean isConflictWithUpstream(final IpPrefix prefix) {
+ return getConflictWithUpstream(prefix) != null;
}
private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) {
@@ -257,33 +361,38 @@
// InUse Prefixes are prefixes of mCachedAddresses which are active downstream addresses, last
// downstream addresses(reserved for next time) and static addresses(e.g. bluetooth, wifi p2p).
- private boolean isDownstreamPrefixInUse(final IpPrefix prefix) {
- // This class always generates downstream prefixes with the same prefix length, so
- // prefixes cannot be contained in each other. They can only be equal to each other.
+ private IpPrefix getInUseDownstreamPrefix(final IpPrefix prefix) {
for (int i = 0; i < mCachedAddresses.size(); i++) {
- if (prefix.equals(asIpPrefix(mCachedAddresses.valueAt(i)))) return true;
+ final IpPrefix downstream = asIpPrefix(mCachedAddresses.valueAt(i));
+ if (isConflictPrefix(prefix, downstream)) return downstream;
}
// IpServer may use manually-defined address (mStaticIpv4ServerAddr) which does not include
// in mCachedAddresses.
for (IpServer downstream : mDownstreams) {
final IpPrefix target = getDownstreamPrefix(downstream);
- if (target == null) continue;
- if (isConflictPrefix(prefix, target)) return true;
+ if (isConflictPrefix(prefix, target)) return target;
}
- return false;
+ return null;
}
+ @NonNull
private IpPrefix getDownstreamPrefix(final IpServer downstream) {
final LinkAddress address = downstream.getAddress();
- if (address == null) return null;
return asIpPrefix(address);
}
void dump(final IndentingPrintWriter pw) {
+ pw.println("mTetheringPrefixes:");
+ pw.increaseIndent();
+ for (IpPrefix prefix : mTetheringPrefixes) {
+ pw.println(prefix);
+ }
+ pw.decreaseIndent();
+
pw.println("mUpstreamPrefixMap:");
pw.increaseIndent();
for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 474f4e8..5a0c5b0 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -326,7 +326,7 @@
// It is OK for the configuration to be passed to the PrivateAddressCoordinator at
// construction time because the only part of the configuration it uses is
// shouldEnableWifiP2pDedicatedIp(), and currently do not support changing that.
- mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext, mConfig);
+ mPrivateAddressCoordinator = mDeps.getPrivateAddressCoordinator(mContext, mConfig);
// Must be initialized after tethering configuration is loaded because BpfCoordinator
// constructor needs to use the configuration.
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index 131a5fb..45b9141 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -156,4 +156,12 @@
public boolean isTetheringDenied() {
return TextUtils.equals(SystemProperties.get("ro.tether.denied"), "true");
}
+
+ /**
+ * Get a reference to PrivateAddressCoordinator to be used by Tethering.
+ */
+ public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
+ TetheringConfiguration cfg) {
+ return new PrivateAddressCoordinator(ctx, cfg);
+ }
}
diff --git a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
index 95e36fa..42a91aa 100644
--- a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
+++ b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
@@ -17,9 +17,9 @@
package android.net.ip;
import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static android.system.OsConstants.IPPROTO_TCP;
-import static com.android.internal.util.BitUtils.uint16;
+import static com.android.net.module.util.IpUtils.icmpv6Checksum;
+import static com.android.net.module.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -30,34 +30,29 @@
import android.net.INetd;
import android.net.InetAddresses;
import android.net.MacAddress;
-import android.net.TestNetworkInterface;
-import android.net.TestNetworkManager;
import android.net.util.InterfaceParams;
import android.net.util.TetheringUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
-import android.system.ErrnoException;
-import android.system.Os;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.net.module.util.IpUtils;
import com.android.testutils.TapPacketReader;
+import com.android.testutils.TapPacketReaderRule;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
-import java.io.FileDescriptor;
import java.nio.ByteBuffer;
-import java.util.concurrent.atomic.AtomicReference;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -65,16 +60,18 @@
private static final int DATA_BUFFER_LEN = 4096;
private static final int PACKET_TIMEOUT_MS = 5_000;
- // TODO: make NetworkStackConstants accessible to this test and use the constant from there.
- private static final int ETHER_SRC_ADDR_OFFSET = 6;
+ // Start the readers manually on a common handler shared with DadProxy, for simplicity
+ @Rule
+ public final TapPacketReaderRule mUpstreamReader = new TapPacketReaderRule(
+ DATA_BUFFER_LEN, false /* autoStart */);
+ @Rule
+ public final TapPacketReaderRule mTetheredReader = new TapPacketReaderRule(
+ DATA_BUFFER_LEN, false /* autoStart */);
- private DadProxy mProxy;
- TestNetworkInterface mUpstreamTestIface, mTetheredTestIface;
private InterfaceParams mUpstreamParams, mTetheredParams;
private HandlerThread mHandlerThread;
private Handler mHandler;
private TapPacketReader mUpstreamPacketReader, mTetheredPacketReader;
- private FileDescriptor mUpstreamTapFd, mTetheredTapFd;
private static INetd sNetd;
@@ -106,12 +103,12 @@
@After
public void tearDown() throws Exception {
+ mUpstreamReader.stop();
+ mTetheredReader.stop();
+
if (mHandlerThread != null) {
- mHandler.post(mUpstreamPacketReader::stop); // Also closes the socket
- mHandler.post(mTetheredPacketReader::stop); // Also closes the socket
- mUpstreamTapFd = null;
- mTetheredTapFd = null;
mHandlerThread.quitSafely();
+ mHandlerThread.join(PACKET_TIMEOUT_MS);
}
if (mTetheredParams != null) {
@@ -120,54 +117,20 @@
if (mUpstreamParams != null) {
sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name);
}
-
- if (mUpstreamTestIface != null) {
- try {
- Os.close(mUpstreamTestIface.getFileDescriptor().getFileDescriptor());
- } catch (ErrnoException e) { }
- }
-
- if (mTetheredTestIface != null) {
- try {
- Os.close(mTetheredTestIface.getFileDescriptor().getFileDescriptor());
- } catch (ErrnoException e) { }
- }
- }
-
- private TestNetworkInterface setupTapInterface() {
- final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
- AtomicReference<TestNetworkInterface> iface = new AtomicReference<>();
-
- inst.getUiAutomation().adoptShellPermissionIdentity();
- try {
- final TestNetworkManager tnm = (TestNetworkManager) inst.getContext().getSystemService(
- Context.TEST_NETWORK_SERVICE);
- iface.set(tnm.createTapInterface());
- } finally {
- inst.getUiAutomation().dropShellPermissionIdentity();
- }
-
- return iface.get();
}
private void setupTapInterfaces() {
// Create upstream test iface.
- mUpstreamTestIface = setupTapInterface();
- mUpstreamParams = InterfaceParams.getByName(mUpstreamTestIface.getInterfaceName());
+ mUpstreamReader.start(mHandler);
+ mUpstreamParams = InterfaceParams.getByName(mUpstreamReader.iface.getInterfaceName());
assertNotNull(mUpstreamParams);
- mUpstreamTapFd = mUpstreamTestIface.getFileDescriptor().getFileDescriptor();
- mUpstreamPacketReader = new TapPacketReader(mHandler, mUpstreamTapFd,
- DATA_BUFFER_LEN);
- mHandler.post(mUpstreamPacketReader::start);
+ mUpstreamPacketReader = mUpstreamReader.getReader();
// Create tethered test iface.
- mTetheredTestIface = setupTapInterface();
- mTetheredParams = InterfaceParams.getByName(mTetheredTestIface.getInterfaceName());
+ mTetheredReader.start(mHandler);
+ mTetheredParams = InterfaceParams.getByName(mTetheredReader.getIface().getInterfaceName());
assertNotNull(mTetheredParams);
- mTetheredTapFd = mTetheredTestIface.getFileDescriptor().getFileDescriptor();
- mTetheredPacketReader = new TapPacketReader(mHandler, mTetheredTapFd,
- DATA_BUFFER_LEN);
- mHandler.post(mTetheredPacketReader::start);
+ mTetheredPacketReader = mTetheredReader.getReader();
}
private static final int IPV6_HEADER_LEN = 40;
@@ -177,31 +140,6 @@
private static final int ICMPV6_CHECKSUM_OFFSET = 2;
private static final int ETHER_TYPE_IPV6 = 0x86dd;
- // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
- private static int checksumFold(int sum) {
- while (sum > 0xffff) {
- sum = (sum >> 16) + (sum & 0xffff);
- }
- return sum;
- }
-
- // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
- private static short checksumAdjust(short checksum, short oldWord, short newWord) {
- checksum = (short) ~checksum;
- int tempSum = checksumFold(uint16(checksum) + uint16(newWord) + 0xffff - uint16(oldWord));
- return (short) ~tempSum;
- }
-
- // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
- private static short icmpv6Checksum(ByteBuffer buf, int ipOffset, int transportOffset,
- int transportLen) {
- // The ICMPv6 checksum is the same as the TCP checksum, except the pseudo-header uses
- // 58 (ICMPv6) instead of 6 (TCP). Calculate the TCP checksum, and then do an incremental
- // checksum adjustment for the change in the next header byte.
- short checksum = IpUtils.tcpChecksum(buf, ipOffset, transportOffset, transportLen);
- return checksumAdjust(checksum, (short) IPPROTO_TCP, (short) IPPROTO_ICMPV6);
- }
-
private static ByteBuffer createDadPacket(int type) {
// Refer to buildArpPacket()
int icmpLen = ICMPV6_NA_NS_LEN
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
index 191eb6e..8cb80ba 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -51,6 +51,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.Arrays;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public final class PrivateAddressCoordinatorTest {
@@ -70,7 +73,17 @@
private final Network mWifiNetwork = new Network(1);
private final Network mMobileNetwork = new Network(2);
private final Network mVpnNetwork = new Network(3);
- private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork, mVpnNetwork};
+ private final Network mMobileNetwork2 = new Network(4);
+ private final Network mMobileNetwork3 = new Network(5);
+ private final Network mMobileNetwork4 = new Network(6);
+ private final Network mMobileNetwork5 = new Network(7);
+ private final Network mMobileNetwork6 = new Network(8);
+ private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork, mVpnNetwork,
+ mMobileNetwork2, mMobileNetwork3, mMobileNetwork4, mMobileNetwork5, mMobileNetwork6};
+ private final ArrayList<IpPrefix> mTetheringPrefixes = new ArrayList<>(Arrays.asList(
+ new IpPrefix("192.168.0.0/16"),
+ new IpPrefix("172.16.0.0/12"),
+ new IpPrefix("10.0.0.0/8")));
private void setUpIpServers() throws Exception {
when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB);
@@ -87,27 +100,34 @@
when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks);
when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false);
setUpIpServers();
- mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig));
+ mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig,
+ mTetheringPrefixes));
+ }
+
+ private LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) {
+ final LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
+ ipServer, useLastAddress);
+ when(ipServer.getAddress()).thenReturn(address);
+ return address;
}
@Test
public void testRequestDownstreamAddressWithoutUsingLastAddress() throws Exception {
final IpPrefix bluetoothPrefix = asIpPrefix(mBluetoothAddress);
- final LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
- mHotspotIpServer, false /* useLastAddress */);
+ final LinkAddress address = requestDownstreamAddress(mHotspotIpServer,
+ false /* useLastAddress */);
final IpPrefix hotspotPrefix = asIpPrefix(address);
assertNotEquals(hotspotPrefix, bluetoothPrefix);
- when(mHotspotIpServer.getAddress()).thenReturn(address);
- final LinkAddress newAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
- mHotspotIpServer, false /* useLastAddress */);
+ final LinkAddress newAddress = requestDownstreamAddress(mHotspotIpServer,
+ false /* useLastAddress */);
final IpPrefix testDupRequest = asIpPrefix(newAddress);
assertNotEquals(hotspotPrefix, testDupRequest);
assertNotEquals(bluetoothPrefix, testDupRequest);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
- final LinkAddress usbAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
- mUsbIpServer, false /* useLastAddress */);
+ final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
+ false /* useLastAddress */);
final IpPrefix usbPrefix = asIpPrefix(usbAddress);
assertNotEquals(usbPrefix, bluetoothPrefix);
assertNotEquals(usbPrefix, hotspotPrefix);
@@ -117,30 +137,27 @@
@Test
public void testSanitizedAddress() throws Exception {
int fakeSubAddr = 0x2b00; // 43.0.
- when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
- LinkAddress actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
- mHotspotIpServer, false /* useLastAddress */);
- assertEquals(new LinkAddress("192.168.43.42/24"), actualAddress);
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
+ LinkAddress actualAddress = requestDownstreamAddress(mHotspotIpServer,
+ false /* useLastAddress */);
+ assertEquals(new LinkAddress("192.168.43.2/24"), actualAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
fakeSubAddr = 0x2d01; // 45.1.
- when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
- actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
- mHotspotIpServer, false /* useLastAddress */);
- assertEquals(new LinkAddress("192.168.45.42/24"), actualAddress);
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
+ actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
+ assertEquals(new LinkAddress("192.168.45.2/24"), actualAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
fakeSubAddr = 0x2eff; // 46.255.
- when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
- actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
- mHotspotIpServer, false /* useLastAddress */);
- assertEquals(new LinkAddress("192.168.46.42/24"), actualAddress);
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
+ actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
+ assertEquals(new LinkAddress("192.168.46.254/24"), actualAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
fakeSubAddr = 0x2f05; // 47.5.
- when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
- actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
- mHotspotIpServer, false /* useLastAddress */);
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
+ actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */);
assertEquals(new LinkAddress("192.168.47.5/24"), actualAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
}
@@ -148,29 +165,29 @@
@Test
public void testReservedPrefix() throws Exception {
// - Test bluetooth prefix is reserved.
- when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
getSubAddress(mBluetoothAddress.getAddress().getAddress()));
- final LinkAddress hotspotAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
- mHotspotIpServer, false /* useLastAddress */);
+ final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer,
+ false /* useLastAddress */);
final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddress);
assertNotEquals(asIpPrefix(mBluetoothAddress), hotspotPrefix);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
// - Test previous enabled hotspot prefix(cached prefix) is reserved.
- when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
getSubAddress(hotspotAddress.getAddress().getAddress()));
- final LinkAddress usbAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
- mUsbIpServer, false /* useLastAddress */);
+ final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
+ false /* useLastAddress */);
final IpPrefix usbPrefix = asIpPrefix(usbAddress);
assertNotEquals(asIpPrefix(mBluetoothAddress), usbPrefix);
assertNotEquals(hotspotPrefix, usbPrefix);
mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
// - Test wifi p2p prefix is reserved.
- when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress()));
- final LinkAddress etherAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
- mEthernetIpServer, false /* useLastAddress */);
+ final LinkAddress etherAddress = requestDownstreamAddress(mEthernetIpServer,
+ false /* useLastAddress */);
final IpPrefix etherPrefix = asIpPrefix(etherAddress);
assertNotEquals(asIpPrefix(mLegacyWifiP2pAddress), etherPrefix);
assertNotEquals(asIpPrefix(mBluetoothAddress), etherPrefix);
@@ -180,30 +197,35 @@
@Test
public void testRequestLastDownstreamAddress() throws Exception {
- final int fakeHotspotSubAddr = 0x2b05;
- final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
- when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
- final LinkAddress hotspotAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
- mHotspotIpServer, true /* useLastAddress */);
- assertEquals("Wrong wifi prefix: ", predefinedPrefix, asIpPrefix(hotspotAddress));
- when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddress);
+ final int fakeHotspotSubAddr = 0x2b05; // 43.5
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
+ final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.43.5/24"), hotspotAddress);
- final LinkAddress usbAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
- mUsbIpServer, true /* useLastAddress */);
- assertNotEquals(predefinedPrefix, asIpPrefix(usbAddress));
+ final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.45.5/24"), usbAddress);
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
final int newFakeSubAddr = 0x3c05;
- when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
- final LinkAddress newHotspotAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
- mHotspotIpServer, true /* useLastAddress */);
+ final LinkAddress newHotspotAddress = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
assertEquals(hotspotAddress, newHotspotAddress);
- final LinkAddress newUsbAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
- mUsbIpServer, true /* useLastAddress */);
+ final LinkAddress newUsbAddress = requestDownstreamAddress(mUsbIpServer,
+ true /* useLastAddress */);
assertEquals(usbAddress, newUsbAddress);
+
+ final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
+ new LinkAddress("192.168.88.23/16"), null,
+ makeNetworkCapabilities(TRANSPORT_WIFI));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream);
+ verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+ verify(mUsbIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
}
private UpstreamNetworkState buildUpstreamNetworkState(final Network network,
@@ -229,17 +251,15 @@
@Test
public void testNoConflictUpstreamPrefix() throws Exception {
- final int fakeHotspotSubId = 43;
- final int fakeHotspotSubAddr = 0x2b05;
+ final int fakeHotspotSubAddr = 0x2b05; // 43.5
final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
// Force always get subAddress "43.5" for conflict testing.
- when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
// - Enable hotspot with prefix 192.168.43.0/24
- final LinkAddress hotspotAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
- mHotspotIpServer, true /* useLastAddress */);
+ final LinkAddress hotspotAddr = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddr);
assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix);
- when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr);
// - test mobile network with null NetworkCapabilities. Ideally this should not happen
// because NetworkCapabilities update should always happen before LinkProperties update
// and the UpstreamNetworkState update, just make sure no crash in this case.
@@ -290,28 +310,211 @@
reset(mHotspotIpServer);
// - Restart hotspot again and its prefix is different previous.
mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
- final LinkAddress hotspotAddr2 = mPrivateAddressCoordinator.requestDownstreamAddress(
- mHotspotIpServer, true /* useLastAddress */);
+ final LinkAddress hotspotAddr2 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
final IpPrefix hotspotPrefix2 = asIpPrefix(hotspotAddr2);
assertNotEquals(hotspotPrefix, hotspotPrefix2);
- when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr2);
mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi);
verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
// - Usb tethering can be enabled and its prefix is different with conflict one.
- final LinkAddress usbAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
- mUsbIpServer, true /* useLastAddress */);
+ final LinkAddress usbAddr = requestDownstreamAddress(mUsbIpServer,
+ true /* useLastAddress */);
final IpPrefix usbPrefix = asIpPrefix(usbAddr);
assertNotEquals(predefinedPrefix, usbPrefix);
assertNotEquals(hotspotPrefix2, usbPrefix);
- when(mUsbIpServer.getAddress()).thenReturn(usbAddr);
// - Disable wifi upstream, then wifi's prefix can be selected again.
mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
- final LinkAddress ethAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
- mEthernetIpServer, true /* useLastAddress */);
+ final LinkAddress ethAddr = requestDownstreamAddress(mEthernetIpServer,
+ true /* useLastAddress */);
final IpPrefix ethPrefix = asIpPrefix(ethAddr);
assertEquals(predefinedPrefix, ethPrefix);
}
+ @Test
+ public void testChooseAvailablePrefix() throws Exception {
+ final int randomAddress = 0x8605; // 134.5
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress);
+ final LinkAddress addr0 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.134.5.
+ assertEquals("Wrong prefix: ", new LinkAddress("192.168.134.5/24"), addr0);
+ final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
+ new LinkAddress("192.168.134.13/26"), null,
+ makeNetworkCapabilities(TRANSPORT_WIFI));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream);
+
+ // Check whether return address is next prefix of 192.168.134.0/24.
+ final LinkAddress addr1 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("192.168.135.5/24"), addr1);
+ final UpstreamNetworkState wifiUpstream2 = buildUpstreamNetworkState(mWifiNetwork,
+ new LinkAddress("192.168.149.16/19"), null,
+ makeNetworkCapabilities(TRANSPORT_WIFI));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream2);
+
+
+ // The conflict range is 128 ~ 159, so the address is 192.168.160.5/24.
+ final LinkAddress addr2 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("192.168.160.5/24"), addr2);
+ final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork,
+ new LinkAddress("192.168.129.53/18"), null,
+ makeNetworkCapabilities(TRANSPORT_CELLULAR));
+ // Update another conflict upstream which is covered by the previous one (but not the first
+ // one) and verify whether this would affect the result.
+ final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2,
+ new LinkAddress("192.168.170.7/19"), null,
+ makeNetworkCapabilities(TRANSPORT_CELLULAR));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream);
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream2);
+
+ // The conflict range are 128 ~ 159 and 159 ~ 191, so the address is 192.168.192.5/24.
+ final LinkAddress addr3 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("192.168.192.5/24"), addr3);
+ final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3,
+ new LinkAddress("192.168.188.133/17"), null,
+ makeNetworkCapabilities(TRANSPORT_CELLULAR));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream3);
+
+ // Conflict range: 128 ~ 255. The next available address is 192.168.0.5 because
+ // 192.168.134/24 ~ 192.168.255.255/24 is not available.
+ final LinkAddress addr4 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("192.168.0.5/24"), addr4);
+ final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4,
+ new LinkAddress("192.168.3.59/21"), null,
+ makeNetworkCapabilities(TRANSPORT_CELLULAR));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream4);
+
+ // Conflict ranges: 128 ~ 255 and 0 ~ 7, so the address is 192.168.8.5/24.
+ final LinkAddress addr5 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr5);
+ final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5,
+ new LinkAddress("192.168.68.43/21"), null,
+ makeNetworkCapabilities(TRANSPORT_CELLULAR));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream5);
+
+ // Update an upstream that does *not* conflict, check whether return the same address
+ // 192.168.5/24.
+ final LinkAddress addr6 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr6);
+ final UpstreamNetworkState mobileUpstream6 = buildUpstreamNetworkState(mMobileNetwork6,
+ new LinkAddress("192.168.10.97/21"), null,
+ makeNetworkCapabilities(TRANSPORT_CELLULAR));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream6);
+
+ // Conflict ranges: 0 ~ 15 and 128 ~ 255, so the address is 192.168.16.5/24.
+ final LinkAddress addr7 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("192.168.16.5/24"), addr7);
+ final UpstreamNetworkState mobileUpstream7 = buildUpstreamNetworkState(mMobileNetwork6,
+ new LinkAddress("192.168.0.0/17"), null,
+ makeNetworkCapabilities(TRANSPORT_CELLULAR));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream7);
+
+ // Choose prefix from next range(172.16.0.0/12) when no available prefix in 192.168.0.0/16.
+ final LinkAddress addr8 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("172.16.134.5/24"), addr8);
+ }
+
+ @Test
+ public void testChoosePrefixFromDifferentRanges() throws Exception {
+ final int randomAddress = 0x1f2b2a; // 31.43.42
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress);
+ final LinkAddress classC1 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.43.42.
+ assertEquals("Wrong prefix: ", new LinkAddress("192.168.43.42/24"), classC1);
+ final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
+ new LinkAddress("192.168.88.23/17"), null,
+ makeNetworkCapabilities(TRANSPORT_WIFI));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream);
+ verifyNotifyConflictAndRelease(mHotspotIpServer);
+
+ // Check whether return address is next address of prefix 192.168.128.0/17.
+ final LinkAddress classC2 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("192.168.128.42/24"), classC2);
+ final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork,
+ new LinkAddress("192.1.2.3/8"), null,
+ makeNetworkCapabilities(TRANSPORT_CELLULAR));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream);
+ verifyNotifyConflictAndRelease(mHotspotIpServer);
+
+ // Check whether return address is under prefix 172.16.0.0/12.
+ final LinkAddress classB1 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("172.31.43.42/24"), classB1);
+ final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2,
+ new LinkAddress("172.28.123.100/14"), null,
+ makeNetworkCapabilities(TRANSPORT_CELLULAR));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream2);
+ verifyNotifyConflictAndRelease(mHotspotIpServer);
+
+ // 172.28.0.0 ~ 172.31.255.255 is not available.
+ // Check whether return address is next address of prefix 172.16.0.0/14.
+ final LinkAddress classB2 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("172.16.0.42/24"), classB2);
+
+ // Check whether new downstream is next address of address 172.16.0.42/24.
+ final LinkAddress classB3 = requestDownstreamAddress(mUsbIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("172.16.1.42/24"), classB3);
+ final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3,
+ new LinkAddress("172.16.0.1/24"), null,
+ makeNetworkCapabilities(TRANSPORT_CELLULAR));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream3);
+ verifyNotifyConflictAndRelease(mHotspotIpServer);
+ verify(mUsbIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+
+ // Check whether return address is next address of prefix 172.16.1.42/24.
+ final LinkAddress classB4 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("172.16.2.42/24"), classB4);
+ final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4,
+ new LinkAddress("172.16.0.1/13"), null,
+ makeNetworkCapabilities(TRANSPORT_CELLULAR));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream4);
+ verifyNotifyConflictAndRelease(mHotspotIpServer);
+ verifyNotifyConflictAndRelease(mUsbIpServer);
+
+ // Check whether return address is next address of prefix 172.16.0.1/13.
+ final LinkAddress classB5 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("172.24.0.42/24"), classB5);
+ // Check whether return address is next address of prefix 172.24.0.42/24.
+ final LinkAddress classB6 = requestDownstreamAddress(mUsbIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("172.24.1.42/24"), classB6);
+ final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5,
+ new LinkAddress("172.24.0.1/12"), null,
+ makeNetworkCapabilities(TRANSPORT_CELLULAR));
+ mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream5);
+ verifyNotifyConflictAndRelease(mHotspotIpServer);
+ verifyNotifyConflictAndRelease(mUsbIpServer);
+
+ // Check whether return address is prefix 10.0.0.0/8 + subAddress 0.31.43.42.
+ final LinkAddress classA1 = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("10.31.43.42/24"), classA1);
+ // Check whether new downstream is next address of address 10.31.43.42/24.
+ final LinkAddress classA2 = requestDownstreamAddress(mUsbIpServer,
+ true /* useLastAddress */);
+ assertEquals("Wrong prefix: ", new LinkAddress("10.31.44.42/24"), classA2);
+ }
+
+ private void verifyNotifyConflictAndRelease(final IpServer ipServer) throws Exception {
+ verify(ipServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+ mPrivateAddressCoordinator.releaseDownstream(ipServer);
+ reset(ipServer);
+ setUpIpServers();
+ }
+
private int getSubAddress(final byte... ipv4Address) {
assertEquals(4, ipv4Address.length);
@@ -320,8 +523,8 @@
}
private void assertReseveredWifiP2pPrefix() throws Exception {
- LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
- mHotspotIpServer, true /* useLastAddress */);
+ LinkAddress address = requestDownstreamAddress(mHotspotIpServer,
+ true /* useLastAddress */);
final IpPrefix hotspotPrefix = asIpPrefix(address);
final IpPrefix legacyWifiP2pPrefix = asIpPrefix(mLegacyWifiP2pAddress);
assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix);
@@ -330,7 +533,7 @@
@Test
public void testEnableLegacyWifiP2PAddress() throws Exception {
- when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(
+ when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress()));
// No matter #shouldEnableWifiP2pDedicatedIp() is enabled or not, legacy wifi p2p prefix
// is resevered.
@@ -340,8 +543,8 @@
assertReseveredWifiP2pPrefix();
// If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address.
- LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress(
- mWifiP2pIpServer, true /* useLastAddress */);
+ LinkAddress address = requestDownstreamAddress(mWifiP2pIpServer,
+ true /* useLastAddress */);
assertEquals(mLegacyWifiP2pAddress, address);
mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer);
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index df57020..20e94b2 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -24,6 +24,9 @@
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
@@ -179,6 +182,7 @@
private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
private static final String TEST_NCM_IFNAME = "test_ncm0";
private static final String TEST_ETH_IFNAME = "test_eth0";
+ private static final String TEST_BT_IFNAME = "test_pan0";
private static final String TETHERING_NAME = "Tethering";
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
@@ -230,6 +234,7 @@
private TetheringConfiguration mConfig;
private EntitlementManager mEntitleMgr;
private OffloadController mOffloadCtrl;
+ private PrivateAddressCoordinator mPrivateAddressCoordinator;
private class TestContext extends BroadcastInterceptingContext {
TestContext(Context base) {
@@ -446,6 +451,18 @@
public boolean isTetheringDenied() {
return false;
}
+
+
+ @Override
+ public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
+ TetheringConfiguration cfg) {
+ final ArrayList<IpPrefix> prefixPool = new ArrayList<>(Arrays.asList(
+ new IpPrefix("192.168.0.0/16"),
+ new IpPrefix("172.16.0.0/12"),
+ new IpPrefix("10.0.0.0/8")));
+ mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(ctx, cfg, prefixPool));
+ return mPrivateAddressCoordinator;
+ }
}
private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4,
@@ -1875,27 +1892,36 @@
sendConfigurationChanged();
}
- private static UpstreamNetworkState buildV4WifiUpstreamState(final String ipv4Address,
- final int prefixLength, final Network network) {
+ private static UpstreamNetworkState buildV4UpstreamState(final LinkAddress address,
+ final Network network, final String iface, final int transportType) {
final LinkProperties prop = new LinkProperties();
- prop.setInterfaceName(TEST_WIFI_IFNAME);
+ prop.setInterfaceName(iface);
- prop.addLinkAddress(
- new LinkAddress(InetAddresses.parseNumericAddress(ipv4Address),
- prefixLength));
+ prop.addLinkAddress(address);
final NetworkCapabilities capabilities = new NetworkCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ .addTransportType(transportType);
return new UpstreamNetworkState(prop, capabilities, network);
}
+ private void updateV4Upstream(final LinkAddress ipv4Address, final Network network,
+ final String iface, final int transportType) {
+ final UpstreamNetworkState upstream = buildV4UpstreamState(ipv4Address, network, iface,
+ transportType);
+ mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage(
+ Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK,
+ UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
+ 0,
+ upstream);
+ mLooper.dispatchAll();
+ }
+
@Test
public void testHandleIpConflict() throws Exception {
final Network wifiNetwork = new Network(200);
final Network[] allNetworks = { wifiNetwork };
when(mCm.getAllNetworks()).thenReturn(allNetworks);
- UpstreamNetworkState upstreamNetwork = null;
- runUsbTethering(upstreamNetwork);
+ runUsbTethering(null);
final ArgumentCaptor<InterfaceConfigurationParcel> ifaceConfigCaptor =
ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
verify(mNetd).interfaceSetCfg(ifaceConfigCaptor.capture());
@@ -1903,13 +1929,10 @@
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
reset(mNetd, mUsbManager);
- upstreamNetwork = buildV4WifiUpstreamState(ipv4Address, 30, wifiNetwork);
- mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage(
- Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK,
- UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
- 0,
- upstreamNetwork);
- mLooper.dispatchAll();
+
+ // Cause a prefix conflict by assigning a /30 out of the downstream's /24 to the upstream.
+ updateV4Upstream(new LinkAddress(InetAddresses.parseNumericAddress(ipv4Address), 30),
+ wifiNetwork, TEST_WIFI_IFNAME, TRANSPORT_WIFI);
// verify turn off usb tethering
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
mTethering.interfaceRemoved(TEST_USB_IFNAME);
@@ -1921,9 +1944,10 @@
@Test
public void testNoAddressAvailable() throws Exception {
final Network wifiNetwork = new Network(200);
- final Network[] allNetworks = { wifiNetwork };
+ final Network btNetwork = new Network(201);
+ final Network mobileNetwork = new Network(202);
+ final Network[] allNetworks = { wifiNetwork, btNetwork, mobileNetwork };
when(mCm.getAllNetworks()).thenReturn(allNetworks);
- final String upstreamAddress = "192.168.0.100";
runUsbTethering(null);
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
@@ -1940,13 +1964,13 @@
mLooper.dispatchAll();
reset(mUsbManager, mEm);
- final UpstreamNetworkState upstreamNetwork = buildV4WifiUpstreamState(
- upstreamAddress, 16, wifiNetwork);
- mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage(
- Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK,
- UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
- 0,
- upstreamNetwork);
+ updateV4Upstream(new LinkAddress("192.168.0.100/16"), wifiNetwork, TEST_WIFI_IFNAME,
+ TRANSPORT_WIFI);
+ updateV4Upstream(new LinkAddress("172.16.0.0/12"), btNetwork, TEST_BT_IFNAME,
+ TRANSPORT_BLUETOOTH);
+ updateV4Upstream(new LinkAddress("10.0.0.0/8"), mobileNetwork, TEST_MOBILE_IFNAME,
+ TRANSPORT_CELLULAR);
+
mLooper.dispatchAll();
// verify turn off usb tethering
verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
diff --git a/services/Android.bp b/services/Android.bp
index 33205c0..8c9c487 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -92,6 +92,7 @@
"services.voiceinteraction",
"services.wifi",
"service-blobstore",
+ "service-connectivity",
"service-jobscheduler",
"android.hidl.base-V1.0-java",
],
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 9ddf7a4..e1c4993 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -941,10 +941,19 @@
if (resolvedUserId != mCurrentUserId) {
return null;
}
- if (mA11yWindowManager.findA11yWindowInfoByIdLocked(windowId) == null) {
+ final AccessibilityWindowInfo accessibilityWindowInfo = mA11yWindowManager
+ .findA11yWindowInfoByIdLocked(windowId);
+ if (accessibilityWindowInfo == null) {
return null;
}
- return mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(userId, windowId);
+ // We use AccessibilityWindowInfo#getId instead of windowId. When the windowId comes
+ // from an embedded hierarchy, the system can't find correct window token because
+ // embedded hierarchy doesn't have windowInfo. Calling
+ // AccessibilityWindowManager#findA11yWindowInfoByIdLocked can look for its parent's
+ // windowInfo, so it is safer to use AccessibilityWindowInfo#getId
+ // to get window token to find real window.
+ return mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(userId,
+ accessibilityWindowInfo.getId());
}
}
@@ -2624,6 +2633,7 @@
}
@Override
+ @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
public PendingIntent getPendingIntentActivity(Context context, int requestCode, Intent intent,
int flags) {
return PendingIntent.getActivity(context, requestCode, intent, flags);
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index a860db3..14af8c6 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -19,6 +19,7 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
@@ -27,11 +28,13 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD;
import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP;
@@ -140,6 +143,9 @@
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP, this));
mMultiFingerGestures.add(
+ new MultiFingerMultiTapAndHold(
+ mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD, this));
+ mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP, this));
mMultiFingerGestures.add(
new MultiFingerMultiTapAndHold(
@@ -153,9 +159,17 @@
new MultiFingerMultiTap(mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP, this));
mMultiFingerGestures.add(
new MultiFingerMultiTapAndHold(
+ mContext, 3, 1, GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD, this));
+ mMultiFingerGestures.add(
+ new MultiFingerMultiTapAndHold(
mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD, this));
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this));
+ mMultiFingerGestures.add(
+ new MultiFingerMultiTapAndHold(
+ mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD, this));
+ mMultiFingerGestures.add(
+ new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this));
// Four-finger taps.
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 4, 1, GESTURE_4_FINGER_SINGLE_TAP, this));
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
index e15c495..46b4628 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
@@ -162,6 +162,7 @@
// Accept down only before target number of fingers are down
// or the finger count is not more than target.
if ((currentFingerCount > mTargetFingerCount) || mIsTargetFingerCountReached) {
+ mIsTargetFingerCountReached = false;
cancelGesture(event, rawEvent, policyFlags);
return;
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index d59c955..c563b6c 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -55,6 +55,7 @@
import android.service.autofill.FieldClassification.Match;
import android.service.autofill.FillEventHistory;
import android.service.autofill.FillEventHistory.Event;
+import android.service.autofill.FillEventHistory.Event.NoSaveReason;
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
import android.service.autofill.InlineSuggestionRenderService;
@@ -429,9 +430,15 @@
return;
}
- session.logContextCommitted();
+ final Session.SaveResult saveResult = session.showSaveLocked();
- final boolean finished = session.showSaveLocked();
+ session.logContextCommitted(saveResult.getNoSaveReason());
+
+ if (saveResult.isLogSaveShown()) {
+ session.logSaveUiShown();
+ }
+
+ final boolean finished = saveResult.isRemoveSession();
if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished);
if (finished) {
@@ -868,7 +875,9 @@
@NonNull ComponentName appComponentName, boolean compatMode) {
logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets,
changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
- manuallyFilledDatasetIds, null, null, appComponentName, compatMode);
+ manuallyFilledDatasetIds, /* detectedFieldIdsList= */ null,
+ /* detectedFieldClassificationsList= */ null, appComponentName, compatMode,
+ Event.NO_SAVE_REASON_NONE);
}
@GuardedBy("mLock")
@@ -881,7 +890,8 @@
@Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
@Nullable ArrayList<AutofillId> detectedFieldIdsList,
@Nullable ArrayList<FieldClassification> detectedFieldClassificationsList,
- @NonNull ComponentName appComponentName, boolean compatMode) {
+ @NonNull ComponentName appComponentName, boolean compatMode,
+ @NoSaveReason int saveDialogNotShowReason) {
if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
if (sVerbose) {
Slog.v(TAG, "logContextCommitted() with FieldClassification: id=" + sessionId
@@ -893,7 +903,8 @@
+ ", detectedFieldIds=" + detectedFieldIdsList
+ ", detectedFieldClassifications=" + detectedFieldClassificationsList
+ ", appComponentName=" + appComponentName.toShortString()
- + ", compatMode=" + compatMode);
+ + ", compatMode=" + compatMode
+ + ", saveDialogNotShowReason=" + saveDialogNotShowReason);
}
AutofillId[] detectedFieldsIds = null;
FieldClassification[] detectedFieldClassifications = null;
@@ -929,7 +940,7 @@
clientState, selectedDatasets, ignoredDatasets,
changedFieldIds, changedDatasetIds,
manuallyFilledFieldIds, manuallyFilledDatasetIds,
- detectedFieldsIds, detectedFieldClassifications));
+ detectedFieldsIds, detectedFieldClassifications, saveDialogNotShowReason));
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 4d2e4f6..6c9d35c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -71,6 +71,8 @@
import android.service.autofill.FieldClassification.Match;
import android.service.autofill.FieldClassificationUserData;
import android.service.autofill.FillContext;
+import android.service.autofill.FillEventHistory.Event;
+import android.service.autofill.FillEventHistory.Event.NoSaveReason;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.service.autofill.InlinePresentation;
@@ -1596,10 +1598,22 @@
* when necessary.
*/
public void logContextCommitted() {
- mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this));
+ mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
+ Event.NO_SAVE_REASON_NONE));
}
- private void handleLogContextCommitted() {
+ /**
+ * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}
+ * when necessary.
+ *
+ * @param saveDialogNotShowReason The reason why a save dialog was not shown.
+ */
+ public void logContextCommitted(@NoSaveReason int saveDialogNotShowReason) {
+ mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
+ saveDialogNotShowReason));
+ }
+
+ private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason) {
final FillResponse lastResponse;
synchronized (mLock) {
lastResponse = getLastResponseLocked("logContextCommited(%s)");
@@ -1629,22 +1643,25 @@
// Sets field classification scores
if (userData != null && fcStrategy != null) {
- logFieldClassificationScore(fcStrategy, userData);
+ logFieldClassificationScore(fcStrategy, userData, saveDialogNotShowReason);
} else {
- logContextCommitted(null, null);
+ logContextCommitted(null, null, saveDialogNotShowReason);
}
}
private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds,
- @Nullable ArrayList<FieldClassification> detectedFieldClassifications) {
+ @Nullable ArrayList<FieldClassification> detectedFieldClassifications,
+ @NoSaveReason int saveDialogNotShowReason) {
synchronized (mLock) {
- logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications);
+ logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications,
+ saveDialogNotShowReason);
}
}
@GuardedBy("mLock")
private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds,
- @Nullable ArrayList<FieldClassification> detectedFieldClassifications) {
+ @Nullable ArrayList<FieldClassification> detectedFieldClassifications,
+ @NoSaveReason int saveDialogNotShowReason) {
final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)");
if (lastResponse == null) return;
@@ -1822,10 +1839,10 @@
}
}
- mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
- ignoredDatasets, changedFieldIds, changedDatasetIds,
- manuallyFilledFieldIds, manuallyFilledDatasetIds, detectedFieldIds,
- detectedFieldClassifications, mComponentName, mCompatMode);
+ mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, ignoredDatasets,
+ changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
+ manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
+ mComponentName, mCompatMode, saveDialogNotShowReason);
}
/**
@@ -1833,7 +1850,8 @@
* {@code fieldId} based on its {@code currentValue} and {@code userData}.
*/
private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy,
- @NonNull FieldClassificationUserData userData) {
+ @NonNull FieldClassificationUserData userData,
+ @NoSaveReason int saveDialogNotShowReason) {
final String[] userValues = userData.getValues();
final String[] categoryIds = userData.getCategoryIds();
@@ -1879,7 +1897,7 @@
final RemoteCallback callback = new RemoteCallback((result) -> {
if (result == null) {
if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results");
- logContextCommitted(null, null);
+ logContextCommitted(null, null, saveDialogNotShowReason);
return;
}
final Scores scores = result.getParcelable(EXTRA_SCORES);
@@ -1940,7 +1958,8 @@
return;
}
- logContextCommitted(detectedFieldIds, detectedFieldClassifications);
+ logContextCommitted(detectedFieldIds, detectedFieldClassifications,
+ saveDialogNotShowReason);
});
fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds,
@@ -1948,17 +1967,28 @@
}
/**
+ * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN}
+ * when necessary.
+ *
+ * <p>Note: It is necessary to call logContextCommitted() first before calling this method.
+ */
+ public void logSaveUiShown() {
+ mHandler.sendMessage(obtainMessage(Session::logSaveShown, this));
+ }
+
+ /**
* Shows the save UI, when session can be saved.
*
- * @return {@code true} if session is done and could be removed, or {@code false} if it's
- * pending user action or the service asked to keep it alive (for multi-screens workflow).
+ * @return {@link SaveResult} that contains the save ui display status information.
*/
@GuardedBy("mLock")
- public boolean showSaveLocked() {
+ @NonNull
+ public SaveResult showSaveLocked() {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: "
+ id + " destroyed");
- return false;
+ return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false,
+ Event.NO_SAVE_REASON_NONE);
}
final FillResponse response = getLastResponseLocked("showSaveLocked(%s)");
final SaveInfo saveInfo = response == null ? null : response.getSaveInfo();
@@ -1975,13 +2005,15 @@
*/
if (saveInfo == null) {
if (sVerbose) Slog.v(TAG, "showSaveLocked(" + this.id + "): no saveInfo from service");
- return true;
+ return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+ Event.NO_SAVE_REASON_NO_SAVE_INFO);
}
if ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0) {
// TODO(b/113281366): log metrics
if (sDebug) Slog.v(TAG, "showSaveLocked(" + this.id + "): service asked to delay save");
- return false;
+ return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false,
+ Event.NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG);
}
final ArrayMap<AutofillId, InternalSanitizer> sanitizers = createSanitizers(saveInfo);
@@ -2073,7 +2105,10 @@
Slog.v(TAG, "allRequiredAreNotEmpty: " + allRequiredAreNotEmpty + " hasOptional: "
+ (optionalIds != null));
}
- if (allRequiredAreNotEmpty) {
+ int saveDialogNotShowReason;
+ if (!allRequiredAreNotEmpty) {
+ saveDialogNotShowReason = Event.NO_SAVE_REASON_HAS_EMPTY_REQUIRED;
+ } else {
// Must look up all optional ids in 2 scenarios:
// - if no required id changed but an optional id did, it should trigger save / update
// - if at least one required id changed but it was not part of a filled dataset, we
@@ -2124,7 +2159,9 @@
}
}
}
- if (atLeastOneChanged) {
+ if (!atLeastOneChanged) {
+ saveDialogNotShowReason = Event.NO_SAVE_REASON_NO_VALUE_CHANGED;
+ } else {
if (sDebug) {
Slog.d(TAG, "at least one field changed, validate fields for save UI");
}
@@ -2142,13 +2179,15 @@
Slog.e(TAG, "Not showing save UI because validation failed:", e);
log.setType(MetricsEvent.TYPE_FAILURE);
mMetricsLogger.write(log);
- return true;
+ return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+ Event.NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
}
mMetricsLogger.write(log);
if (!isValid) {
Slog.i(TAG, "not showing save UI because fields failed validation");
- return true;
+ return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+ Event.NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
}
}
@@ -2187,7 +2226,8 @@
Slog.d(TAG, "ignoring Save UI because all fields match contents of "
+ "dataset #" + i + ": " + dataset);
}
- return true;
+ return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+ Event.NO_SAVE_REASON_DATASET_MATCH);
}
}
@@ -2196,9 +2236,6 @@
+ id + "!");
}
- // Use handler so logContextCommitted() is logged first
- mHandler.sendMessage(obtainMessage(Session::logSaveShown, this));
-
final IAutoFillManagerClient client = getClient();
mPendingSaveUi = new PendingUi(new Binder(), id, client);
@@ -2210,8 +2247,10 @@
}
if (serviceLabel == null || serviceIcon == null) {
wtf(null, "showSaveLocked(): no service label or icon");
- return true;
+ return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+ Event.NO_SAVE_REASON_NONE);
}
+
getUiForShowing().showSaveUi(serviceLabel, serviceIcon,
mService.getServicePackageName(), saveInfo, this,
mComponentName, this, mPendingSaveUi, isUpdate, mCompatMode);
@@ -2223,7 +2262,8 @@
}
}
mIsSaving = true;
- return false;
+ return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false,
+ Event.NO_SAVE_REASON_NONE);
}
}
// Nothing changed...
@@ -2232,7 +2272,8 @@
+ "allRequiredAreNotNull=" + allRequiredAreNotEmpty
+ ", atLeastOneChanged=" + atLeastOneChanged);
}
- return true;
+ return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+ saveDialogNotShowReason);
}
private void logSaveShown() {
@@ -3608,6 +3649,97 @@
}
}
+ /**
+ * The result of checking whether to show the save dialog, when session can be saved.
+ *
+ * @hide
+ */
+ static final class SaveResult {
+ /**
+ * Whether to record the save dialog has been shown.
+ */
+ private boolean mLogSaveShown;
+
+ /**
+ * Whether to remove the session.
+ */
+ private boolean mRemoveSession;
+
+ /**
+ * The reason why a save dialog was not shown.
+ */
+ @NoSaveReason private int mSaveDialogNotShowReason;
+
+ SaveResult(boolean logSaveShown, boolean removeSession,
+ @NoSaveReason int saveDialogNotShowReason) {
+ mLogSaveShown = logSaveShown;
+ mRemoveSession = removeSession;
+ mSaveDialogNotShowReason = saveDialogNotShowReason;
+ }
+
+ /**
+ * Returns whether to record the save dialog has been shown.
+ *
+ * @return Whether to record the save dialog has been shown.
+ */
+ public boolean isLogSaveShown() {
+ return mLogSaveShown;
+ }
+
+ /**
+ * Sets whether to record the save dialog has been shown.
+ *
+ * @param logSaveShown Whether to record the save dialog has been shown.
+ */
+ public void setLogSaveShown(boolean logSaveShown) {
+ mLogSaveShown = logSaveShown;
+ }
+
+ /**
+ * Returns whether to remove the session.
+ *
+ * @return Whether to remove the session.
+ */
+ public boolean isRemoveSession() {
+ return mRemoveSession;
+ }
+
+ /**
+ * Sets whether to remove the session.
+ *
+ * @param removeSession Whether to remove the session.
+ */
+ public void setRemoveSession(boolean removeSession) {
+ mRemoveSession = removeSession;
+ }
+
+ /**
+ * Returns the reason why a save dialog was not shown.
+ *
+ * @return The reason why a save dialog was not shown.
+ */
+ @NoSaveReason
+ public int getNoSaveReason() {
+ return mSaveDialogNotShowReason;
+ }
+
+ /**
+ * Sets the reason why a save dialog was not shown.
+ *
+ * @param saveDialogNotShowReason The reason why a save dialog was not shown.
+ */
+ public void setSaveDialogNotShowReason(@NoSaveReason int saveDialogNotShowReason) {
+ mSaveDialogNotShowReason = saveDialogNotShowReason;
+ }
+
+ @Override
+ public String toString() {
+ return "SaveResult: [logSaveShown=" + mLogSaveShown
+ + ", removeSession=" + mRemoveSession
+ + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason + "]";
+ }
+ }
+
@Override
public String toString() {
return "Session: [id=" + id + ", component=" + mComponentName + "]";
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 6157004..032820d 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -391,7 +391,10 @@
checkArgument(getCallingUserId() == userId,
"Must be called by either same user or system");
- mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg);
+ int callingUid = Binder.getCallingUid();
+ if (mAppOpsManager.checkPackage(callingUid, pkg) != AppOpsManager.MODE_ALLOWED) {
+ throw new SecurityException(pkg + " doesn't belong to uid " + callingUid);
+ }
}
@Override
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 26c28d5..1a7f0d1 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -1,6 +1,7 @@
filegroup {
name: "services.core-sources",
srcs: ["java/**/*.java"],
+ exclude_srcs: [":connectivity-service-srcs"],
path: "java",
visibility: [
"//frameworks/base/services",
@@ -122,6 +123,7 @@
"netd_aidl_interfaces-platform-java",
"overlayable_policy_aidl-java",
"SurfaceFlingerProperties",
+ "com.android.sysprop.watchdog",
],
}
@@ -166,3 +168,50 @@
name: "protolog.conf.json.gz",
src: ":services.core.json.gz",
}
+
+// TODO: Move connectivity service sources to independent directory.
+filegroup {
+ name: "connectivity-service-srcs",
+ srcs: [
+ "java/com/android/server/ConnectivityService.java",
+ "java/com/android/server/ConnectivityServiceInitializer.java",
+ "java/com/android/server/TestNetworkService.java",
+ "java/com/android/server/connectivity/AutodestructReference.java",
+ "java/com/android/server/connectivity/ConnectivityConstants.java",
+ "java/com/android/server/connectivity/DataConnectionStats.java",
+ "java/com/android/server/connectivity/DefaultNetworkMetrics.java",
+ "java/com/android/server/connectivity/DnsManager.java",
+ "java/com/android/server/connectivity/IpConnectivityEventBuilder.java",
+ "java/com/android/server/connectivity/IpConnectivityMetrics.java",
+ "java/com/android/server/connectivity/KeepaliveTracker.java",
+ "java/com/android/server/connectivity/LingerMonitor.java",
+ "java/com/android/server/connectivity/MockableSystemProperties.java",
+ "java/com/android/server/connectivity/MultipathPolicyTracker.java",
+ "java/com/android/server/connectivity/Nat464Xlat.java",
+ "java/com/android/server/connectivity/NetdEventListenerService.java",
+ "java/com/android/server/connectivity/NetworkAgentInfo.java",
+ "java/com/android/server/connectivity/NetworkDiagnostics.java",
+ "java/com/android/server/connectivity/NetworkNotificationManager.java",
+ "java/com/android/server/connectivity/NetworkRanker.java",
+ "java/com/android/server/connectivity/PermissionMonitor.java",
+ "java/com/android/server/connectivity/ProxyTracker.java",
+ "java/com/android/server/connectivity/TcpKeepaliveController.java",
+ "java/com/android/server/connectivity/Vpn.java",
+ "java/com/android/server/connectivity/VpnIkev2Utils.java",
+ "java/com/android/server/net/LockdownVpnTracker.java",
+ ],
+}
+
+java_library {
+ name: "service-connectivity",
+ srcs: [
+ ":connectivity-service-srcs",
+ ],
+ installable: true,
+ libs: [
+ "android.net.ipsec.ike",
+ "services.core",
+ "services.net",
+ "unsupportedappusage",
+ ],
+}
diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
index 31cd5d5..4d9680c 100644
--- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
+++ b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
@@ -16,22 +16,14 @@
package com.android.server;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothProfile.ServiceListener;
import android.content.Context;
-import android.content.res.Resources;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.provider.Settings;
import android.util.Log;
-import android.widget.Toast;
-import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
/**
@@ -53,7 +45,7 @@
private final BluetoothManagerService mBluetoothManager;
private final BluetoothAirplaneModeHandler mHandler;
- private AirplaneModeHelper mAirplaneHelper;
+ private BluetoothModeChangeHelper mAirplaneHelper;
@VisibleForTesting int mToastCount = 0;
@@ -97,7 +89,7 @@
* Call after boot complete
*/
@VisibleForTesting
- void start(AirplaneModeHelper helper) {
+ void start(BluetoothModeChangeHelper helper) {
Log.i(TAG, "start");
mAirplaneHelper = helper;
mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT);
@@ -141,118 +133,4 @@
}
return true;
}
-
- /**
- * Helper class that handles callout and callback methods without
- * complex logic.
- */
- @VisibleForTesting
- public static class AirplaneModeHelper {
- private volatile BluetoothA2dp mA2dp;
- private volatile BluetoothHearingAid mHearingAid;
- private final BluetoothAdapter mAdapter;
- private final Context mContext;
-
- AirplaneModeHelper(Context context) {
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- mContext = context;
-
- mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
- mAdapter.getProfileProxy(mContext, mProfileServiceListener,
- BluetoothProfile.HEARING_AID);
- }
-
- private final ServiceListener mProfileServiceListener = new ServiceListener() {
- @Override
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- // Setup Bluetooth profile proxies
- switch (profile) {
- case BluetoothProfile.A2DP:
- mA2dp = (BluetoothA2dp) proxy;
- break;
- case BluetoothProfile.HEARING_AID:
- mHearingAid = (BluetoothHearingAid) proxy;
- break;
- default:
- break;
- }
- }
-
- @Override
- public void onServiceDisconnected(int profile) {
- // Clear Bluetooth profile proxies
- switch (profile) {
- case BluetoothProfile.A2DP:
- mA2dp = null;
- break;
- case BluetoothProfile.HEARING_AID:
- mHearingAid = null;
- break;
- default:
- break;
- }
- }
- };
-
- @VisibleForTesting
- public boolean isA2dpOrHearingAidConnected() {
- return isA2dpConnected() || isHearingAidConnected();
- }
-
- @VisibleForTesting
- public boolean isBluetoothOn() {
- final BluetoothAdapter adapter = mAdapter;
- if (adapter == null) {
- return false;
- }
- return adapter.getLeState() == BluetoothAdapter.STATE_ON;
- }
-
- @VisibleForTesting
- public boolean isAirplaneModeOn() {
- return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
- }
-
- @VisibleForTesting
- public void onAirplaneModeChanged(BluetoothManagerService managerService) {
- managerService.onAirplaneModeChanged();
- }
-
- @VisibleForTesting
- public int getSettingsInt(String name) {
- return Settings.Global.getInt(mContext.getContentResolver(),
- name, 0);
- }
-
- @VisibleForTesting
- public void setSettingsInt(String name, int value) {
- Settings.Global.putInt(mContext.getContentResolver(),
- name, value);
- }
-
- @VisibleForTesting
- public void showToastMessage() {
- Resources r = mContext.getResources();
- final CharSequence text = r.getString(
- R.string.bluetooth_airplane_mode_toast, 0);
- Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
- }
-
- private boolean isA2dpConnected() {
- final BluetoothA2dp a2dp = mA2dp;
- if (a2dp == null) {
- return false;
- }
- return a2dp.getConnectedDevices().size() > 0;
- }
-
- private boolean isHearingAidConnected() {
- final BluetoothHearingAid hearingAid = mHearingAid;
- if (hearingAid == null) {
- return false;
- }
- return hearingAid.getConnectedDevices().size() > 0;
- }
- };
}
diff --git a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
new file mode 100644
index 0000000..2dcf82f
--- /dev/null
+++ b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java
@@ -0,0 +1,64 @@
+/*
+ * 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 com.android.server;
+
+import android.provider.DeviceConfig;
+
+/**
+ * The BluetoothDeviceConfigListener handles system device config change callback and checks
+ * whether we need to inform BluetoothManagerService on this change.
+ *
+ * The information of device config change would not be passed to the BluetoothManagerService
+ * when Bluetooth is on and Bluetooth is in one of the following situations:
+ * 1. Bluetooth A2DP is connected.
+ * 2. Bluetooth Hearing Aid profile is connected.
+ */
+class BluetoothDeviceConfigListener {
+ private static final String TAG = "BluetoothDeviceConfigListener";
+
+ BluetoothManagerService mService;
+
+ BluetoothDeviceConfigListener(BluetoothManagerService service) {
+ mService = service;
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_BLUETOOTH,
+ (Runnable r) -> r.run(),
+ mDeviceConfigChangedListener);
+ }
+
+ private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener =
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) {
+ return;
+ }
+ boolean foundInit = false;
+ for (String name : properties.getKeyset()) {
+ if (name.startsWith("INIT_")) {
+ foundInit = true;
+ break;
+ }
+ }
+ if (!foundInit) {
+ return;
+ }
+ mService.onInitFlagsChanged();
+ }
+ };
+
+}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 011231c..f6a29aa 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -23,7 +23,9 @@
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
import android.bluetooth.IBluetooth;
@@ -63,7 +65,6 @@
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
@@ -118,6 +119,7 @@
// Delay for retrying enable and disable in msec
private static final int ENABLE_DISABLE_DELAY_MS = 300;
private static final int DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS = 300;
+ private static final int DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS = 86400;
private static final int MESSAGE_ENABLE = 1;
private static final int MESSAGE_DISABLE = 2;
@@ -178,8 +180,12 @@
private int mWaitForEnableRetry;
private int mWaitForDisableRetry;
+ private BluetoothModeChangeHelper mBluetoothModeChangeHelper;
+
private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
+ private BluetoothDeviceConfigListener mBluetoothDeviceConfigListener;
+
// used inside handler thread
private boolean mQuietEnable = false;
private boolean mEnable;
@@ -284,29 +290,13 @@
}
};
- private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener =
- new DeviceConfig.OnPropertiesChangedListener() {
- @Override
- public void onPropertiesChanged(DeviceConfig.Properties properties) {
- if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) {
- return;
- }
- boolean foundInit = false;
- for (String name : properties.getKeyset()) {
- if (name.startsWith("INIT_")) {
- foundInit = true;
- break;
- }
- }
- if (!foundInit) {
- return;
- }
- mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
- mHandler.sendEmptyMessageDelayed(
- MESSAGE_INIT_FLAGS_CHANGED,
- DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS);
- }
- };
+ @VisibleForTesting
+ public void onInitFlagsChanged() {
+ mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
+ mHandler.sendEmptyMessageDelayed(
+ MESSAGE_INIT_FLAGS_CHANGED,
+ DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS);
+ }
public boolean onFactoryReset() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
@@ -457,6 +447,15 @@
mHandler.sendMessage(msg);
}
}
+ } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)
+ || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
+ final int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
+ BluetoothProfile.STATE_CONNECTED);
+ if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED)
+ && state == BluetoothProfile.STATE_DISCONNECTED
+ && !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+ onInitFlagsChanged();
+ }
}
}
};
@@ -508,6 +507,8 @@
filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
filter.addAction(Intent.ACTION_SETTING_RESTORED);
+ filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+ filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiver(mReceiver, filter);
@@ -542,10 +543,6 @@
Slog.w(TAG, "Unable to resolve SystemUI's UID.");
}
mSystemUiUid = systemUiUid;
- DeviceConfig.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_BLUETOOTH,
- (Runnable r) -> r.run(),
- mDeviceConfigChangedListener);
}
/**
@@ -1383,10 +1380,12 @@
Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
mHandler.sendMessage(getMsg);
}
+
+ mBluetoothModeChangeHelper = new BluetoothModeChangeHelper(mContext);
if (mBluetoothAirplaneModeListener != null) {
- mBluetoothAirplaneModeListener.start(
- new BluetoothAirplaneModeListener.AirplaneModeHelper(mContext));
+ mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper);
}
+ mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this);
}
/**
@@ -2229,6 +2228,12 @@
Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED");
}
mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED);
+ if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) {
+ mHandler.sendEmptyMessageDelayed(
+ MESSAGE_INIT_FLAGS_CHANGED,
+ DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS);
+ break;
+ }
if (mBluetooth != null && isEnabled()) {
restartForReason(
BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED);
diff --git a/services/core/java/com/android/server/BluetoothModeChangeHelper.java b/services/core/java/com/android/server/BluetoothModeChangeHelper.java
new file mode 100644
index 0000000..242fa84
--- /dev/null
+++ b/services/core/java/com/android/server/BluetoothModeChangeHelper.java
@@ -0,0 +1,143 @@
+/*
+ * 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 com.android.server;
+
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+import android.widget.Toast;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Helper class that handles callout and callback methods without
+ * complex logic.
+ */
+public class BluetoothModeChangeHelper {
+ private volatile BluetoothA2dp mA2dp;
+ private volatile BluetoothHearingAid mHearingAid;
+ private final BluetoothAdapter mAdapter;
+ private final Context mContext;
+
+ BluetoothModeChangeHelper(Context context) {
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mContext = context;
+
+ mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
+ mAdapter.getProfileProxy(mContext, mProfileServiceListener,
+ BluetoothProfile.HEARING_AID);
+ }
+
+ private final ServiceListener mProfileServiceListener = new ServiceListener() {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ // Setup Bluetooth profile proxies
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dp = (BluetoothA2dp) proxy;
+ break;
+ case BluetoothProfile.HEARING_AID:
+ mHearingAid = (BluetoothHearingAid) proxy;
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ // Clear Bluetooth profile proxies
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dp = null;
+ break;
+ case BluetoothProfile.HEARING_AID:
+ mHearingAid = null;
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ @VisibleForTesting
+ public boolean isA2dpOrHearingAidConnected() {
+ return isA2dpConnected() || isHearingAidConnected();
+ }
+
+ @VisibleForTesting
+ public boolean isBluetoothOn() {
+ final BluetoothAdapter adapter = mAdapter;
+ if (adapter == null) {
+ return false;
+ }
+ return adapter.getLeState() == BluetoothAdapter.STATE_ON;
+ }
+
+ @VisibleForTesting
+ public boolean isAirplaneModeOn() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+ }
+
+ @VisibleForTesting
+ public void onAirplaneModeChanged(BluetoothManagerService managerService) {
+ managerService.onAirplaneModeChanged();
+ }
+
+ @VisibleForTesting
+ public int getSettingsInt(String name) {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ name, 0);
+ }
+
+ @VisibleForTesting
+ public void setSettingsInt(String name, int value) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ name, value);
+ }
+
+ @VisibleForTesting
+ public void showToastMessage() {
+ Resources r = mContext.getResources();
+ final CharSequence text = r.getString(
+ R.string.bluetooth_airplane_mode_toast, 0);
+ Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
+ }
+
+ private boolean isA2dpConnected() {
+ final BluetoothA2dp a2dp = mA2dp;
+ if (a2dp == null) {
+ return false;
+ }
+ return a2dp.getConnectedDevices().size() > 0;
+ }
+
+ private boolean isHearingAidConnected() {
+ final BluetoothHearingAid hearingAid = mHearingAid;
+ if (hearingAid == null) {
+ return false;
+ }
+ return hearingAid.getConnectedDevices().size() > 0;
+ }
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b59f7645..35f1194 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -140,6 +140,7 @@
import android.net.util.LinkPropertiesUtils.CompareResult;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
+import android.os.BasicShellCommandHandler;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -156,11 +157,8 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
-import android.os.ShellCallback;
-import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -238,7 +236,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
@@ -2303,10 +2300,21 @@
}
/**
- * Called when the system is ready and ConnectivityService can initialize remaining components.
+ * Called by SystemServer through ConnectivityManager when the system is ready.
+ */
+ @Override
+ public void systemReady() {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Calling Uid is not system uid.");
+ }
+ systemReadyInternal();
+ }
+
+ /**
+ * Called when ConnectivityService can initialize remaining components.
*/
@VisibleForTesting
- public void systemReady() {
+ public void systemReadyInternal() {
// Let PermissionMonitor#startMonitoring() running in the beginning of the systemReady
// before MultipathPolicyTracker.start(). Since mApps in PermissionMonitor needs to be
// populated first to ensure that listening network request which is sent by
@@ -6194,20 +6202,12 @@
return; // no updating necessary
}
- final NetworkAgentInfo defaultNai = getDefaultNetwork();
- final boolean isDefaultNetwork = (defaultNai != null && defaultNai.network.netId == netId);
-
if (DBG) {
final Collection<InetAddress> dnses = newLp.getDnsServers();
log("Setting DNS servers for network " + netId + " to " + dnses);
}
try {
mDnsManager.noteDnsServersForNetwork(netId, newLp);
- // TODO: netd should listen on [::1]:53 and proxy queries to the current
- // default network, and we should just set net.dns1 to ::1, not least
- // because applications attempting to use net.dns resolvers will bypass
- // the privacy protections of things like DNS-over-TLS.
- if (isDefaultNetwork) mDnsManager.setDefaultDnsSystemProperties(newLp.getDnsServers());
mDnsManager.flushVmDnsCache();
} catch (Exception e) {
loge("Exception in setDnsConfigurationForNetwork: " + e);
@@ -6722,8 +6722,6 @@
? newNetwork.linkProperties.getHttpProxy() : null);
updateTcpBufferSizes(null != newNetwork
? newNetwork.linkProperties.getTcpBufferSizes() : null);
- mDnsManager.setDefaultDnsSystemProperties(null != newNetwork
- ? newNetwork.linkProperties.getDnsServers() : Collections.EMPTY_LIST);
notifyIfacesChangedForNetworkStats();
// Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
updateAllVpnsCapabilities();
@@ -7658,14 +7656,14 @@
}
@Override
- public void onShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
- FileDescriptor err, @NonNull String[] args, ShellCallback callback,
- @NonNull ResultReceiver resultReceiver) {
- (new ShellCmd()).exec(this, in, out, err, args, callback, resultReceiver);
+ public int handleShellCommand(@NonNull ParcelFileDescriptor in,
+ @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+ @NonNull String[] args) {
+ return new ShellCmd().exec(this, in.getFileDescriptor(), out.getFileDescriptor(),
+ err.getFileDescriptor(), args);
}
- private class ShellCmd extends ShellCommand {
-
+ private class ShellCmd extends BasicShellCommandHandler {
@Override
public int onCommand(String cmd) {
if (cmd == null) {
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
new file mode 100644
index 0000000..2bc8925
--- /dev/null
+++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
+
+import android.content.Context;
+import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
+import android.os.INetworkManagementService;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * Connectivity service initializer for core networking. This is called by system server to create
+ * a new instance of ConnectivityService.
+ */
+public final class ConnectivityServiceInitializer extends SystemService {
+ private static final String TAG = ConnectivityServiceInitializer.class.getSimpleName();
+ private final ConnectivityService mConnectivity;
+
+ public ConnectivityServiceInitializer(Context context) {
+ super(context);
+ // TODO: Define formal APIs to get the needed services.
+ mConnectivity = new ConnectivityService(context, getNetworkManagementService(),
+ getNetworkStatsService(), getNetworkPolicyManager());
+ }
+
+ @Override
+ public void onStart() {
+ Log.i(TAG, "Registering " + Context.CONNECTIVITY_SERVICE);
+ publishBinderService(Context.CONNECTIVITY_SERVICE, mConnectivity,
+ /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+ }
+
+ private INetworkManagementService getNetworkManagementService() {
+ return INetworkManagementService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
+ }
+
+ private INetworkStatsService getNetworkStatsService() {
+ return INetworkStatsService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ }
+
+ private INetworkPolicyManager getNetworkPolicyManager() {
+ return INetworkPolicyManager.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+ }
+
+}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index b3d4085..20f68da 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -38,7 +38,6 @@
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
-import android.telecom.TelecomManager;
import android.util.MutableBoolean;
import android.util.Slog;
import android.view.KeyEvent;
@@ -46,7 +45,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -430,11 +428,18 @@
mPowerButtonConsecutiveTaps++;
mPowerButtonSlowConsecutiveTaps++;
}
- if (mPanicButtonGestureEnabled
- && mPowerButtonConsecutiveTaps == PANIC_POWER_TAP_COUNT_THRESHOLD) {
- launchPanic = true;
- intercept = interactive;
- } else if (mCameraDoubleTapPowerEnabled
+ // Check if we need to launch camera or panic flows
+ if (mPanicButtonGestureEnabled) {
+ // Commit to intercepting the powerkey event after the second "quick" tap to avoid
+ // lockscreen changes between launching camera and the panic flow.
+ if (mPowerButtonConsecutiveTaps > 1) {
+ intercept = interactive;
+ }
+ if (mPowerButtonConsecutiveTaps == PANIC_POWER_TAP_COUNT_THRESHOLD) {
+ launchPanic = true;
+ }
+ }
+ if (mCameraDoubleTapPowerEnabled
&& powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
&& mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) {
launchCamera = true;
@@ -464,8 +469,11 @@
mMetricsLogger.histogram("power_consecutive_short_tap_count",
mPowerButtonSlowConsecutiveTaps);
mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);
+
outLaunched.value = launchCamera || launchPanic;
- return intercept && (launchCamera || launchPanic);
+ // Intercept power key event if the press is part of a gesture (camera, panic) and the user
+ // has completed setup.
+ return intercept && isUserSetupComplete();
}
/**
@@ -475,8 +483,7 @@
boolean handleCameraGesture(boolean useWakelock, int source) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "GestureLauncher:handleCameraGesture");
try {
- boolean userSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+ boolean userSetupComplete = isUserSetupComplete();
if (!userSetupComplete) {
if (DBG) {
Slog.d(TAG, String.format(
@@ -514,8 +521,7 @@
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"GestureLauncher:handlePanicButtonGesture");
try {
- boolean userSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+ boolean userSetupComplete = isUserSetupComplete();
if (!userSetupComplete) {
if (DBG) {
Slog.d(TAG, String.format(
@@ -529,21 +535,20 @@
"userSetupComplete = %s, performing panic gesture.",
userSetupComplete));
}
- // TODO(b/160006048): Not all devices have telephony. Check system feature first.
- TelecomManager telecomManager = (TelecomManager) mContext.getSystemService(
- Context.TELECOM_SERVICE);
- mContext.startActivity(telecomManager.createLaunchEmergencyDialerIntent(null).addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_SINGLE_TOP).putExtra(
- "com.android.phone.EmergencyDialer.extra.ENTRY_TYPE",
- 2)); // 2 maps to power button, forcing into fast emergency dialer experience.
+ StatusBarManagerInternal service = LocalServices.getService(
+ StatusBarManagerInternal.class);
+ service.onEmergencyActionLaunchGestureDetected();
return true;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
+ private boolean isUserSetupComplete() {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+ }
+
private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 0c34744..afddd65 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -33,7 +33,6 @@
import android.hardware.input.InputManager;
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.V1_0.EffectStrength;
-import android.icu.text.DateFormat;
import android.media.AudioManager;
import android.os.BatteryStats;
import android.os.Binder;
@@ -80,6 +79,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@@ -90,6 +90,8 @@
public class VibratorService extends IVibratorService.Stub
implements InputManager.InputDeviceListener {
private static final String TAG = "VibratorService";
+ private static final SimpleDateFormat DEBUG_DATE_FORMAT =
+ new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
private static final boolean DEBUG = false;
private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
@@ -126,7 +128,7 @@
private final LinkedList<VibrationInfo> mPreviousRingVibrations;
private final LinkedList<VibrationInfo> mPreviousNotificationVibrations;
private final LinkedList<VibrationInfo> mPreviousAlarmVibrations;
- private final LinkedList<ExternalVibration> mPreviousExternalVibrations;
+ private final LinkedList<VibrationInfo> mPreviousExternalVibrations;
private final LinkedList<VibrationInfo> mPreviousVibrations;
private final int mPreviousVibrationsLimit;
private final boolean mAllowPriorityVibrationsInLowPowerMode;
@@ -162,7 +164,7 @@
@GuardedBy("mLock")
private Vibration mCurrentVibration;
private int mCurVibUid = -1;
- private ExternalVibration mCurrentExternalVibration;
+ private ExternalVibrationHolder mCurrentExternalVibration;
private boolean mVibratorUnderExternalControl;
private boolean mLowPowerMode;
@GuardedBy("mLock")
@@ -231,19 +233,12 @@
void onComplete(long vibrationId);
}
- /**
- * Holder for a vibration to be played. This class can be shared with native methods for
- * hardware callback support.
- */
+ /** Holder for a {@link VibrationEffect}. */
private final class Vibration implements IBinder.DeathRecipient {
public final IBinder token;
// Start time in CLOCK_BOOTTIME base.
public final long startTime;
- // Start time in unix epoch time. Only to be used for debugging purposes and to correlate
- // with other system events, any duration calculations should be done use startTime so as
- // not to be affected by discontinuities created by RTC adjustments.
- public final long startTimeDebug;
public final VibrationAttributes attrs;
public final long id;
public final int uid;
@@ -255,18 +250,28 @@
// The original effect that was requested. Typically these two things differ because
// the effect was scaled based on the users vibration intensity settings.
public VibrationEffect originalEffect;
+ // The scale applied to the original effect.
+ public float scale;
+
+ // Start/end times in unix epoch time. Only to be used for debugging purposes and to
+ // correlate with other system events, any duration calculations should be done use
+ // startTime so as not to be affected by discontinuities created by RTC adjustments.
+ private final long mStartTimeDebug;
+ private long mEndTimeDebug;
+ private VibrationInfo.Status mStatus;
private Vibration(IBinder token, VibrationEffect effect,
VibrationAttributes attrs, int uid, String opPkg, String reason) {
this.token = token;
- this.id = mNextVibrationId.getAndIncrement();
this.effect = effect;
+ this.id = mNextVibrationId.getAndIncrement();
this.startTime = SystemClock.elapsedRealtime();
- this.startTimeDebug = System.currentTimeMillis();
this.attrs = attrs;
this.uid = uid;
this.opPkg = opPkg;
this.reason = reason;
+ mStartTimeDebug = System.currentTimeMillis();
+ mStatus = VibrationInfo.Status.RUNNING;
}
@Override
@@ -276,11 +281,24 @@
if (DEBUG) {
Slog.d(TAG, "Vibration finished because binder died, cleaning up");
}
- doCancelVibrateLocked();
+ doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
}
}
}
+ public void end(VibrationInfo.Status status) {
+ if (hasEnded()) {
+ // Vibration already ended, keep first ending status set and ignore this one.
+ return;
+ }
+ mStatus = status;
+ mEndTimeDebug = System.currentTimeMillis();
+ }
+
+ public boolean hasEnded() {
+ return mStatus != VibrationInfo.Status.RUNNING;
+ }
+
public boolean hasTimeoutLongerThan(long millis) {
final long duration = effect.getDuration();
return duration >= 0 && duration > millis;
@@ -308,40 +326,109 @@
public VibrationInfo toInfo() {
return new VibrationInfo(
- startTimeDebug, effect, originalEffect, attrs, uid, opPkg, reason);
+ mStartTimeDebug, mEndTimeDebug, effect, originalEffect, scale, attrs,
+ uid, opPkg, reason, mStatus);
}
}
- private static class VibrationInfo {
+ /** Holder for a {@link ExternalVibration}. */
+ private final class ExternalVibrationHolder {
+
+ public final ExternalVibration externalVibration;
+ public int scale;
+
private final long mStartTimeDebug;
+ private long mEndTimeDebug;
+ private VibrationInfo.Status mStatus;
+
+ private ExternalVibrationHolder(ExternalVibration externalVibration) {
+ this.externalVibration = externalVibration;
+ this.scale = SCALE_NONE;
+ mStartTimeDebug = System.currentTimeMillis();
+ mStatus = VibrationInfo.Status.RUNNING;
+ }
+
+ public void end(VibrationInfo.Status status) {
+ if (mStatus != VibrationInfo.Status.RUNNING) {
+ // Vibration already ended, keep first ending status set and ignore this one.
+ return;
+ }
+ mStatus = status;
+ mEndTimeDebug = System.currentTimeMillis();
+ }
+
+ public VibrationInfo toInfo() {
+ return new VibrationInfo(
+ mStartTimeDebug, mEndTimeDebug, /* effect= */ null, /* originalEffect= */ null,
+ scale, externalVibration.getVibrationAttributes(),
+ externalVibration.getUid(), externalVibration.getPackage(),
+ /* reason= */ null, mStatus);
+ }
+ }
+
+ /** Debug information about vibrations. */
+ private static class VibrationInfo {
+
+ public enum Status {
+ RUNNING,
+ FINISHED,
+ CANCELLED,
+ ERROR_APP_OPS,
+ IGNORED,
+ IGNORED_APP_OPS,
+ IGNORED_BACKGROUND,
+ IGNORED_RINGTONE,
+ IGNORED_UNKNOWN_VIBRATION,
+ IGNORED_UNSUPPORTED,
+ IGNORED_FOR_ALARM,
+ IGNORED_FOR_EXTERNAL,
+ IGNORED_FOR_ONGOING,
+ IGNORED_FOR_POWER,
+ IGNORED_FOR_SETTINGS,
+ }
+
+ private final long mStartTimeDebug;
+ private final long mEndTimeDebug;
private final VibrationEffect mEffect;
private final VibrationEffect mOriginalEffect;
+ private final float mScale;
private final VibrationAttributes mAttrs;
private final int mUid;
private final String mOpPkg;
private final String mReason;
+ private final VibrationInfo.Status mStatus;
- VibrationInfo(long startTimeDebug, VibrationEffect effect,
- VibrationEffect originalEffect, VibrationAttributes attrs, int uid,
- String opPkg, String reason) {
+ VibrationInfo(long startTimeDebug, long endTimeDebug, VibrationEffect effect,
+ VibrationEffect originalEffect, float scale, VibrationAttributes attrs,
+ int uid, String opPkg, String reason, VibrationInfo.Status status) {
mStartTimeDebug = startTimeDebug;
+ mEndTimeDebug = endTimeDebug;
mEffect = effect;
mOriginalEffect = originalEffect;
+ mScale = scale;
mAttrs = attrs;
mUid = uid;
mOpPkg = opPkg;
mReason = reason;
+ mStatus = status;
}
@Override
public String toString() {
return new StringBuilder()
.append("startTime: ")
- .append(DateFormat.getDateTimeInstance().format(new Date(mStartTimeDebug)))
+ .append(DEBUG_DATE_FORMAT.format(new Date(mStartTimeDebug)))
+ .append(", endTime: ")
+ .append(mEndTimeDebug == 0 ? null
+ : DEBUG_DATE_FORMAT.format(new Date(mEndTimeDebug)))
+ .append(", status: ")
+ .append(mStatus.name().toLowerCase())
.append(", effect: ")
.append(mEffect)
.append(", originalEffect: ")
.append(mOriginalEffect)
+ .append(", scale: ")
+ .append(String.format("%.2f", mScale))
.append(", attrs: ")
.append(mAttrs)
.append(", uid: ")
@@ -354,11 +441,80 @@
}
void dumpProto(ProtoOutputStream proto, long fieldId) {
- synchronized (this) {
- final long token = proto.start(fieldId);
- proto.write(VibrationProto.START_TIME, mStartTimeDebug);
- proto.end(token);
+ final long token = proto.start(fieldId);
+ proto.write(VibrationProto.START_TIME, mStartTimeDebug);
+ proto.write(VibrationProto.END_TIME, mEndTimeDebug);
+ proto.write(VibrationProto.STATUS, mStatus.ordinal());
+
+ final long attrsToken = proto.start(VibrationProto.ATTRIBUTES);
+ proto.write(VibrationAttributesProto.USAGE, mAttrs.getUsage());
+ proto.write(VibrationAttributesProto.AUDIO_USAGE, mAttrs.getAudioUsage());
+ proto.write(VibrationAttributesProto.FLAGS, mAttrs.getFlags());
+ proto.end(attrsToken);
+
+ if (mEffect != null) {
+ dumpEffect(proto, VibrationProto.EFFECT, mEffect);
}
+ if (mOriginalEffect != null) {
+ dumpEffect(proto, VibrationProto.ORIGINAL_EFFECT, mOriginalEffect);
+ }
+
+ proto.end(token);
+ }
+
+ private void dumpEffect(ProtoOutputStream proto, long fieldId, VibrationEffect effect) {
+ final long token = proto.start(fieldId);
+ if (effect instanceof VibrationEffect.OneShot) {
+ dumpEffect(proto, VibrationEffectProto.ONESHOT, (VibrationEffect.OneShot) effect);
+ } else if (effect instanceof VibrationEffect.Waveform) {
+ dumpEffect(proto, VibrationEffectProto.WAVEFORM, (VibrationEffect.Waveform) effect);
+ } else if (effect instanceof VibrationEffect.Prebaked) {
+ dumpEffect(proto, VibrationEffectProto.PREBAKED, (VibrationEffect.Prebaked) effect);
+ } else if (effect instanceof VibrationEffect.Composed) {
+ dumpEffect(proto, VibrationEffectProto.COMPOSED, (VibrationEffect.Composed) effect);
+ }
+ proto.end(token);
+ }
+
+ private void dumpEffect(ProtoOutputStream proto, long fieldId,
+ VibrationEffect.OneShot effect) {
+ final long token = proto.start(fieldId);
+ proto.write(OneShotProto.DURATION, (int) effect.getDuration());
+ proto.write(OneShotProto.AMPLITUDE, effect.getAmplitude());
+ proto.end(token);
+ }
+
+ private void dumpEffect(ProtoOutputStream proto, long fieldId,
+ VibrationEffect.Waveform effect) {
+ final long token = proto.start(fieldId);
+ for (long timing : effect.getTimings()) {
+ proto.write(WaveformProto.TIMINGS, (int) timing);
+ }
+ for (int amplitude : effect.getAmplitudes()) {
+ proto.write(WaveformProto.AMPLITUDES, amplitude);
+ }
+ proto.write(WaveformProto.REPEAT, effect.getRepeatIndex() >= 0);
+ proto.end(token);
+ }
+
+ private void dumpEffect(ProtoOutputStream proto, long fieldId,
+ VibrationEffect.Prebaked effect) {
+ final long token = proto.start(fieldId);
+ proto.write(PrebakedProto.EFFECT_ID, effect.getId());
+ proto.write(PrebakedProto.EFFECT_STRENGTH, effect.getEffectStrength());
+ proto.write(PrebakedProto.FALLBACK, effect.shouldFallback());
+ proto.end(token);
+ }
+
+ private void dumpEffect(ProtoOutputStream proto, long fieldId,
+ VibrationEffect.Composed effect) {
+ final long token = proto.start(fieldId);
+ for (PrimitiveEffect primitive : effect.getPrimitiveEffects()) {
+ proto.write(ComposedProto.EFFECT_IDS, primitive.id);
+ proto.write(ComposedProto.EFFECT_SCALES, primitive.scale);
+ proto.write(ComposedProto.DELAYS, primitive.delay);
+ }
+ proto.end(token);
}
}
@@ -549,7 +705,7 @@
if (DEBUG) {
Slog.d(TAG, "Vibration finished by callback, cleaning up");
}
- doCancelVibrateLocked();
+ doCancelVibrateLocked(VibrationInfo.Status.FINISHED);
}
}
}
@@ -792,6 +948,7 @@
}
attrs = fixupVibrationAttributes(attrs);
+ Vibration vib = new Vibration(token, effect, attrs, uid, opPkg, reason);
// If our current vibration is longer than the new vibration and is the same amplitude,
// then just let the current one finish.
@@ -808,6 +965,7 @@
Slog.d(TAG,
"Ignoring incoming vibration in favor of current vibration");
}
+ endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_ONGOING);
return;
}
}
@@ -819,6 +977,7 @@
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
}
+ endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_EXTERNAL);
return;
}
@@ -832,24 +991,29 @@
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
}
+ endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_ALARM);
return;
}
- Vibration vib = new Vibration(token, effect, attrs, uid, opPkg, reason);
if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
> ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
&& !vib.isNotification() && !vib.isRingtone() && !vib.isAlarm()) {
Slog.e(TAG, "Ignoring incoming vibration as process with"
+ " uid= " + uid + " is background,"
+ " attrs= " + vib.attrs);
+ endVibrationLocked(vib, VibrationInfo.Status.IGNORED_BACKGROUND);
return;
}
linkVibration(vib);
final long ident = Binder.clearCallingIdentity();
try {
- doCancelVibrateLocked();
+ doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
startVibrationLocked(vib);
- addToPreviousVibrationsLocked(vib);
+
+ if (!vib.hasEnded() && mCurrentVibration.id != vib.id) {
+ // Vibration was unexpectedly ignored: add to list for debugging
+ endVibrationLocked(vib, VibrationInfo.Status.IGNORED);
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -868,7 +1032,7 @@
return effect.getDuration() == Long.MAX_VALUE;
}
- private void addToPreviousVibrationsLocked(Vibration vib) {
+ private void endVibrationLocked(Vibration vib, VibrationInfo.Status status) {
final LinkedList<VibrationInfo> previousVibrations;
if (vib.isRingtone()) {
previousVibrations = mPreviousRingVibrations;
@@ -883,9 +1047,18 @@
if (previousVibrations.size() > mPreviousVibrationsLimit) {
previousVibrations.removeFirst();
}
+ vib.end(status);
previousVibrations.addLast(vib.toInfo());
}
+ private void endVibrationLocked(ExternalVibrationHolder vib, VibrationInfo.Status status) {
+ if (mPreviousExternalVibrations.size() > mPreviousVibrationsLimit) {
+ mPreviousExternalVibrations.removeFirst();
+ }
+ vib.end(status);
+ mPreviousExternalVibrations.addLast(vib.toInfo());
+ }
+
@Override // Binder call
public void cancelVibrate(IBinder token) {
mContext.enforceCallingOrSelfPermission(
@@ -899,7 +1072,7 @@
}
final long ident = Binder.clearCallingIdentity();
try {
- doCancelVibrateLocked();
+ doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -908,7 +1081,7 @@
}
@GuardedBy("mLock")
- private void doCancelVibrateLocked() {
+ private void doCancelVibrateLocked(VibrationInfo.Status status) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked");
try {
@@ -917,12 +1090,13 @@
mThread = null;
}
if (mCurrentExternalVibration != null) {
- mCurrentExternalVibration.mute();
+ endVibrationLocked(mCurrentExternalVibration, status);
+ mCurrentExternalVibration.externalVibration.mute();
mCurrentExternalVibration = null;
setVibratorUnderExternalControl(false);
}
doVibratorOff();
- reportFinishVibrationLocked();
+ reportFinishVibrationLocked(status);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
@@ -936,7 +1110,7 @@
synchronized (mLock) {
// Make sure the vibration is really done. This also reports that the vibration is
// finished.
- doCancelVibrateLocked();
+ doCancelVibrateLocked(VibrationInfo.Status.FINISHED);
}
}
@@ -944,7 +1118,7 @@
private void startVibrationLocked(final Vibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
try {
- final int intensity = getCurrentIntensityLocked(vib);
+ final int intensity = getCurrentIntensityLocked(vib.attrs.getUsage());
if (!shouldVibrate(vib, intensity)) {
return;
}
@@ -959,6 +1133,7 @@
private void startVibrationInnerLocked(Vibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
try {
+ // Set current vibration before starting it, so callback will work.
mCurrentVibration = vib;
if (vib.effect instanceof VibrationEffect.OneShot) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
@@ -971,18 +1146,21 @@
} else if (vib.effect instanceof VibrationEffect.Prebaked) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
doVibratorPrebakedEffectLocked(vib);
- } else if (vib.effect instanceof VibrationEffect.Composed) {
+ } else if (vib.effect instanceof VibrationEffect.Composed) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
doVibratorComposedEffectLocked(vib);
} else {
Slog.e(TAG, "Unknown vibration type, ignoring");
+ endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNKNOWN_VIBRATION);
+ // The set current vibration is not actually playing, so drop it.
+ mCurrentVibration = null;
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
- private boolean isAllowedToVibrateLocked(Vibration vib) {
+ private boolean shouldVibrateForPowerModeLocked(Vibration vib) {
if (!mLowPowerMode) {
return true;
}
@@ -993,14 +1171,28 @@
|| usage == VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
}
- private int getCurrentIntensityLocked(Vibration vib) {
- if (vib.isRingtone()) {
+ private int getCurrentIntensityLocked(int usageHint) {
+ if (isRingtone(usageHint)) {
return mRingIntensity;
- } else if (vib.isNotification()) {
+ } else if (isNotification(usageHint)) {
return mNotificationIntensity;
- } else if (vib.isHapticFeedback()) {
+ } else if (isHapticFeedback(usageHint)) {
return mHapticFeedbackIntensity;
- } else if (vib.isAlarm()) {
+ } else if (isAlarm(usageHint)) {
+ return Vibrator.VIBRATION_INTENSITY_HIGH;
+ } else {
+ return Vibrator.VIBRATION_INTENSITY_MEDIUM;
+ }
+ }
+
+ private int getDefaultIntensity(int usageHint) {
+ if (isRingtone(usageHint)) {
+ return mVibrator.getDefaultRingVibrationIntensity();
+ } else if (isNotification(usageHint)) {
+ return mVibrator.getDefaultNotificationVibrationIntensity();
+ } else if (isHapticFeedback(usageHint)) {
+ return mVibrator.getDefaultHapticFeedbackIntensity();
+ } else if (isAlarm(usageHint)) {
return Vibrator.VIBRATION_INTENSITY_HIGH;
} else {
return Vibrator.VIBRATION_INTENSITY_MEDIUM;
@@ -1019,21 +1211,7 @@
return;
}
- final int defaultIntensity;
- if (vib.isRingtone()) {
- defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
- } else if (vib.isNotification()) {
- defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
- } else if (vib.isHapticFeedback()) {
- defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
- } else if (vib.isAlarm()) {
- defaultIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
- } else {
- // If we don't know what kind of vibration we're playing then just skip scaling for
- // now.
- return;
- }
-
+ final int defaultIntensity = getDefaultIntensity(vib.attrs.getUsage());
final ScaleLevel scale = mScaleLevels.get(intensity - defaultIntensity);
if (scale == null) {
// We should have scaling levels for all cases, so not being able to scale because of a
@@ -1045,6 +1223,7 @@
vib.originalEffect = vib.effect;
vib.effect = vib.effect.resolve(mDefaultVibrationAmplitude).scale(scale.factor);
+ vib.scale = scale.factor;
}
private boolean shouldVibrateForRingtone() {
@@ -1084,11 +1263,13 @@
}
private boolean shouldVibrate(Vibration vib, int intensity) {
- if (!isAllowedToVibrateLocked(vib)) {
+ if (!shouldVibrateForPowerModeLocked(vib)) {
+ endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_POWER);
return false;
}
if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+ endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_SETTINGS);
return false;
}
@@ -1096,6 +1277,7 @@
if (DEBUG) {
Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
}
+ endVibrationLocked(vib, VibrationInfo.Status.IGNORED_RINGTONE);
return false;
}
@@ -1105,6 +1287,9 @@
// We might be getting calls from within system_server, so we don't actually
// want to throw a SecurityException here.
Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
+ endVibrationLocked(vib, VibrationInfo.Status.ERROR_APP_OPS);
+ } else {
+ endVibrationLocked(vib, VibrationInfo.Status.IGNORED_APP_OPS);
}
return false;
}
@@ -1113,10 +1298,11 @@
}
@GuardedBy("mLock")
- private void reportFinishVibrationLocked() {
+ private void reportFinishVibrationLocked(VibrationInfo.Status status) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
try {
if (mCurrentVibration != null) {
+ endVibrationLocked(mCurrentVibration, status);
mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
mCurrentVibration.opPkg);
unlinkVibration(mCurrentVibration);
@@ -1153,7 +1339,7 @@
if (devicesUpdated || lowPowerModeUpdated) {
// If the state changes out from under us then just reset.
- doCancelVibrateLocked();
+ doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
}
updateAlwaysOnLocked();
@@ -1224,7 +1410,7 @@
}
private void updateAlwaysOnLocked(int id, Vibration vib) {
- final int intensity = getCurrentIntensityLocked(vib);
+ final int intensity = getCurrentIntensityLocked(vib.attrs.getUsage());
if (!shouldVibrate(vib, intensity)) {
mNativeWrapper.vibratorAlwaysOnDisable(id);
} else {
@@ -1345,6 +1531,10 @@
return;
}
}
+ endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNSUPPORTED);
+ // The set current vibration is not actually playing, so drop it.
+ mCurrentVibration = null;
+
if (!prebaked.shouldFallback()) {
return;
}
@@ -1355,11 +1545,12 @@
}
Vibration fallbackVib = new Vibration(vib.token, effect, vib.attrs, vib.uid,
vib.opPkg, vib.reason + " (fallback)");
- final int intensity = getCurrentIntensityLocked(fallbackVib);
+ // Set current vibration before starting it, so callback will work.
+ mCurrentVibration = fallbackVib;
+ final int intensity = getCurrentIntensityLocked(fallbackVib.attrs.getUsage());
linkVibration(fallbackVib);
applyVibrationIntensityScalingLocked(fallbackVib, intensity);
startVibrationInnerLocked(fallbackVib);
- return;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
@@ -1376,11 +1567,10 @@
usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
}
// Input devices don't support composed effect, so skip trying it with them.
- if (usingInputDeviceVibrators) {
- return;
- }
-
- if (!hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+ if (usingInputDeviceVibrators || !hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+ endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNSUPPORTED);
+ // The set current vibration is not actually playing, so drop it.
+ mCurrentVibration = null;
return;
}
@@ -1489,15 +1679,25 @@
} else {
pw.println("null");
}
- pw.print(" mCurrentExternalVibration=" + mCurrentExternalVibration);
+ pw.print(" mCurrentExternalVibration=");
+ if (mCurrentExternalVibration != null) {
+ pw.println(mCurrentExternalVibration.toInfo().toString());
+ } else {
+ pw.println("null");
+ }
pw.println(" mVibratorUnderExternalControl=" + mVibratorUnderExternalControl);
pw.println(" mIsVibrating=" + mIsVibrating);
- pw.println(" mVibratorStateListeners Count=" +
- mVibratorStateListeners.getRegisteredCallbackCount());
+ pw.println(" mVibratorStateListeners Count="
+ + mVibratorStateListeners.getRegisteredCallbackCount());
pw.println(" mLowPowerMode=" + mLowPowerMode);
pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
+ pw.println(" mHapticFeedbackDefaultIntensity="
+ + mVibrator.getDefaultHapticFeedbackIntensity());
pw.println(" mNotificationIntensity=" + mNotificationIntensity);
+ pw.println(" mNotificationDefaultIntensity="
+ + mVibrator.getDefaultNotificationVibrationIntensity());
pw.println(" mRingIntensity=" + mRingIntensity);
+ pw.println(" mRingDefaultIntensity=" + mVibrator.getDefaultRingVibrationIntensity());
pw.println(" mSupportedEffects=" + mSupportedEffects);
pw.println(" mSupportedPrimitives=" + mSupportedPrimitives);
pw.println();
@@ -1523,8 +1723,8 @@
}
pw.println(" Previous external vibrations:");
- for (ExternalVibration vib : mPreviousExternalVibrations) {
- pw.println(" " + vib);
+ for (VibrationInfo info : mPreviousExternalVibrations) {
+ pw.println(" " + info);
}
}
}
@@ -1535,36 +1735,45 @@
synchronized (mLock) {
if (mCurrentVibration != null) {
mCurrentVibration.toInfo().dumpProto(proto,
- VibratorServiceDumpProto.CURRENT_VIBRATION);
+ VibratorServiceDumpProto.CURRENT_VIBRATION);
+ }
+ if (mCurrentExternalVibration != null) {
+ mCurrentExternalVibration.toInfo().dumpProto(proto,
+ VibratorServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
}
proto.write(VibratorServiceDumpProto.IS_VIBRATING, mIsVibrating);
proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
- mVibratorUnderExternalControl);
+ mVibratorUnderExternalControl);
proto.write(VibratorServiceDumpProto.LOW_POWER_MODE, mLowPowerMode);
proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
- mHapticFeedbackIntensity);
- proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY,
- mNotificationIntensity);
+ mHapticFeedbackIntensity);
+ proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
+ mVibrator.getDefaultHapticFeedbackIntensity());
+ proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY, mNotificationIntensity);
+ proto.write(VibratorServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
+ mVibrator.getDefaultNotificationVibrationIntensity());
proto.write(VibratorServiceDumpProto.RING_INTENSITY, mRingIntensity);
+ proto.write(VibratorServiceDumpProto.RING_DEFAULT_INTENSITY,
+ mVibrator.getDefaultRingVibrationIntensity());
for (VibrationInfo info : mPreviousRingVibrations) {
- info.dumpProto(proto,
- VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
+ info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
}
for (VibrationInfo info : mPreviousNotificationVibrations) {
- info.dumpProto(proto,
- VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
+ info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
}
for (VibrationInfo info : mPreviousAlarmVibrations) {
- info.dumpProto(proto,
- VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
+ info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
}
for (VibrationInfo info : mPreviousVibrations) {
- info.dumpProto(proto,
- VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
+ info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
+ }
+
+ for (VibrationInfo info : mPreviousExternalVibrations) {
+ info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
}
}
proto.flush();
@@ -1579,8 +1788,8 @@
VibrateWaveformThread(Vibration vib) {
mWaveform = (VibrationEffect.Waveform) vib.effect;
- mVibration = new Vibration(vib.token, /* effect= */ null, vib.attrs, vib.uid, vib.opPkg,
- vib.reason);
+ mVibration = new Vibration(vib.token, /* effect= */ null, vib.attrs, vib.uid,
+ vib.opPkg, vib.reason);
mTmpWorkSource.set(vib.uid);
mWakeLock.setWorkSource(mTmpWorkSource);
}
@@ -1655,8 +1864,8 @@
// appropriate intervals.
onDuration = getTotalOnDuration(timings, amplitudes, index - 1,
repeat);
- mVibration.effect =
- VibrationEffect.createOneShot(onDuration, amplitude);
+ mVibration.effect = VibrationEffect.createOneShot(
+ onDuration, amplitude);
doVibratorOn(mVibration);
} else {
doVibratorSetAmplitude(amplitude);
@@ -1827,7 +2036,7 @@
if (mCurrentVibration != null
&& !(mCurrentVibration.isHapticFeedback()
&& mCurrentVibration.isFromSystem())) {
- doCancelVibrateLocked();
+ doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
}
}
}
@@ -1882,63 +2091,54 @@
int mode = getAppOpMode(vib.getUid(), vib.getPackage(), vib.getVibrationAttributes());
if (mode != AppOpsManager.MODE_ALLOWED) {
+ ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
+ vibHolder.scale = SCALE_MUTE;
if (mode == AppOpsManager.MODE_ERRORED) {
Slog.w(TAG, "Would be an error: external vibrate from uid " + vib.getUid());
+ endVibrationLocked(vibHolder, VibrationInfo.Status.ERROR_APP_OPS);
+ } else {
+ endVibrationLocked(vibHolder, VibrationInfo.Status.IGNORED_APP_OPS);
}
return SCALE_MUTE;
}
final int scaleLevel;
synchronized (mLock) {
- if (!vib.equals(mCurrentExternalVibration)) {
- if (mCurrentExternalVibration == null) {
- // If we're not under external control right now, then cancel any normal
- // vibration that may be playing and ready the vibrator for external
- // control.
- doCancelVibrateLocked();
- setVibratorUnderExternalControl(true);
- }
- // At this point we either have an externally controlled vibration playing, or
- // no vibration playing. Since the interface defines that only one externally
- // controlled vibration can play at a time, by returning something other than
- // SCALE_MUTE from this function we can be assured that if we are currently
- // playing vibration, it will be muted in favor of the new vibration.
- //
- // Note that this doesn't support multiple concurrent external controls, as we
- // would need to mute the old one still if it came from a different controller.
- mCurrentExternalVibration = vib;
- mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
- mCurrentExternalVibration.linkToDeath(mCurrentExternalDeathRecipient);
- if (mPreviousExternalVibrations.size() > mPreviousVibrationsLimit) {
- mPreviousExternalVibrations.removeFirst();
- }
- mPreviousExternalVibrations.addLast(vib);
- if (DEBUG) {
- Slog.e(TAG, "Playing external vibration: " + vib);
- }
+ if (mCurrentExternalVibration != null
+ && mCurrentExternalVibration.externalVibration.equals(vib)) {
+ // We are already playing this external vibration, so we can return the same
+ // scale calculated in the previous call to this method.
+ return mCurrentExternalVibration.scale;
}
- final int usage = vib.getVibrationAttributes().getUsage();
- final int defaultIntensity;
- final int currentIntensity;
- if (isRingtone(usage)) {
- defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
- currentIntensity = mRingIntensity;
- } else if (isNotification(usage)) {
- defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
- currentIntensity = mNotificationIntensity;
- } else if (isHapticFeedback(usage)) {
- defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
- currentIntensity = mHapticFeedbackIntensity;
- } else if (isAlarm(usage)) {
- defaultIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
- currentIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
+ if (mCurrentExternalVibration == null) {
+ // If we're not under external control right now, then cancel any normal
+ // vibration that may be playing and ready the vibrator for external control.
+ doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
+ setVibratorUnderExternalControl(true);
} else {
- defaultIntensity = 0;
- currentIntensity = 0;
+ endVibrationLocked(mCurrentExternalVibration, VibrationInfo.Status.CANCELLED);
}
+ // At this point we either have an externally controlled vibration playing, or
+ // no vibration playing. Since the interface defines that only one externally
+ // controlled vibration can play at a time, by returning something other than
+ // SCALE_MUTE from this function we can be assured that if we are currently
+ // playing vibration, it will be muted in favor of the new vibration.
+ //
+ // Note that this doesn't support multiple concurrent external controls, as we
+ // would need to mute the old one still if it came from a different controller.
+ mCurrentExternalVibration = new ExternalVibrationHolder(vib);
+ mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
+ vib.linkToDeath(mCurrentExternalDeathRecipient);
+ if (DEBUG) {
+ Slog.e(TAG, "Playing external vibration: " + vib);
+ }
+ int usage = vib.getVibrationAttributes().getUsage();
+ int defaultIntensity = getDefaultIntensity(usage);
+ int currentIntensity = getCurrentIntensityLocked(usage);
scaleLevel = currentIntensity - defaultIntensity;
}
if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) {
+ mCurrentExternalVibration.scale = scaleLevel;
return scaleLevel;
} else {
// Presumably we want to play this but something about our scaling has gone
@@ -1952,22 +2152,42 @@
@Override
public void onExternalVibrationStop(ExternalVibration vib) {
synchronized (mLock) {
- if (vib.equals(mCurrentExternalVibration)) {
- mCurrentExternalVibration.unlinkToDeath(mCurrentExternalDeathRecipient);
- mCurrentExternalDeathRecipient = null;
- mCurrentExternalVibration = null;
- setVibratorUnderExternalControl(false);
+ if (mCurrentExternalVibration != null
+ && mCurrentExternalVibration.externalVibration.equals(vib)) {
if (DEBUG) {
Slog.e(TAG, "Stopping external vibration" + vib);
}
+ doCancelExternalVibrateLocked(VibrationInfo.Status.FINISHED);
}
}
}
+ private void doCancelExternalVibrateLocked(VibrationInfo.Status status) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelExternalVibrateLocked");
+ try {
+ if (mCurrentExternalVibration == null) {
+ return;
+ }
+ endVibrationLocked(mCurrentExternalVibration, status);
+ mCurrentExternalVibration.externalVibration.unlinkToDeath(
+ mCurrentExternalDeathRecipient);
+ mCurrentExternalDeathRecipient = null;
+ mCurrentExternalVibration = null;
+ setVibratorUnderExternalControl(false);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+ }
+
private class ExternalVibrationDeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
synchronized (mLock) {
- onExternalVibrationStop(mCurrentExternalVibration);
+ if (mCurrentExternalVibration != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "External vibration finished because binder died");
+ }
+ doCancelExternalVibrateLocked(VibrationInfo.Status.CANCELLED);
+ }
}
}
}
@@ -2229,5 +2449,4 @@
}
}
}
-
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 990a547..49444834 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -23,7 +23,9 @@
import android.content.IntentFilter;
import android.hidl.manager.V1_0.IServiceManager;
import android.os.Binder;
+import android.os.Build;
import android.os.Debug;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.Looper;
@@ -31,10 +33,12 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.sysprop.WatchdogProperties;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.ZygoteConnectionConstants;
@@ -42,12 +46,16 @@
import com.android.server.am.ActivityManagerService;
import com.android.server.wm.SurfaceAnimationThread;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
import java.util.HashSet;
import java.util.List;
@@ -75,6 +83,12 @@
private static final int WAITED_HALF = 2;
private static final int OVERDUE = 3;
+ // Track watchdog timeout history and break the crash loop if there is.
+ private static final String TIMEOUT_HISTORY_FILE = "/data/system/watchdog-timeout-history.txt";
+ private static final String PROP_FATAL_LOOP_COUNT = "framework_watchdog.fatal_count";
+ private static final String PROP_FATAL_LOOP_WINDOWS_SECS =
+ "framework_watchdog.fatal_window.second";
+
// Which native processes to dump into dropbox's stack traces
public static final String[] NATIVE_STACKS_OF_INTEREST = new String[] {
"/system/bin/audioserver",
@@ -699,6 +713,10 @@
Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);
WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
Slog.w(TAG, "*** GOODBYE!");
+ if (!Build.IS_USER && isCrashLoopFound()
+ && !WatchdogProperties.is_fatal_ignore().orElse(false)) {
+ breakCrashLoop();
+ }
Process.killProcess(Process.myPid());
System.exit(10);
}
@@ -716,4 +734,107 @@
Slog.w(TAG, "Failed to write to /proc/sysrq-trigger", e);
}
}
+
+ private void resetTimeoutHistory() {
+ writeTimeoutHistory(new ArrayList<String>());
+ }
+
+ private void writeTimeoutHistory(Iterable<String> crashHistory) {
+ String data = String.join(",", crashHistory);
+
+ try (FileWriter writer = new FileWriter(TIMEOUT_HISTORY_FILE)) {
+ writer.write(SystemProperties.get("ro.boottime.zygote"));
+ writer.write(":");
+ writer.write(data);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write file " + TIMEOUT_HISTORY_FILE, e);
+ }
+ }
+
+ private String[] readTimeoutHistory() {
+ final String[] emptyStringArray = {};
+
+ try (BufferedReader reader = new BufferedReader(new FileReader(TIMEOUT_HISTORY_FILE))) {
+ String line = reader.readLine();
+ if (line == null) {
+ return emptyStringArray;
+ }
+
+ String[] data = line.trim().split(":");
+ String boottime = data.length >= 1 ? data[0] : "";
+ String history = data.length >= 2 ? data[1] : "";
+ if (SystemProperties.get("ro.boottime.zygote").equals(boottime) && !history.isEmpty()) {
+ return history.split(",");
+ } else {
+ return emptyStringArray;
+ }
+ } catch (FileNotFoundException e) {
+ return emptyStringArray;
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read file " + TIMEOUT_HISTORY_FILE, e);
+ return emptyStringArray;
+ }
+ }
+
+ private boolean hasActiveUsbConnection() {
+ try {
+ final String state = FileUtils.readTextFile(
+ new File("/sys/class/android_usb/android0/state"),
+ 128 /*max*/, null /*ellipsis*/).trim();
+ if ("CONFIGURED".equals(state)) {
+ return true;
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to determine if device was on USB", e);
+ }
+ return false;
+ }
+
+ private boolean isCrashLoopFound() {
+ int fatalCount = WatchdogProperties.fatal_count().orElse(0);
+ long fatalWindowMs = TimeUnit.SECONDS.toMillis(
+ WatchdogProperties.fatal_window_second().orElse(0));
+ if (fatalCount == 0 || fatalWindowMs == 0) {
+ if (fatalCount != fatalWindowMs) {
+ Slog.w(TAG, String.format("sysprops '%s' and '%s' should be set or unset together",
+ PROP_FATAL_LOOP_COUNT, PROP_FATAL_LOOP_WINDOWS_SECS));
+ }
+ return false;
+ }
+
+ // new-history = [last (fatalCount - 1) items in old-history] + [nowMs].
+ long nowMs = SystemClock.elapsedRealtime(); // Time since boot including deep sleep.
+ String[] rawCrashHistory = readTimeoutHistory();
+ ArrayList<String> crashHistory = new ArrayList<String>(Arrays.asList(Arrays.copyOfRange(
+ rawCrashHistory,
+ Math.max(0, rawCrashHistory.length - fatalCount - 1),
+ rawCrashHistory.length)));
+ // Something wrong here.
+ crashHistory.add(String.valueOf(nowMs));
+ writeTimeoutHistory(crashHistory);
+
+ // Returns false if the device has an active USB connection.
+ if (hasActiveUsbConnection()) {
+ return false;
+ }
+
+ long firstCrashMs;
+ try {
+ firstCrashMs = Long.parseLong(crashHistory.get(0));
+ } catch (NumberFormatException t) {
+ Slog.w(TAG, "Failed to parseLong " + crashHistory.get(0), t);
+ resetTimeoutHistory();
+ return false;
+ }
+ return crashHistory.size() >= fatalCount && nowMs - firstCrashMs < fatalWindowMs;
+ }
+
+ private void breakCrashLoop() {
+ try (FileWriter kmsg = new FileWriter("/dev/kmsg_debug", /* append= */ true)) {
+ kmsg.append("Fatal reset to escape the system_server crashing loop\n");
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to append to kmsg", e);
+ }
+ doSysRq('c');
+ }
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b2e021f..31712be 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -56,6 +56,8 @@
import android.app.ServiceStartArgs;
import android.app.admin.DevicePolicyEventLogger;
import android.appwidget.AppWidgetManagerInternal;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.content.ComponentName;
import android.content.ComponentName.WithComponentName;
import android.content.Context;
@@ -79,6 +81,7 @@
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.TransactionTooLargeException;
@@ -100,6 +103,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.ServiceState;
+import com.android.internal.compat.IPlatformCompat;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BatteryStatsImpl;
@@ -234,6 +238,16 @@
private static final SimpleDateFormat DATE_FORMATTER =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ private final IPlatformCompat mPlatformCompat;
+
+ /**
+ * The BG-launch FGS restriction feature is going to be allowed only for apps targetSdkVersion
+ * is higher than R.
+ */
+ @ChangeId
+ @Disabled
+ static final long FGS_BG_START_RESTRICTION_CHANGE_ID = 170668199L;
+
final Runnable mLastAnrDumpClearer = new Runnable() {
@Override public void run() {
synchronized (mAm) {
@@ -430,6 +444,9 @@
}
mMaxStartingBackground = maxBg > 0
? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8;
+
+ final IBinder b = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
+ mPlatformCompat = IPlatformCompat.Stub.asInterface(b);
}
void systemServicesReady() {
@@ -557,12 +574,20 @@
r.mLoggedInfoAllowStartForeground = true;
}
if (r.mAllowStartForeground == FGS_FEATURE_DENIED
- && mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
- Slog.w(TAG, "startForegroundService() not allowed due to "
- + "mAllowStartForeground false: service "
- + r.shortInstanceName);
- showFgsBgRestrictedNotificationLocked(r);
- forcedStandby = true;
+ && (mAm.mConstants.mFlagFgsStartRestrictionEnabled
+ || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) {
+ if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled
+ && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) {
+ // uid is on DeviceIdleController's allowlist.
+ Slog.d(TAG, "startForegroundService() mAllowStartForeground false "
+ + "but allowlist true: service " + r.shortInstanceName);
+ } else {
+ Slog.w(TAG, "startForegroundService() not allowed due to "
+ + "mAllowStartForeground false: service "
+ + r.shortInstanceName);
+ showFgsBgRestrictedNotificationLocked(r);
+ return null;
+ }
}
}
}
@@ -1461,14 +1486,22 @@
r.mLoggedInfoAllowStartForeground = true;
}
if (r.mAllowStartForeground == FGS_FEATURE_DENIED
- && mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
- Slog.w(TAG,
- "Service.startForeground() not allowed due to "
- + "mAllowStartForeground false: service "
- + r.shortInstanceName);
- showFgsBgRestrictedNotificationLocked(r);
- updateServiceForegroundLocked(r.app, true);
- ignoreForeground = true;
+ && (mAm.mConstants.mFlagFgsStartRestrictionEnabled
+ || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) {
+ if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled
+ && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) {
+ // uid is on DeviceIdleController's allowlist.
+ Slog.d(TAG, "Service.startForeground() "
+ + "mAllowStartForeground false but allowlist true: service "
+ + r.shortInstanceName);
+ } else {
+ Slog.w(TAG, "Service.startForeground() not allowed due to "
+ + "mAllowStartForeground false: service "
+ + r.shortInstanceName);
+ showFgsBgRestrictedNotificationLocked(r);
+ updateServiceForegroundLocked(r.app, true);
+ ignoreForeground = true;
+ }
}
}
}
@@ -5087,4 +5120,12 @@
context.getSystemService(NotificationManager.class).notifyAsUser(Long.toString(now),
NOTE_FOREGROUND_SERVICE_BG_LAUNCH, n.build(), UserHandle.ALL);
}
+
+ private boolean isChangeEnabled(long changeId, ServiceRecord r) {
+ boolean enabled = false;
+ try {
+ enabled = mPlatformCompat.isChangeEnabled(changeId, r.appInfo);
+ } catch (RemoteException e) { }
+ return enabled;
+ }
}
diff --git a/services/core/java/com/android/server/am/ActiveUids.java b/services/core/java/com/android/server/am/ActiveUids.java
index 4e1435e..c86c29f 100644
--- a/services/core/java/com/android/server/am/ActiveUids.java
+++ b/services/core/java/com/android/server/am/ActiveUids.java
@@ -16,7 +16,12 @@
package com.android.server.am;
+import android.app.ActivityManager;
+import android.os.UserHandle;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
/** Class for tracking active uids for running processes. */
final class ActiveUids {
@@ -71,4 +76,43 @@
int indexOfKey(int uid) {
return mActiveUids.indexOfKey(uid);
}
+
+ boolean dump(PrintWriter pw, String dumpPackage, int dumpAppId,
+ String header, boolean needSep) {
+ boolean printed = false;
+ for (int i = 0; i < mActiveUids.size(); i++) {
+ final UidRecord uidRec = mActiveUids.valueAt(i);
+ if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) {
+ continue;
+ }
+ if (!printed) {
+ printed = true;
+ if (needSep) {
+ pw.println();
+ }
+ pw.print(" "); pw.println(header);
+ }
+ pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
+ pw.print(": "); pw.println(uidRec);
+ pw.print(" curProcState="); pw.print(uidRec.mCurProcState);
+ pw.print(" curCapability=");
+ ActivityManager.printCapabilitiesFull(pw, uidRec.curCapability);
+ pw.println();
+ for (int j = uidRec.procRecords.size() - 1; j >= 0; j--) {
+ pw.print(" proc=");
+ pw.println(uidRec.procRecords.valueAt(j));
+ }
+ }
+ return printed;
+ }
+
+ void dumpProto(ProtoOutputStream proto, String dumpPackage, int dumpAppId, long fieldId) {
+ for (int i = 0; i < mActiveUids.size(); i++) {
+ UidRecord uidRec = mActiveUids.valueAt(i);
+ if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) {
+ continue;
+ }
+ uidRec.dumpDebug(proto, fieldId);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 48055b5..b54a917e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -161,6 +161,13 @@
private static final String KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED =
"default_fgs_starts_restriction_enabled";
+ /**
+ * Default value for mFlagFgsStartTempAllowListEnabled if not explicitly set in
+ * Settings.Global.
+ */
+ private static final String KEY_DEFAULT_FGS_STARTS_TEMP_ALLOWLIST_ENABLED =
+ "default_fgs_starts_temp_allowlist_enabled";
+
// Maximum number of cached processes we will allow.
public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
@@ -321,6 +328,10 @@
// at all.
volatile boolean mFlagFgsStartRestrictionEnabled = false;
+ // When the foreground service background start restriction is enabled, if the app in
+ // DeviceIdleController's Temp AllowList is allowed to bypass the restriction.
+ volatile boolean mFlagFgsStartTempAllowListEnabled = false;
+
private final ActivityManagerService mService;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -472,6 +483,9 @@
case KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED:
updateFgsStartsRestriction();
break;
+ case KEY_DEFAULT_FGS_STARTS_TEMP_ALLOWLIST_ENABLED:
+ updateFgsStartsTempAllowList();
+ break;
case KEY_OOMADJ_UPDATE_POLICY:
updateOomAdjUpdatePolicy();
break;
@@ -724,6 +738,13 @@
/*defaultValue*/ false);
}
+ private void updateFgsStartsTempAllowList() {
+ mFlagFgsStartTempAllowListEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_DEFAULT_FGS_STARTS_TEMP_ALLOWLIST_ENABLED,
+ /*defaultValue*/ false);
+ }
+
private void updateOomAdjUpdatePolicy() {
OOMADJ_UPDATE_QUICK = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dbc77d8..f0c9845 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1979,7 +1979,6 @@
mConstants = hasHandlerThread
? new ActivityManagerConstants(mContext, this, mHandler) : null;
final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
- mUidObserverController = new UidObserverController(this);
mPlatformCompat = null;
mProcessList = injector.getProcessList(this);
mProcessList.init(this, activeUids, mPlatformCompat);
@@ -1997,6 +1996,7 @@
mServices = hasHandlerThread ? new ActiveServices(this) : null;
mSystemThread = null;
mUiHandler = injector.getUiHandler(null /* service */);
+ mUidObserverController = new UidObserverController(mUiHandler);
mUserController = hasHandlerThread ? new UserController(this) : null;
mPendingIntentController = hasHandlerThread
? new PendingIntentController(handlerThread.getLooper(), mUserController,
@@ -2077,7 +2077,7 @@
mCpHelper = new ContentProviderHelper(this, true);
mPackageWatchdog = PackageWatchdog.getInstance(mUiContext);
mAppErrors = new AppErrors(mUiContext, this, mPackageWatchdog);
- mUidObserverController = new UidObserverController(this);
+ mUidObserverController = new UidObserverController(mUiHandler);
final File systemDir = SystemServiceManager.ensureSystemDir();
@@ -8816,37 +8816,6 @@
return -1;
}
- boolean dumpUids(PrintWriter pw, String dumpPackage, int dumpAppId, ActiveUids uids,
- String header, boolean needSep) {
- boolean printed = false;
- for (int i=0; i<uids.size(); i++) {
- UidRecord uidRec = uids.valueAt(i);
- if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) {
- continue;
- }
- if (!printed) {
- printed = true;
- if (needSep) {
- pw.println();
- }
- pw.print(" ");
- pw.println(header);
- needSep = true;
- }
- pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid);
- pw.print(": "); pw.println(uidRec);
- pw.print(" curProcState="); pw.print(uidRec.mCurProcState);
- pw.print(" curCapability=");
- ActivityManager.printCapabilitiesFull(pw, uidRec.curCapability);
- pw.println();
- for (int j = uidRec.procRecords.size() - 1; j >= 0; j--) {
- pw.print(" proc=");
- pw.println(uidRec.procRecords.valueAt(j));
- }
- }
- return printed;
- }
-
void dumpBinderProxyInterfaceCounts(PrintWriter pw, String header) {
final BinderProxy.InterfaceCount[] proxyCounts = BinderProxy.getSortedInterfaceCounts(50);
@@ -9096,19 +9065,13 @@
needSep = dumpProcessesToGc(pw, needSep, dumpPackage);
if (mProcessList.mActiveUids.size() > 0) {
- if (dumpUids(pw, dumpPackage, dumpAppId, mProcessList.mActiveUids,
- "UID states:", needSep)) {
- needSep = true;
- }
+ needSep |= mProcessList.mActiveUids.dump(pw, dumpPackage, dumpAppId,
+ "UID states:", needSep);
}
if (dumpAll) {
- if (mUidObserverController.mValidateUids.size() > 0) {
- if (dumpUids(pw, dumpPackage, dumpAppId, mUidObserverController.mValidateUids,
- "UID validation:", needSep)) {
- needSep = true;
- }
- }
+ needSep |= mUidObserverController.dumpValidateUids(pw,
+ dumpPackage, dumpAppId, "UID validation:", needSep);
}
if (needSep) {
@@ -9377,22 +9340,12 @@
ActivityManagerServiceDumpProcessesProto.ACTIVE_INSTRUMENTATIONS);
}
- int whichAppId = getAppId(dumpPackage);
- for (int i = 0; i < mProcessList.mActiveUids.size(); i++) {
- UidRecord uidRec = mProcessList.mActiveUids.valueAt(i);
- if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
- continue;
- }
- uidRec.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
- }
+ final int dumpAppId = getAppId(dumpPackage);
+ mProcessList.mActiveUids.dumpProto(proto, dumpPackage, dumpAppId,
+ ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
- for (int i = 0; i < mUidObserverController.mValidateUids.size(); i++) {
- UidRecord uidRec = mUidObserverController.mValidateUids.valueAt(i);
- if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) {
- continue;
- }
- uidRec.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS);
- }
+ mUidObserverController.dumpValidateUidsProto(proto, dumpPackage, dumpAppId,
+ ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS);
if (mProcessList.getLruSizeLocked() > 0) {
long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
@@ -12127,6 +12080,7 @@
app.setHasClientActivities(false);
mServices.killServicesLocked(app, allowRestart);
+ mPhantomProcessList.onAppDied(app.pid);
boolean restart = false;
@@ -14891,6 +14845,58 @@
return false;
}
+ private boolean isEphemeralLocked(int uid) {
+ final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
+ if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid
+ return false;
+ }
+ return getPackageManagerInternalLocked().isPackageEphemeral(
+ UserHandle.getUserId(uid), packages[0]);
+ }
+
+ void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
+ uid = uidRec != null ? uidRec.uid : uid;
+ if (uid < 0) {
+ throw new IllegalArgumentException("No UidRecord or uid");
+ }
+
+ final int procState = uidRec != null
+ ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT;
+ final long procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
+ final int capability = uidRec != null ? uidRec.setCapability : 0;
+ final boolean ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
+
+ if (uidRec != null && !uidRec.idle && (change & UidRecord.CHANGE_GONE) != 0) {
+ // If this uid is going away, and we haven't yet reported it is gone,
+ // then do so now.
+ change |= UidRecord.CHANGE_IDLE;
+ }
+ final int enqueuedChange = mUidObserverController.enqueueUidChange(
+ uid, change, procState, procStateSeq, capability, ephemeral);
+ if (uidRec != null) {
+ uidRec.lastReportedChange = enqueuedChange;
+ uidRec.updateLastDispatchedProcStateSeq(enqueuedChange);
+ }
+
+ // Directly update the power manager, since we sit on top of it and it is critical
+ // it be kept in sync (so wake locks will be held as soon as appropriate).
+ if (mLocalPowerManager != null) {
+ // TODO: dispatch cached/uncached changes here, so we don't need to report
+ // all proc state changes.
+ if ((enqueuedChange & UidRecord.CHANGE_ACTIVE) != 0) {
+ mLocalPowerManager.uidActive(uid);
+ }
+ if ((enqueuedChange & UidRecord.CHANGE_IDLE) != 0) {
+ mLocalPowerManager.uidIdle(uid);
+ }
+ if ((enqueuedChange & UidRecord.CHANGE_GONE) != 0) {
+ mLocalPowerManager.uidGone(uid);
+ } else {
+ mLocalPowerManager.updateUidProcState(uid, procState);
+ }
+ }
+ }
+
final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
synchronized (mProcessStats.mLock) {
if (proc.thread != null && proc.baseProcessTracker != null) {
@@ -15156,7 +15162,7 @@
@GuardedBy("this")
final void doStopUidLocked(int uid, final UidRecord uidRec) {
mServices.stopInBackgroundLocked(uid);
- mUidObserverController.enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
+ enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
}
/**
@@ -16706,7 +16712,7 @@
+ totalTime + ". Uid: " + callingUid + " procStateSeq: "
+ procStateSeq + " UidRec: " + record
+ " validateUidRec: "
- + mUidObserverController.mValidateUids.get(callingUid));
+ + mUidObserverController.getValidateUidRecord(callingUid));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 58ac2dc..9d49236 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1147,7 +1147,7 @@
uidRec.setWhitelist = uidRec.curWhitelist;
uidRec.setIdle = uidRec.idle;
mService.mAtmInternal.onUidProcStateChanged(uidRec.uid, uidRec.setProcState);
- mService.mUidObserverController.enqueueUidChangeLocked(uidRec, -1, uidChange);
+ mService.enqueueUidChangeLocked(uidRec, -1, uidChange);
mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState(),
uidRec.curCapability);
if (uidRec.foregroundServices) {
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index e2fcf08..5167c57 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -27,15 +27,22 @@
import android.app.ApplicationExitInfo.SubReason;
import android.os.Handler;
import android.os.Process;
+import android.os.StrictMode;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ProcStatsUtil;
import com.android.internal.os.ProcessCpuTracker;
import libcore.io.IoUtils;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
@@ -78,18 +85,178 @@
@GuardedBy("mLock")
private final ArrayList<PhantomProcessRecord> mTempPhantomProcesses = new ArrayList<>();
+ /**
+ * The mapping between a phantom process ID to its parent process (an app process)
+ */
+ @GuardedBy("mLock")
+ private final SparseArray<ProcessRecord> mPhantomToAppProcessMap = new SparseArray<>();
+
+ @GuardedBy("mLock")
+ private final SparseArray<InputStream> mCgroupProcsFds = new SparseArray<>();
+
+ @GuardedBy("mLock")
+ private final byte[] mDataBuffer = new byte[4096];
+
@GuardedBy("mLock")
private boolean mTrimPhantomProcessScheduled = false;
@GuardedBy("mLock")
int mUpdateSeq;
+ @VisibleForTesting
+ Injector mInjector;
+
private final ActivityManagerService mService;
private final Handler mKillHandler;
PhantomProcessList(final ActivityManagerService service) {
mService = service;
mKillHandler = service.mProcessList.sKillHandler;
+ mInjector = new Injector();
+ }
+
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ void lookForPhantomProcessesLocked() {
+ mPhantomToAppProcessMap.clear();
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ try {
+ synchronized (mService.mPidsSelfLocked) {
+ for (int i = mService.mPidsSelfLocked.size() - 1; i >= 0; i--) {
+ final ProcessRecord app = mService.mPidsSelfLocked.valueAt(i);
+ lookForPhantomProcessesLocked(app);
+ }
+ }
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ }
+
+ @GuardedBy({"mLock", "mService.mPidsSelfLocked"})
+ private void lookForPhantomProcessesLocked(ProcessRecord app) {
+ if (app.appZygote || app.killed || app.killedByAm) {
+ // process forked from app zygote doesn't have its own acct entry
+ return;
+ }
+ InputStream input = mCgroupProcsFds.get(app.pid);
+ if (input == null) {
+ final String path = getCgroupFilePath(app.info.uid, app.pid);
+ try {
+ input = mInjector.openCgroupProcs(path);
+ } catch (FileNotFoundException | SecurityException e) {
+ if (DEBUG_PROCESSES) {
+ Slog.w(TAG, "Unable to open " + path, e);
+ }
+ return;
+ }
+ // Keep the FD open for better performance
+ mCgroupProcsFds.put(app.pid, input);
+ }
+ final byte[] buf = mDataBuffer;
+ try {
+ int read = 0;
+ int pid = 0;
+ long totalRead = 0;
+ do {
+ read = mInjector.readCgroupProcs(input, buf, 0, buf.length);
+ if (read == -1) {
+ break;
+ }
+ totalRead += read;
+ for (int i = 0; i < read; i++) {
+ final byte b = buf[i];
+ if (b == '\n') {
+ addChildPidLocked(app, pid);
+ pid = 0;
+ } else {
+ pid = pid * 10 + (b - '0');
+ }
+ }
+ if (read < buf.length) {
+ // we may break from here safely as sysfs reading should return the whole page
+ // if the remaining data is larger than a page
+ break;
+ }
+ } while (true);
+ if (pid != 0) {
+ addChildPidLocked(app, pid);
+ }
+ // rewind the fd for the next read
+ input.skip(-totalRead);
+ } catch (IOException e) {
+ Slog.e(TAG, "Error in reading cgroup procs from " + app, e);
+ IoUtils.closeQuietly(input);
+ mCgroupProcsFds.delete(app.pid);
+ }
+ }
+
+ @VisibleForTesting
+ static String getCgroupFilePath(int uid, int pid) {
+ return "/acct/uid_" + uid + "/pid_" + pid + "/cgroup.procs";
+ }
+
+ static String getProcessName(int pid) {
+ String procName = ProcStatsUtil.readTerminatedProcFile(
+ "/proc/" + pid + "/cmdline", (byte) '\0');
+ if (procName == null) {
+ return null;
+ }
+ int l = procName.lastIndexOf('/');
+ if (l > 0 && l < procName.length() - 1) {
+ procName = procName.substring(l + 1);
+ }
+ return procName;
+ }
+
+ @GuardedBy({"mLock", "mService.mPidsSelfLocked"})
+ private void addChildPidLocked(final ProcessRecord app, final int pid) {
+ if (app.pid != pid) {
+ // That's something else...
+ final ProcessRecord r = mService.mPidsSelfLocked.get(pid);
+ if (r != null) {
+ // Is this a process forked via app zygote?
+ if (!r.appZygote) {
+ // Unexpected...
+ if (DEBUG_PROCESSES) {
+ Slog.w(TAG, "Unexpected: " + r + " appears in the cgroup.procs of " + app);
+ }
+ } else {
+ // Just a child process of app zygote, no worries
+ }
+ } else {
+ final int index = mPhantomToAppProcessMap.indexOfKey(pid);
+ if (index >= 0) { // unlikely since we cleared the map at the beginning
+ final ProcessRecord current = mPhantomToAppProcessMap.valueAt(index);
+ if (app == current) {
+ // Okay it's unchanged
+ return;
+ }
+ mPhantomToAppProcessMap.setValueAt(index, app);
+ } else {
+ mPhantomToAppProcessMap.put(pid, app);
+ }
+ // Its UID isn't necessarily to be the same as the app.info.uid, since it could be
+ // forked from child processes of app zygote
+ final int uid = Process.getUidForPid(pid);
+ String procName = mInjector.getProcessName(pid);
+ if (procName == null || uid < 0) {
+ mPhantomToAppProcessMap.delete(pid);
+ return;
+ }
+ getOrCreatePhantomProcessIfNeededLocked(procName, uid, pid, true);
+ }
+ }
+ }
+
+ void onAppDied(final int pid) {
+ synchronized (mLock) {
+ final int index = mCgroupProcsFds.indexOfKey(pid);
+ if (index >= 0) {
+ final InputStream inputStream = mCgroupProcsFds.valueAt(index);
+ mCgroupProcsFds.removeAt(index);
+ IoUtils.closeQuietly(inputStream);
+ }
+ }
}
/**
@@ -99,7 +266,7 @@
*/
@GuardedBy("mLock")
PhantomProcessRecord getOrCreatePhantomProcessIfNeededLocked(final String processName,
- final int uid, final int pid) {
+ final int uid, final int pid, boolean createIfNeeded) {
// First check if it's actually an app process we know
if (isAppProcess(pid)) {
return null;
@@ -123,56 +290,47 @@
if (proc.equals(processName, uid, pid)) {
return proc;
}
- // Our zombie process information is outdated, let's remove this one, it shoud
+ // Our zombie process information is outdated, let's remove this one, it should
// have been gone.
mZombiePhantomProcesses.removeAt(idx);
}
}
- int ppid = getParentPid(pid);
+ if (!createIfNeeded) {
+ return null;
+ }
- // Walk through its parents and see if it could be traced back to an app process.
- while (ppid > 1) {
- if (isAppProcess(ppid)) {
- // It's a phantom process, bookkeep it
- try {
- final PhantomProcessRecord proc = new PhantomProcessRecord(
- processName, uid, pid, ppid, mService,
- this::onPhantomProcessKilledLocked);
- proc.mUpdateSeq = mUpdateSeq;
- mPhantomProcesses.put(pid, proc);
- SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.get(ppid);
- if (array == null) {
- array = new SparseArray<>();
- mAppPhantomProcessMap.put(ppid, array);
- }
- array.put(pid, proc);
- if (proc.mPidFd != null) {
- mKillHandler.getLooper().getQueue().addOnFileDescriptorEventListener(
- proc.mPidFd, EVENT_INPUT | EVENT_ERROR,
- this::onPhantomProcessFdEvent);
- mPhantomProcessesPidFds.put(proc.mPidFd.getInt$(), proc);
- }
- scheduleTrimPhantomProcessesLocked();
- return proc;
- } catch (IllegalStateException e) {
- return null;
+ final ProcessRecord r = mPhantomToAppProcessMap.get(pid);
+
+ if (r != null) {
+ // It's a phantom process, bookkeep it
+ try {
+ final PhantomProcessRecord proc = new PhantomProcessRecord(
+ processName, uid, pid, r.pid, mService,
+ this::onPhantomProcessKilledLocked);
+ proc.mUpdateSeq = mUpdateSeq;
+ mPhantomProcesses.put(pid, proc);
+ SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.get(r.pid);
+ if (array == null) {
+ array = new SparseArray<>();
+ mAppPhantomProcessMap.put(r.pid, array);
}
+ array.put(pid, proc);
+ if (proc.mPidFd != null) {
+ mKillHandler.getLooper().getQueue().addOnFileDescriptorEventListener(
+ proc.mPidFd, EVENT_INPUT | EVENT_ERROR,
+ this::onPhantomProcessFdEvent);
+ mPhantomProcessesPidFds.put(proc.mPidFd.getInt$(), proc);
+ }
+ scheduleTrimPhantomProcessesLocked();
+ return proc;
+ } catch (IllegalStateException e) {
+ return null;
}
-
- ppid = getParentPid(ppid);
}
return null;
}
- private static int getParentPid(int pid) {
- try {
- return Process.getParentPid(pid);
- } catch (Exception e) {
- }
- return -1;
- }
-
private boolean isAppProcess(int pid) {
synchronized (mService.mPidsSelfLocked) {
return mService.mPidsSelfLocked.get(pid) != null;
@@ -346,10 +504,14 @@
synchronized (mLock) {
// refresh the phantom process list with the latest cpu stats results.
mUpdateSeq++;
+
+ // Scan app process's accounting procs
+ lookForPhantomProcessesLocked();
+
for (int i = tracker.countStats() - 1; i >= 0; i--) {
final ProcessCpuTracker.Stats st = tracker.getStats(i);
final PhantomProcessRecord r =
- getOrCreatePhantomProcessIfNeededLocked(st.name, st.uid, st.pid);
+ getOrCreatePhantomProcessIfNeededLocked(st.name, st.uid, st.pid, false);
if (r != null) {
r.mUpdateSeq = mUpdateSeq;
r.mCurrentCputime += st.rel_utime + st.rel_stime;
@@ -392,4 +554,19 @@
proc.dump(pw, prefix + " ");
}
}
+
+ @VisibleForTesting
+ static class Injector {
+ InputStream openCgroupProcs(String path) throws FileNotFoundException, SecurityException {
+ return new FileInputStream(path);
+ }
+
+ int readCgroupProcs(InputStream input, byte[] buf, int offset, int len) throws IOException {
+ return input.read(buf, offset, len);
+ }
+
+ String getProcessName(final int pid) {
+ return PhantomProcessList.getProcessName(pid);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index cf0223b..6f6cad0 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2948,7 +2948,7 @@
// No more processes using this uid, tell clients it is gone.
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"No more processes in " + uidRecord);
- mService.mUidObserverController.enqueueUidChangeLocked(uidRecord, -1,
+ mService.enqueueUidChangeLocked(uidRecord, -1,
UidRecord.CHANGE_GONE);
EventLogTags.writeAmUidStopped(uid);
mActiveUids.remove(uid);
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index 4d9260a..b3488c3 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -23,11 +23,13 @@
import android.app.ActivityManager;
import android.app.ActivityManagerProto;
import android.app.IUidObserver;
+import android.os.Handler;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
@@ -40,158 +42,150 @@
import java.util.ArrayList;
public class UidObserverController {
- private final ActivityManagerService mService;
- final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>();
-
- UidRecord.ChangeItem[] mActiveUidChanges = new UidRecord.ChangeItem[5];
- final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>();
- final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>();
-
- /** Total # of UID change events dispatched, shown in dumpsys. */
- int mUidChangeDispatchCount;
-
/** If a UID observer takes more than this long, send a WTF. */
private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
+ private final Handler mHandler;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>();
+
+ @GuardedBy("mLock")
+ private final SparseArray<ChangeRecord> mPendingUidChanges = new SparseArray<>();
+ @GuardedBy("mLock")
+ private final ArrayList<ChangeRecord> mAvailUidChanges = new ArrayList<>();
+
+ private ChangeRecord[] mActiveUidChanges = new ChangeRecord[5];
+
+ /** Total # of UID change events dispatched, shown in dumpsys. */
+ @GuardedBy("mLock")
+ private int mUidChangeDispatchCount;
+
+ private final Runnable mDispatchRunnable = this::dispatchUidsChanged;
+
/**
* This is for verifying the UID report flow.
*/
- static final boolean VALIDATE_UID_STATES = true;
- final ActiveUids mValidateUids;
+ private static final boolean VALIDATE_UID_STATES = true;
+ private final ActiveUids mValidateUids;
- UidObserverController(ActivityManagerService service) {
- mService = service;
- mValidateUids = new ActiveUids(mService, false /* postChangesToAtm */);
+ UidObserverController(Handler handler) {
+ mHandler = handler;
+ mValidateUids = new ActiveUids(null /* service */, false /* postChangesToAtm */);
}
- @GuardedBy("mService")
void register(IUidObserver observer, int which, int cutpoint, String callingPackage,
int callingUid) {
- mUidObservers.register(observer, new UidObserverRegistration(callingUid,
- callingPackage, which, cutpoint));
+ synchronized (mLock) {
+ mUidObservers.register(observer, new UidObserverRegistration(callingUid,
+ callingPackage, which, cutpoint));
+ }
}
- @GuardedBy("mService")
void unregister(IUidObserver observer) {
- mUidObservers.unregister(observer);
+ synchronized (mLock) {
+ mUidObservers.unregister(observer);
+ }
}
- @GuardedBy("mService")
- final void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
- final UidRecord.ChangeItem pendingChange;
- if (uidRec == null || uidRec.pendingChange == null) {
+ int enqueueUidChange(int uid, int change, int procState, long procStateSeq,
+ int capability, boolean ephemeral) {
+ synchronized (mLock) {
if (mPendingUidChanges.size() == 0) {
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "*** Enqueueing dispatch uid changed!");
}
- mService.mUiHandler.post(this::dispatchUidsChanged);
+ mHandler.post(mDispatchRunnable);
}
- final int size = mAvailUidChanges.size();
- if (size > 0) {
- pendingChange = mAvailUidChanges.remove(size - 1);
- if (DEBUG_UID_OBSERVERS) {
- Slog.i(TAG_UID_OBSERVERS, "Retrieving available item: " + pendingChange);
- }
- } else {
- pendingChange = new UidRecord.ChangeItem();
- if (DEBUG_UID_OBSERVERS) {
- Slog.i(TAG_UID_OBSERVERS, "Allocating new item: " + pendingChange);
- }
- }
- if (uidRec != null) {
- uidRec.pendingChange = pendingChange;
- if ((change & UidRecord.CHANGE_GONE) != 0 && !uidRec.idle) {
- // If this uid is going away, and we haven't yet reported it is gone,
- // then do so now.
- change |= UidRecord.CHANGE_IDLE;
- }
- } else if (uid < 0) {
- throw new IllegalArgumentException("No UidRecord or uid");
- }
- pendingChange.uidRecord = uidRec;
- pendingChange.uid = uidRec != null ? uidRec.uid : uid;
- mPendingUidChanges.add(pendingChange);
- } else {
- pendingChange = uidRec.pendingChange;
- // If there is no change in idle or active state, then keep whatever was pending.
- if ((change & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) {
- change |= (pendingChange.change & (UidRecord.CHANGE_IDLE
- | UidRecord.CHANGE_ACTIVE));
- }
- // If there is no change in cached or uncached state, then keep whatever was pending.
- if ((change & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) {
- change |= (pendingChange.change & (UidRecord.CHANGE_CACHED
- | UidRecord.CHANGE_UNCACHED));
- }
- // If this is a report of the UID being gone, then we shouldn't keep any previous
- // report of it being active or cached. (That is, a gone uid is never active,
- // and never cached.)
- if ((change & UidRecord.CHANGE_GONE) != 0) {
- change &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED);
- if (!uidRec.idle) {
- // If this uid is going away, and we haven't yet reported it is gone,
- // then do so now.
- change |= UidRecord.CHANGE_IDLE;
- }
- }
- }
- pendingChange.change = change;
- pendingChange.processState = uidRec != null
- ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT;
- pendingChange.capability = uidRec != null ? uidRec.setCapability : 0;
- pendingChange.ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
- pendingChange.procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
- if (uidRec != null) {
- uidRec.lastReportedChange = change;
- uidRec.updateLastDispatchedProcStateSeq(change);
- }
- // Directly update the power manager, since we sit on top of it and it is critical
- // it be kept in sync (so wake locks will be held as soon as appropriate).
- if (mService.mLocalPowerManager != null) {
- // TO DO: dispatch cached/uncached changes here, so we don't need to report
- // all proc state changes.
- if ((change & UidRecord.CHANGE_ACTIVE) != 0) {
- mService.mLocalPowerManager.uidActive(pendingChange.uid);
- }
- if ((change & UidRecord.CHANGE_IDLE) != 0) {
- mService.mLocalPowerManager.uidIdle(pendingChange.uid);
- }
- if ((change & UidRecord.CHANGE_GONE) != 0) {
- mService.mLocalPowerManager.uidGone(pendingChange.uid);
+ ChangeRecord changeRecord = mPendingUidChanges.get(uid);
+ if (changeRecord == null) {
+ changeRecord = getOrCreateChangeRecordLocked();
+ mPendingUidChanges.put(uid, changeRecord);
} else {
- mService.mLocalPowerManager.updateUidProcState(pendingChange.uid,
- pendingChange.processState);
+ change = mergeWithPendingChange(change, changeRecord.change);
+ }
+
+ changeRecord.uid = uid;
+ changeRecord.change = change;
+ changeRecord.procState = procState;
+ changeRecord.procStateSeq = procStateSeq;
+ changeRecord.capability = capability;
+ changeRecord.ephemeral = ephemeral;
+
+ return changeRecord.change;
+ }
+ }
+
+ SparseArray<ChangeRecord> getPendingUidChangesForTest() {
+ return mPendingUidChanges;
+ }
+
+ ActiveUids getValidateUidsForTest() {
+ return mValidateUids;
+ }
+
+ @VisibleForTesting
+ static int mergeWithPendingChange(int currentChange, int pendingChange) {
+ // If there is no change in idle or active state, then keep whatever was pending.
+ if ((currentChange & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) {
+ currentChange |= (pendingChange & (UidRecord.CHANGE_IDLE
+ | UidRecord.CHANGE_ACTIVE));
+ }
+ // If there is no change in cached or uncached state, then keep whatever was pending.
+ if ((currentChange & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) {
+ currentChange |= (pendingChange & (UidRecord.CHANGE_CACHED
+ | UidRecord.CHANGE_UNCACHED));
+ }
+ // If this is a report of the UID being gone, then we shouldn't keep any previous
+ // report of it being active or cached. (That is, a gone uid is never active,
+ // and never cached.)
+ if ((currentChange & UidRecord.CHANGE_GONE) != 0) {
+ currentChange &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED);
+ }
+ return currentChange;
+ }
+
+ @GuardedBy("mLock")
+ private ChangeRecord getOrCreateChangeRecordLocked() {
+ final ChangeRecord changeRecord;
+ final int size = mAvailUidChanges.size();
+ if (size > 0) {
+ changeRecord = mAvailUidChanges.remove(size - 1);
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Retrieving available item: " + changeRecord);
+ }
+ } else {
+ changeRecord = new ChangeRecord();
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "Allocating new item: " + changeRecord);
}
}
+ return changeRecord;
}
@VisibleForTesting
void dispatchUidsChanged() {
- int numUidChanges;
- synchronized (mService) {
+ final int numUidChanges;
+ synchronized (mLock) {
numUidChanges = mPendingUidChanges.size();
if (mActiveUidChanges.length < numUidChanges) {
- mActiveUidChanges = new UidRecord.ChangeItem[numUidChanges];
+ mActiveUidChanges = new ChangeRecord[numUidChanges];
}
for (int i = 0; i < numUidChanges; i++) {
- final UidRecord.ChangeItem change = mPendingUidChanges.get(i);
- mActiveUidChanges[i] = change;
- if (change.uidRecord != null) {
- change.uidRecord.pendingChange = null;
- change.uidRecord = null;
- }
+ mActiveUidChanges[i] = mPendingUidChanges.valueAt(i);
}
mPendingUidChanges.clear();
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "*** Delivering " + numUidChanges + " uid changes");
}
+ mUidChangeDispatchCount += numUidChanges;
}
- mUidChangeDispatchCount += numUidChanges;
int i = mUidObservers.beginBroadcast();
- while (i > 0) {
- i--;
+ while (i-- > 0) {
dispatchUidsChangedForObserver(mUidObservers.getBroadcastItem(i),
(UidObserverRegistration) mUidObservers.getBroadcastCookie(i), numUidChanges);
}
@@ -199,7 +193,7 @@
if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) {
for (int j = 0; j < numUidChanges; ++j) {
- final UidRecord.ChangeItem item = mActiveUidChanges[j];
+ final ChangeRecord item = mActiveUidChanges[j];
if ((item.change & UidRecord.CHANGE_GONE) != 0) {
mValidateUids.remove(item.uid);
} else {
@@ -213,14 +207,14 @@
} else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
validateUid.idle = false;
}
- validateUid.setCurProcState(validateUid.setProcState = item.processState);
+ validateUid.setCurProcState(validateUid.setProcState = item.procState);
validateUid.curCapability = validateUid.setCapability = item.capability;
validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
}
}
}
- synchronized (mService) {
+ synchronized (mLock) {
for (int j = 0; j < numUidChanges; j++) {
mAvailUidChanges.add(mActiveUidChanges[j]);
}
@@ -234,7 +228,7 @@
}
try {
for (int j = 0; j < changesSize; j++) {
- UidRecord.ChangeItem item = mActiveUidChanges[j];
+ final ChangeRecord item = mActiveUidChanges[j];
final int change = item.change;
if (change == UidRecord.CHANGE_PROCSTATE
&& (reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
@@ -285,7 +279,7 @@
if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid
- + ": " + item.processState + ": " + item.capability);
+ + ": " + item.procState + ": " + item.capability);
}
boolean doReport = true;
if (reg.mCutpoint >= ActivityManager.MIN_PROCESS_STATE) {
@@ -293,17 +287,17 @@
ActivityManager.PROCESS_STATE_UNKNOWN);
if (lastState != ActivityManager.PROCESS_STATE_UNKNOWN) {
final boolean lastAboveCut = lastState <= reg.mCutpoint;
- final boolean newAboveCut = item.processState <= reg.mCutpoint;
+ final boolean newAboveCut = item.procState <= reg.mCutpoint;
doReport = lastAboveCut != newAboveCut;
} else {
- doReport = item.processState != PROCESS_STATE_NONEXISTENT;
+ doReport = item.procState != PROCESS_STATE_NONEXISTENT;
}
}
if (doReport) {
if (reg.mLastProcStates != null) {
- reg.mLastProcStates.put(item.uid, item.processState);
+ reg.mLastProcStates.put(item.uid, item.procState);
}
- observer.onUidStateChanged(item.uid, item.processState,
+ observer.onUidStateChanged(item.uid, item.procState,
item.procStateSeq, item.capability);
}
}
@@ -320,94 +314,82 @@
}
}
- private boolean isEphemeralLocked(int uid) {
- final String[] packages = mService.mContext.getPackageManager().getPackagesForUid(uid);
- if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid
- return false;
- }
- return mService.getPackageManagerInternalLocked().isPackageEphemeral(
- UserHandle.getUserId(uid), packages[0]);
+ UidRecord getValidateUidRecord(int uid) {
+ return mValidateUids.get(uid);
}
- @GuardedBy("mService")
void dump(PrintWriter pw, String dumpPackage) {
- final int count = mUidObservers.getRegisteredCallbackCount();
- boolean printed = false;
- for (int i = 0; i < count; i++) {
- final UidObserverRegistration reg = (UidObserverRegistration)
- mUidObservers.getRegisteredCallbackCookie(i);
- if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
- if (!printed) {
- pw.println(" mUidObservers:");
- printed = true;
- }
- pw.print(" "); UserHandle.formatUid(pw, reg.mUid);
- pw.print(" "); pw.print(reg.mPkg);
- final IUidObserver observer = mUidObservers.getRegisteredCallbackItem(i);
- pw.print(" "); pw.print(observer.getClass().getTypeName()); pw.print(":");
- if ((reg.mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
- pw.print(" IDLE");
- }
- if ((reg.mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
- pw.print(" ACT");
- }
- if ((reg.mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
- pw.print(" GONE");
- }
- if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
- pw.print(" STATE");
- pw.print(" (cut="); pw.print(reg.mCutpoint);
- pw.print(")");
- }
- pw.println();
- if (reg.mLastProcStates != null) {
- final int size = reg.mLastProcStates.size();
- for (int j = 0; j < size; j++) {
- pw.print(" Last ");
- UserHandle.formatUid(pw, reg.mLastProcStates.keyAt(j));
- pw.print(": "); pw.println(reg.mLastProcStates.valueAt(j));
+ synchronized (mLock) {
+ final int count = mUidObservers.getRegisteredCallbackCount();
+ boolean printed = false;
+ for (int i = 0; i < count; i++) {
+ final UidObserverRegistration reg = (UidObserverRegistration)
+ mUidObservers.getRegisteredCallbackCookie(i);
+ if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
+ if (!printed) {
+ pw.println(" mUidObservers:");
+ printed = true;
}
+ reg.dump(pw, mUidObservers.getRegisteredCallbackItem(i));
}
}
- }
- pw.println();
- pw.print(" mUidChangeDispatchCount=");
- pw.print(mUidChangeDispatchCount);
- pw.println();
- pw.println(" Slow UID dispatches:");
- final int size = mUidObservers.beginBroadcast();
- for (int i = 0; i < size; i++) {
- UidObserverRegistration r =
- (UidObserverRegistration) mUidObservers.getBroadcastCookie(i);
- pw.print(" ");
- pw.print(mUidObservers.getBroadcastItem(i).getClass().getTypeName());
- pw.print(": ");
- pw.print(r.mSlowDispatchCount);
- pw.print(" / Max ");
- pw.print(r.mMaxDispatchTime);
- pw.println("ms");
+ pw.println();
+ pw.print(" mUidChangeDispatchCount=");
+ pw.print(mUidChangeDispatchCount);
+ pw.println();
+ pw.println(" Slow UID dispatches:");
+ for (int i = 0; i < count; i++) {
+ final UidObserverRegistration reg = (UidObserverRegistration)
+ mUidObservers.getRegisteredCallbackCookie(i);
+ pw.print(" ");
+ pw.print(mUidObservers.getRegisteredCallbackItem(i).getClass().getTypeName());
+ pw.print(": ");
+ pw.print(reg.mSlowDispatchCount);
+ pw.print(" / Max ");
+ pw.print(reg.mMaxDispatchTime);
+ pw.println("ms");
+ }
}
- mUidObservers.finishBroadcast();
}
- @GuardedBy("mService")
void dumpDebug(ProtoOutputStream proto, String dumpPackage) {
- final int count = mUidObservers.getRegisteredCallbackCount();
- for (int i = 0; i < count; i++) {
- final UidObserverRegistration reg = (UidObserverRegistration)
- mUidObservers.getRegisteredCallbackCookie(i);
- if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
- reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
+ synchronized (mLock) {
+ final int count = mUidObservers.getRegisteredCallbackCount();
+ for (int i = 0; i < count; i++) {
+ final UidObserverRegistration reg = (UidObserverRegistration)
+ mUidObservers.getRegisteredCallbackCookie(i);
+ if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) {
+ reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS);
+ }
}
}
}
+ boolean dumpValidateUids(PrintWriter pw, String dumpPackage, int dumpAppId,
+ String header, boolean needSep) {
+ return mValidateUids.dump(pw, dumpPackage, dumpAppId, header, needSep);
+ }
+
+ void dumpValidateUidsProto(ProtoOutputStream proto, String dumpPackage,
+ int dumpAppId, long fieldId) {
+ mValidateUids.dumpProto(proto, dumpPackage, dumpAppId, fieldId);
+ }
+
+ static final class ChangeRecord {
+ public int uid;
+ public int change;
+ public int procState;
+ public int capability;
+ public boolean ephemeral;
+ public long procStateSeq;
+ }
+
private static final class UidObserverRegistration {
- final int mUid;
- final String mPkg;
- final int mWhich;
- final int mCutpoint;
+ private final int mUid;
+ private final String mPkg;
+ private final int mWhich;
+ private final int mCutpoint;
/**
* Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}.
@@ -439,10 +421,42 @@
this.mPkg = pkg;
this.mWhich = which;
this.mCutpoint = cutpoint;
- if (cutpoint >= ActivityManager.MIN_PROCESS_STATE) {
- mLastProcStates = new SparseIntArray();
- } else {
- mLastProcStates = null;
+ mLastProcStates = cutpoint >= ActivityManager.MIN_PROCESS_STATE
+ ? new SparseIntArray() : null;
+ }
+
+ void dump(PrintWriter pw, IUidObserver observer) {
+ pw.print(" ");
+ UserHandle.formatUid(pw, mUid);
+ pw.print(" ");
+ pw.print(mPkg);
+ pw.print(" ");
+ pw.print(observer.getClass().getTypeName());
+ pw.print(":");
+ if ((mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
+ pw.print(" IDLE");
+ }
+ if ((mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
+ pw.print(" ACT");
+ }
+ if ((mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
+ pw.print(" GONE");
+ }
+ if ((mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
+ pw.print(" STATE");
+ pw.print(" (cut=");
+ pw.print(mCutpoint);
+ pw.print(")");
+ }
+ pw.println();
+ if (mLastProcStates != null) {
+ final int size = mLastProcStates.size();
+ for (int j = 0; j < size; j++) {
+ pw.print(" Last ");
+ UserHandle.formatUid(pw, mLastProcStates.keyAt(j));
+ pw.print(": ");
+ pw.println(mLastProcStates.valueAt(j));
+ }
}
}
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index c84ccb2..dfd6149 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -107,17 +107,6 @@
UidRecordProto.CHANGE_UNCACHED,
};
- static final class ChangeItem {
- UidRecord uidRecord;
- int uid;
- int change;
- int processState;
- int capability;
- boolean ephemeral;
- long procStateSeq;
- }
-
- ChangeItem pendingChange;
int lastReportedChange;
public UidRecord(int _uid) {
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 0d077c6..68cfc23 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -26,6 +26,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.attention.AttentionManagerInternal;
import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
import android.content.BroadcastReceiver;
@@ -68,6 +69,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Objects;
+import java.util.Set;
/**
* An attention service implementation that runs in System Server process.
@@ -81,21 +83,15 @@
/** Service will unbind if connection is not used for that amount of time. */
private static final long CONNECTION_TTL_MILLIS = 60_000;
- /**
- * We cache the DeviceConfig values to avoid frequent ashmem-related checks; if the cached
- * values are stale for more than this duration we will update the cache.
- */
- @VisibleForTesting static final long DEVICE_CONFIG_MAX_STALENESS_MILLIS = 4 * 60 * 60 * 1000L;
-
- @VisibleForTesting long mLastReadDeviceConfigMillis = Long.MIN_VALUE;
-
/** DeviceConfig flag name, if {@code true}, enables AttentionManagerService features. */
- private static final String KEY_SERVICE_ENABLED = "service_enabled";
+ @VisibleForTesting
+ static final String KEY_SERVICE_ENABLED = "service_enabled";
/** Default value in absence of {@link DeviceConfig} override. */
private static final boolean DEFAULT_SERVICE_ENABLED = true;
- private boolean mIsServiceEnabledCached;
+ @VisibleForTesting
+ boolean mIsServiceEnabled;
/**
* DeviceConfig flag name, describes how much time we consider a result fresh; if the check
@@ -108,7 +104,8 @@
@VisibleForTesting
static final long DEFAULT_STALE_AFTER_MILLIS = 1_000;
- private long mStaleAfterMillisCached;
+ @VisibleForTesting
+ long mStaleAfterMillis;
/** The size of the buffer that stores recent attention check results. */
@VisibleForTesting
@@ -156,6 +153,11 @@
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
mContext.registerReceiver(new ScreenStateReceiver(),
new IntentFilter(Intent.ACTION_SCREEN_OFF));
+
+ readValuesFromDeviceConfig();
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ ActivityThread.currentApplication().getMainExecutor(),
+ (properties) -> onDeviceConfigChange(properties.getKeyset()));
}
}
@@ -179,17 +181,9 @@
return mComponentName != null;
}
- /**
- * Returns {@code true} if attention service is supported on this device.
- */
- @VisibleForTesting
- protected boolean isAttentionServiceSupported() {
- return isServiceEnabled();
- }
-
- private boolean isServiceEnabled() {
- ensureDeviceConfigCachedValuesFreshness();
- return mIsServiceEnabledCached;
+ private boolean getIsServiceEnabled() {
+ return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, KEY_SERVICE_ENABLED,
+ DEFAULT_SERVICE_ENABLED);
}
/**
@@ -198,39 +192,38 @@
*/
@VisibleForTesting
protected long getStaleAfterMillis() {
- ensureDeviceConfigCachedValuesFreshness();
- return mStaleAfterMillisCached;
- }
-
- @VisibleForTesting
- protected void ensureDeviceConfigCachedValuesFreshness() {
- final long now = SystemClock.elapsedRealtime();
- final long whenBecomesStale =
- mLastReadDeviceConfigMillis + DEVICE_CONFIG_MAX_STALENESS_MILLIS;
- if (now < whenBecomesStale) {
- if (DEBUG) {
- Slog.d(LOG_TAG,
- "Cached values are still fresh. Refreshed at=" + mLastReadDeviceConfigMillis
- + ", now=" + now);
- }
- return;
- }
-
- mIsServiceEnabledCached = DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE,
- KEY_SERVICE_ENABLED,
- DEFAULT_SERVICE_ENABLED);
-
final long millis = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
KEY_STALE_AFTER_MILLIS,
DEFAULT_STALE_AFTER_MILLIS);
+
if (millis < 0 || millis > 10_000) {
Slog.w(LOG_TAG, "Bad flag value supplied for: " + KEY_STALE_AFTER_MILLIS);
- mStaleAfterMillisCached = DEFAULT_STALE_AFTER_MILLIS;
- } else {
- mStaleAfterMillisCached = millis;
+ return DEFAULT_STALE_AFTER_MILLIS;
}
- mLastReadDeviceConfigMillis = now;
+ return millis;
+ }
+
+ private void onDeviceConfigChange(@NonNull Set<String> keys) {
+ for (String key : keys) {
+ switch (key) {
+ case KEY_SERVICE_ENABLED:
+ case KEY_STALE_AFTER_MILLIS:
+ readValuesFromDeviceConfig();
+ return;
+ default:
+ Slog.i(LOG_TAG, "Ignoring change on " + key);
+ }
+ }
+ }
+
+ private void readValuesFromDeviceConfig() {
+ mIsServiceEnabled = getIsServiceEnabled();
+ mStaleAfterMillis = getStaleAfterMillis();
+
+ Slog.i(LOG_TAG, "readValuesFromDeviceConfig():"
+ + "\nmIsServiceEnabled=" + mIsServiceEnabled
+ + "\nmStaleAfterMillis=" + mStaleAfterMillis);
}
/**
@@ -246,7 +239,7 @@
boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) {
Objects.requireNonNull(callbackInternal);
- if (!isAttentionServiceSupported()) {
+ if (!mIsServiceEnabled) {
Slog.w(LOG_TAG, "Trying to call checkAttention() on an unsupported device.");
return false;
}
@@ -272,7 +265,7 @@
// throttle frequent requests
final AttentionCheckCache cache = mAttentionCheckCacheBuffer == null ? null
: mAttentionCheckCacheBuffer.getLast();
- if (cache != null && now < cache.mLastComputed + getStaleAfterMillis()) {
+ if (cache != null && now < cache.mLastComputed + mStaleAfterMillis) {
callbackInternal.onSuccess(cache.mResult, cache.mTimestamp);
return true;
}
@@ -379,7 +372,8 @@
private void dumpInternal(IndentingPrintWriter ipw) {
ipw.println("Attention Manager Service (dumpsys attention) state:\n");
- ipw.println("isServiceEnabled=" + isServiceEnabled());
+ ipw.println("isServiceEnabled=" + mIsServiceEnabled);
+ ipw.println("mStaleAfterMillis=" + mStaleAfterMillis);
ipw.println("AttentionServicePackageName=" + getServiceConfigPackage(mContext));
ipw.println("Resolved component:");
if (mComponentName != null) {
@@ -403,7 +397,7 @@
private final class LocalService extends AttentionManagerInternal {
@Override
public boolean isAttentionServiceSupported() {
- return AttentionManagerService.this.isAttentionServiceSupported();
+ return AttentionManagerService.this.mIsServiceEnabled;
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/HardwareAuthTokenUtils.java b/services/core/java/com/android/server/biometrics/HardwareAuthTokenUtils.java
new file mode 100644
index 0000000..eff4da3
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/HardwareAuthTokenUtils.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import static java.nio.ByteOrder.LITTLE_ENDIAN;
+
+import android.hardware.keymaster.HardwareAuthToken;
+import android.hardware.keymaster.Timestamp;
+
+import java.nio.ByteOrder;
+
+/**
+ * Utilities for converting between old and new HardwareAuthToken types. See
+ * {@link HardwareAuthToken}.
+ */
+public class HardwareAuthTokenUtils {
+ public static byte[] toByteArray(HardwareAuthToken hat) {
+ final byte[] array = new byte[69];
+
+ // Version, first byte. Used in hw_auth_token.h but not HardwareAuthToken
+ array[0] = 0;
+
+ // Challenge, 1:8.
+ writeLong(hat.challenge, array, 1 /* offset */);
+
+ // UserId, 9:16.
+ writeLong(hat.userId, array, 9 /* offset */);
+
+ // AuthenticatorId, 17:24.
+ writeLong(hat.authenticatorId, array, 17 /* offset */);
+
+ // AuthenticatorType, 25:28.
+ writeInt(flipIfNativelyLittle(hat.authenticatorType), array, 25 /* offset */);
+
+ // Timestamp, 29:36.
+ writeLong(flipIfNativelyLittle(hat.timestamp.milliSeconds), array, 29 /* offset */);
+
+ // MAC, 37:69. Byte array.
+ System.arraycopy(hat.mac, 0 /* srcPos */, array, 37 /* destPos */, hat.mac.length);
+
+ return array;
+ }
+
+ public static HardwareAuthToken toHardwareAuthToken(byte[] array) {
+ final HardwareAuthToken hardwareAuthToken = new HardwareAuthToken();
+
+ // First byte is version, which doesn't not exist in HardwareAuthToken anymore
+ // Next 8 bytes is the challenge.
+ hardwareAuthToken.challenge = getLong(array, 1 /* offset */);
+
+ // Next 8 bytes is the userId
+ hardwareAuthToken.userId = getLong(array, 9 /* offset */);
+
+ // Next 8 bytes is the authenticatorId.
+ hardwareAuthToken.authenticatorId = getLong(array, 17 /* offset */);
+
+ // Next 4 bytes is the authenticatorType.
+ hardwareAuthToken.authenticatorType = flipIfNativelyLittle(getInt(array, 25 /* offset */));
+
+ // Next 8 bytes is the timestamp.
+ final Timestamp timestamp = new Timestamp();
+ timestamp.milliSeconds = flipIfNativelyLittle(getLong(array, 29 /* offset */));
+ hardwareAuthToken.timestamp = timestamp;
+
+ // Last 32 bytes is the mac, 37:69
+ hardwareAuthToken.mac = new byte[32];
+ System.arraycopy(array, 37 /* srcPos */,
+ hardwareAuthToken.mac,
+ 0 /* destPos */,
+ 32 /* length */);
+
+ return hardwareAuthToken;
+ }
+
+ private static long flipIfNativelyLittle(long l) {
+ if (LITTLE_ENDIAN == ByteOrder.nativeOrder()) {
+ return Long.reverseBytes(l);
+ }
+ return l;
+ }
+
+ private static int flipIfNativelyLittle(int i) {
+ if (LITTLE_ENDIAN == ByteOrder.nativeOrder()) {
+ return Integer.reverseBytes(i);
+ }
+ return i;
+ }
+
+ private static void writeLong(long l, byte[] dest, int offset) {
+ dest[offset + 0] = (byte) l;
+ dest[offset + 1] = (byte) (l >> 8);
+ dest[offset + 2] = (byte) (l >> 16);
+ dest[offset + 3] = (byte) (l >> 24);
+ dest[offset + 4] = (byte) (l >> 32);
+ dest[offset + 5] = (byte) (l >> 40);
+ dest[offset + 6] = (byte) (l >> 48);
+ dest[offset + 7] = (byte) (l >> 56);
+ }
+
+ private static void writeInt(int i, byte[] dest, int offset) {
+ dest[offset + 0] = (byte) i;
+ dest[offset + 1] = (byte) (i >> 8);
+ dest[offset + 2] = (byte) (i >> 16);
+ dest[offset + 3] = (byte) (i >> 24);
+ }
+
+ private static long getLong(byte[] array, int offset) {
+ long result = 0;
+ // Lowest bit is LSB
+ for (int i = 0; i < 8; i++) {
+ result += (long) ((array[i + offset] & 0xffL) << (8 * i));
+ }
+ return result;
+ }
+
+ private static int getInt(byte[] array, int offset) {
+ int result = 0;
+ // Lowest bit is LSB
+ for (int i = 0; i < 4; i++) {
+ result += (int) (((int) array[i + offset] & 0xff) << (8 * i));
+ }
+ return result;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
index 5b18349..a8250ac 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
@@ -16,6 +16,7 @@
package com.android.server.biometrics.sensors;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.os.AsyncTask;
@@ -62,11 +63,6 @@
protected abstract String getBiometricsTag();
/**
- * @return The file where the biometric metadata should be stored.
- */
- protected abstract String getBiometricFile();
-
- /**
* @return The resource for the name template, this is used to generate the default name.
*/
protected abstract int getNameTemplateResource();
@@ -88,8 +84,8 @@
throws IOException, XmlPullParserException;
- public BiometricUserState(Context context, int userId) {
- mFile = getFileForUser(userId);
+ public BiometricUserState(Context context, int userId, @NonNull String fileName) {
+ mFile = getFileForUser(userId, fileName);
mContext = context;
synchronized (this) {
readStateSyncLocked();
@@ -159,8 +155,8 @@
return true;
}
- private File getFileForUser(int userId) {
- return new File(Environment.getUserSystemDirectory(userId), getBiometricFile());
+ private File getFileForUser(int userId, @NonNull String fileName) {
+ return new File(Environment.getUserSystemDirectory(userId), fileName);
}
private void scheduleWriteStateLocked() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index cb7db92..c87f62f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -89,7 +89,8 @@
}
}
- void onError(int sensorId, int cookie, int error, int vendorCode) throws RemoteException {
+ public void onError(int sensorId, int cookie, int error, int vendorCode)
+ throws RemoteException {
if (mSensorReceiver != null) {
mSensorReceiver.onError(sensorId, cookie, error, vendorCode);
} else if (mFaceServiceReceiver != null) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 5c08bce..e738d17 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -57,7 +57,7 @@
}
private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
- private final BiometricUtils mBiometricUtils;
+ private final BiometricUtils<S> mBiometricUtils;
private final Map<Integer, Long> mAuthenticatorIds;
private final List<S> mEnrolledList;
private ClientMonitor<T> mCurrentTask;
@@ -95,15 +95,15 @@
protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context,
LazyDaemon<T> lazyDaemon, IBinder token, int userId, String owner,
- List<S> enrolledList, BiometricUtils utils, int sensorId);
+ List<S> enrolledList, BiometricUtils<S> utils, int sensorId);
- protected abstract RemovalClient<T> getRemovalClient(Context context, LazyDaemon<T> lazyDaemon,
- IBinder token, int biometricId, int userId, String owner, BiometricUtils utils,
- int sensorId, Map<Integer, Long> authenticatorIds);
+ protected abstract RemovalClient<S, T> getRemovalClient(Context context,
+ LazyDaemon<T> lazyDaemon, IBinder token, int biometricId, int userId, String owner,
+ BiometricUtils<S> utils, int sensorId, Map<Integer, Long> authenticatorIds);
protected InternalCleanupClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
int userId, @NonNull String owner, int sensorId, int statsModality,
- @NonNull List<S> enrolledList, @NonNull BiometricUtils utils,
+ @NonNull List<S> enrolledList, @NonNull BiometricUtils<S> utils,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */,
userId, owner, 0 /* cookie */, sensorId, statsModality,
@@ -153,7 +153,7 @@
+ mCurrentTask.getClass().getSimpleName());
return;
}
- ((RemovalClient<T>) mCurrentTask).onRemoved(identifier, remaining);
+ ((RemovalClient<S, T>) mCurrentTask).onRemoved(identifier, remaining);
}
@Override
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/services/core/java/com/android/server/biometrics/sensors/LockoutConsumer.java
similarity index 64%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to services/core/java/com/android/server/biometrics/sensors/LockoutConsumer.java
index 71cd0a7..153bd46 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutConsumer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 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,6 +14,12 @@
* limitations under the License.
*/
-package android.media.tv;
+package com.android.server.biometrics.sensors;
-parcelable TvChannelInfo;
+/**
+ * Interface that clients interested/eligible for lockout events should implement.
+ */
+public interface LockoutConsumer {
+ void onLockoutTimed(long durationMillis);
+ void onLockoutPermanent();
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index 1348f79..f79abd5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -17,6 +17,7 @@
package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
@@ -29,17 +30,18 @@
/**
* A class to keep track of the remove state for a given client.
*/
-public abstract class RemovalClient<T> extends ClientMonitor<T> implements RemovalConsumer {
+public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier, T>
+ extends ClientMonitor<T> implements RemovalConsumer {
private static final String TAG = "Biometrics/RemovalClient";
protected final int mBiometricId;
- private final BiometricUtils mBiometricUtils;
+ private final BiometricUtils<S> mBiometricUtils;
private final Map<Integer, Long> mAuthenticatorIds;
public RemovalClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
- int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+ int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<S> utils,
int sensorId, @NonNull Map<Integer, Long> authenticatorIds, int statsModality) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
statsModality, BiometricsProtoEnums.ACTION_REMOVE,
@@ -63,8 +65,8 @@
}
@Override
- public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) {
- if (identifier.getBiometricId() != 0) {
+ public void onRemoved(@Nullable BiometricAuthenticator.Identifier identifier, int remaining) {
+ if (identifier != null) {
mBiometricUtils.removeBiometricForUser(getContext(), getTargetUserId(),
identifier.getBiometricId());
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java b/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
index 3d7988c..0aba7e4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java
@@ -16,6 +16,7 @@
package com.android.server.biometrics.sensors;
+import android.annotation.Nullable;
import android.hardware.biometrics.BiometricAuthenticator;
/**
@@ -28,5 +29,5 @@
* @param remaining number of templates that still need to be removed before the operation in
* the HAL is complete (e.g. when removing all templates).
*/
- void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining);
+ void onRemoved(@Nullable BiometricAuthenticator.Identifier identifier, int remaining);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
index c2d4c15..f7998ee 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
@@ -219,8 +219,7 @@
removalConsumer.onRemoved(face, remaining);
}
} else {
- final Face face = new Face("", 0 /* identifier */, deviceId);
- removalConsumer.onRemoved(face, 0 /* remaining */);
+ removalConsumer.onRemoved(null, 0 /* remaining */);
}
Settings.Secure.putIntForUser(mContext.getContentResolver(),
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
index 3e843ca..989b5c9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
@@ -23,6 +23,7 @@
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.Status;
+import android.hardware.face.Face;
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.NativeHandle;
@@ -53,9 +54,9 @@
FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
- @NonNull int[] disabledFeatures, int timeoutSec, @Nullable NativeHandle surfaceHandle,
- int sensorId) {
+ @NonNull byte[] hardwareAuthToken, @NonNull String owner,
+ @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
+ @Nullable NativeHandle surfaceHandle, int sensorId) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
false /* shouldVibrate */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
index 93f35f4..7626587 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
@@ -40,7 +40,7 @@
FaceInternalCleanupClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
+ int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils<Face> utils,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE,
enrolledList, utils, authenticatorIds);
@@ -49,15 +49,15 @@
@Override
protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context,
LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token, int userId, String owner,
- List<Face> enrolledList, BiometricUtils utils, int sensorId) {
+ List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId) {
return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
enrolledList, utils, sensorId);
}
@Override
- protected RemovalClient<IBiometricsFace> getRemovalClient(Context context,
+ protected RemovalClient<Face, IBiometricsFace> getRemovalClient(Context context,
LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token,
- int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
+ int biometricId, int userId, String owner, BiometricUtils<Face> utils, int sensorId,
Map<Integer, Long> authenticatorIds) {
// Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
index 32d4832..4166dff 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
@@ -40,8 +40,8 @@
FaceInternalEnumerateClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, int userId,
- @NonNull String owner, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils,
- int sensorId) {
+ @NonNull String owner, @NonNull List<Face> enrolledList,
+ @NonNull BiometricUtils<Face> utils, int sensorId) {
super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
BiometricsProtoEnums.MODALITY_FACE);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
index dde5aba..31ae3a3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.Face;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -34,12 +35,12 @@
* Face-specific removal client supporting the {@link android.hardware.biometrics.face.V1_0}
* and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
*/
-class FaceRemovalClient extends RemovalClient<IBiometricsFace> {
+class FaceRemovalClient extends RemovalClient<Face, IBiometricsFace> {
private static final String TAG = "FaceRemovalClient";
FaceRemovalClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
- int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils,
+ int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<Face> utils,
int sensorId, @NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
authenticatorIds, BiometricsProtoEnums.MODALITY_FACE);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
index 42c7d16..d30c3c8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
@@ -17,7 +17,6 @@
package com.android.server.biometrics.sensors.face;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.face.Face;
import android.util.AtomicFile;
import android.util.Slog;
@@ -41,10 +40,9 @@
* Class managing the set of faces per user across device reboots.
* @hide
*/
-public class FaceUserState extends BiometricUserState {
+public class FaceUserState extends BiometricUserState<Face> {
private static final String TAG = "FaceState";
- private static final String FACE_FILE = "settings_face.xml";
private static final String TAG_FACES = "faces";
private static final String TAG_FACE = "face";
@@ -52,8 +50,8 @@
private static final String ATTR_FACE_ID = "faceId";
private static final String ATTR_DEVICE_ID = "deviceId";
- public FaceUserState(Context ctx, int userId) {
- super(ctx, userId);
+ public FaceUserState(Context ctx, int userId, String fileName) {
+ super(ctx, userId, fileName);
}
@Override
@@ -62,29 +60,14 @@
}
@Override
- protected String getBiometricFile() {
- return FACE_FILE;
- }
-
- @Override
protected int getNameTemplateResource() {
return com.android.internal.R.string.face_name_template;
}
@Override
- public void addBiometric(BiometricAuthenticator.Identifier identifier) {
- if (identifier instanceof Face) {
- super.addBiometric(identifier);
- } else {
- Slog.w(TAG, "Attempted to add non-face identifier");
- }
- }
-
- @Override
- protected ArrayList getCopy(ArrayList array) {
- ArrayList<Face> result = new ArrayList<>(array.size());
- for (int i = 0; i < array.size(); i++) {
- Face f = (Face) array.get(i);
+ protected ArrayList<Face> getCopy(ArrayList<Face> array) {
+ final ArrayList<Face> result = new ArrayList<>();
+ for (Face f : array) {
result.add(new Face(f.getName(), f.getBiometricId(), f.getDeviceId()));
}
return result;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
index 0197028..a0ffe58 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java
@@ -16,6 +16,7 @@
package com.android.server.biometrics.sensors.face;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.face.Face;
@@ -30,24 +31,59 @@
/**
* Utility class for dealing with faces and face settings.
*/
-public class FaceUtils implements BiometricUtils {
+public class FaceUtils implements BiometricUtils<Face> {
private static final Object sInstanceLock = new Object();
- private static FaceUtils sInstance;
+ // Map<SensorId, FaceUtils>
+ private static SparseArray<FaceUtils> sInstances;
+ private static final String LEGACY_FACE_FILE = "settings_face.xml";
@GuardedBy("this")
- private final SparseArray<FaceUserState> mUsers = new SparseArray<>();
+ private final SparseArray<FaceUserState> mUserStates;
+ private final String mFileName;
- public static FaceUtils getInstance() {
- synchronized (sInstanceLock) {
- if (sInstance == null) {
- sInstance = new FaceUtils();
- }
- }
- return sInstance;
+ public static FaceUtils getInstance(int sensorId) {
+ // Specify a null fileName to use an auto-generated sensorId-specific filename.
+ return getInstance(sensorId, null /* fileName */);
}
- private FaceUtils() {
+ /**
+ * Retrieves an instance for the specified sensorId. If the fileName is null, a default
+ * filename (e.g. settings_face_<sensorId>.xml will be generated.
+ *
+ * Specifying an explicit fileName allows for backward compatibility with legacy devices,
+ * where everything is stored in settings_face.xml.
+ */
+ private static FaceUtils getInstance(int sensorId, @Nullable String fileName) {
+ final FaceUtils utils;
+ synchronized (sInstanceLock) {
+ if (sInstances == null) {
+ sInstances = new SparseArray<>();
+ }
+ if (sInstances.get(sensorId) == null) {
+ if (fileName == null) {
+ fileName = "settings_face_" + sensorId + ".xml";
+ }
+ sInstances.put(sensorId, new FaceUtils(fileName));
+ }
+ utils = sInstances.get(sensorId);
+ }
+ return utils;
+ }
+
+ /**
+ * Legacy getter for {@link android.hardware.biometrics.face.V1_0} and its extended subclasses,
+ * which do not support a well defined sensorId from the HAL.
+ */
+ public static FaceUtils getInstance() {
+ // Note that sensorId for legacy services can be hard-coded to 0 since it's only used
+ // to index into the sensor states map.
+ return getInstance(0 /* sensorId */, LEGACY_FACE_FILE);
+ }
+
+ private FaceUtils(String fileName) {
+ mUserStates = new SparseArray<>();
+ mFileName = fileName;
}
@Override
@@ -56,9 +92,8 @@
}
@Override
- public void addBiometricForUser(Context ctx, int userId,
- BiometricAuthenticator.Identifier identifier) {
- getStateForUser(ctx, userId).addBiometric(identifier);
+ public void addBiometricForUser(Context ctx, int userId, Face face) {
+ getStateForUser(ctx, userId).addBiometric(face);
}
@Override
@@ -82,10 +117,10 @@
private FaceUserState getStateForUser(Context ctx, int userId) {
synchronized (this) {
- FaceUserState state = mUsers.get(userId);
+ FaceUserState state = mUserStates.get(userId);
if (state == null) {
- state = new FaceUserState(ctx, userId);
- mUsers.put(userId, state);
+ state = new FaceUserState(ctx, userId, mFileName);
+ mUserStates.put(userId, state);
}
return state;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 165755a..1f71d78 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -20,6 +20,7 @@
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
+import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
@@ -32,6 +33,7 @@
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
@@ -50,6 +52,8 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.Pair;
import android.util.Slog;
@@ -88,11 +92,74 @@
private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
private final LockPatternUtils mLockPatternUtils;
@NonNull private List<ServiceProvider> mServiceProviders;
+ @NonNull private final ArrayMap<Integer, TestSession> mTestSessions;
+
+ private final class TestSession extends ITestSession.Stub {
+ private final int mSensorId;
+
+ TestSession(int sensorId) {
+ mSensorId = sensorId;
+ }
+
+ @Override
+ public void enableTestHal(boolean enableTestHal) {
+ Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+ }
+
+ @Override
+ public void startEnroll(int userId) {
+ Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+ }
+
+ @Override
+ public void finishEnroll(int userId) {
+ Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+ }
+
+ @Override
+ public void acceptAuthentication(int userId) {
+ Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+ }
+
+ @Override
+ public void rejectAuthentication(int userId) {
+ Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+ }
+
+ @Override
+ public void notifyAcquired(int userId) {
+ Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+ }
+
+ @Override
+ public void notifyError(int userId) {
+ Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+ }
+
+ @Override
+ public void cleanupInternalState(int userId) {
+ Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+ }
+ }
/**
* Receives the incoming binder calls from FingerprintManager.
*/
private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+ @Override
+ public ITestSession createTestSession(int sensorId, String opPackageName) {
+ Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+
+ final TestSession session;
+ synchronized (mTestSessions) {
+ if (!mTestSessions.containsKey(sensorId)) {
+ mTestSessions.put(sensorId, new TestSession(sensorId));
+ }
+ session = mTestSessions.get(sensorId);
+ }
+ return session;
+ }
+
@Override // Binder call
public List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(
String opPackageName) {
@@ -107,7 +174,7 @@
}
@Override // Binder call
- public void generateChallenge(IBinder token, int sensorId,
+ public void generateChallenge(IBinder token, int sensorId, int userId,
IFingerprintServiceReceiver receiver, String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
@@ -117,20 +184,22 @@
return;
}
- provider.scheduleGenerateChallenge(sensorId, token, receiver, opPackageName);
+ provider.scheduleGenerateChallenge(sensorId, userId, token, receiver, opPackageName);
}
@Override // Binder call
- public void revokeChallenge(IBinder token, String opPackageName) {
+ public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName,
+ long challenge) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ final ServiceProvider provider = getProviderForSensor(sensorId);
if (provider == null) {
- Slog.w(TAG, "Null provider for revokeChallenge");
+ Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId);
return;
}
- provider.second.scheduleRevokeChallenge(provider.first, token, opPackageName);
+ provider.scheduleRevokeChallenge(sensorId, userId, token, opPackageName,
+ challenge);
}
@Override // Binder call
@@ -553,6 +622,7 @@
mLockoutResetDispatcher = new LockoutResetDispatcher(context);
mLockPatternUtils = new LockPatternUtils(context);
mServiceProviders = new ArrayList<>();
+ mTestSessions = new ArrayMap<>();
initializeAidlHals();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
index 56312bc..e56c8d5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
@@ -17,7 +17,6 @@
package com.android.server.biometrics.sensors.fingerprint;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.fingerprint.Fingerprint;
import android.util.AtomicFile;
import android.util.Slog;
@@ -40,10 +39,9 @@
* Class managing the set of fingerprint per user across device reboots.
* @hide
*/
-public class FingerprintUserState extends BiometricUserState {
+public class FingerprintUserState extends BiometricUserState<Fingerprint> {
private static final String TAG = "FingerprintState";
- private static final String FINGERPRINT_FILE = "settings_fingerprint.xml";
private static final String TAG_FINGERPRINTS = "fingerprints";
private static final String TAG_FINGERPRINT = "fingerprint";
@@ -52,8 +50,8 @@
private static final String ATTR_FINGER_ID = "fingerId";
private static final String ATTR_DEVICE_ID = "deviceId";
- public FingerprintUserState(Context context, int userId) {
- super(context, userId);
+ public FingerprintUserState(Context context, int userId, String fileName) {
+ super(context, userId, fileName);
}
@Override
@@ -62,29 +60,14 @@
}
@Override
- protected String getBiometricFile() {
- return FINGERPRINT_FILE;
- }
-
- @Override
protected int getNameTemplateResource() {
return com.android.internal.R.string.fingerprint_name_template;
}
@Override
- public void addBiometric(BiometricAuthenticator.Identifier identifier) {
- if (identifier instanceof Fingerprint) {
- super.addBiometric(identifier);
- } else {
- Slog.w(TAG, "Attempted to add non-fingerprint identifier");
- }
- }
-
- @Override
- protected ArrayList getCopy(ArrayList array) {
- ArrayList<Fingerprint> result = new ArrayList<>();
- for (int i = 0; i < array.size(); i++) {
- Fingerprint fp = (Fingerprint) array.get(i);
+ protected ArrayList<Fingerprint> getCopy(ArrayList<Fingerprint> array) {
+ final ArrayList<Fingerprint> result = new ArrayList<>();
+ for (Fingerprint fp : array) {
result.add(new Fingerprint(fp.getName(), fp.getGroupId(), fp.getBiometricId(),
fp.getDeviceId()));
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
index f0bfe12..6da8650 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
@@ -16,8 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint;
+import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.fingerprint.Fingerprint;
import android.text.TextUtils;
import android.util.SparseArray;
@@ -30,24 +30,62 @@
/**
* Utility class for dealing with fingerprints and fingerprint settings.
*/
-public class FingerprintUtils implements BiometricUtils {
+public class FingerprintUtils implements BiometricUtils<Fingerprint> {
private static final Object sInstanceLock = new Object();
- private static FingerprintUtils sInstance;
+ // Map<SensorId, FingerprintUtils>
+ private static SparseArray<FingerprintUtils> sInstances;
+ private static final String LEGACY_FINGERPRINT_FILE = "settings_fingerprint.xml";
@GuardedBy("this")
- private final SparseArray<FingerprintUserState> mUsers = new SparseArray<>();
+ private final SparseArray<FingerprintUserState> mUserStates;
+ private final String mFileName;
- public static FingerprintUtils getInstance() {
- synchronized (sInstanceLock) {
- if (sInstance == null) {
- sInstance = new FingerprintUtils();
- }
- }
- return sInstance;
+ /**
+ * Retrieves an instance for the specified sensorId.
+ */
+ public static FingerprintUtils getInstance(int sensorId) {
+ // Specify a null fileName to use an auto-generated sensorId-specific filename.
+ return getInstance(sensorId, null /* fileName */);
}
- private FingerprintUtils() {
+ /**
+ * Retrieves an instance for the specified sensorId. If the fileName is null, a default
+ * filename (e.g. settings_fingerprint_<sensorId>.xml will be generated.
+ *
+ * Specifying an explicit fileName allows for backward compatibility with legacy devices,
+ * where everything is stored in settings_fingerprint.xml.
+ */
+ private static FingerprintUtils getInstance(int sensorId, @Nullable String fileName) {
+ final FingerprintUtils utils;
+ synchronized (sInstanceLock) {
+ if (sInstances == null) {
+ sInstances = new SparseArray<>();
+ }
+ if (sInstances.get(sensorId) == null) {
+ if (fileName == null) {
+ fileName = "settings_fingerprint_" + sensorId + ".xml";
+ }
+ sInstances.put(sensorId, new FingerprintUtils(fileName));
+ }
+ utils = sInstances.get(sensorId);
+ }
+ return utils;
+ }
+
+ /**
+ * Legacy getter for {@link android.hardware.biometrics.fingerprint.V2_1} ands its extended
+ * subclasses, which do not support a well defined sensorId from the HAL.
+ */
+ public static FingerprintUtils getInstance() {
+ // Note that sensorId for legacy services can be hard-coded to 0 since it's only used
+ // to index into the sensor states map.
+ return getInstance(0 /* sensorId */, LEGACY_FINGERPRINT_FILE);
+ }
+
+ private FingerprintUtils(String fileName) {
+ mUserStates = new SparseArray<>();
+ mFileName = fileName;
}
@Override
@@ -56,9 +94,8 @@
}
@Override
- public void addBiometricForUser(Context context, int userId,
- BiometricAuthenticator.Identifier identifier) {
- getStateForUser(context, userId).addBiometric(identifier);
+ public void addBiometricForUser(Context context, int userId, Fingerprint fingerprint) {
+ getStateForUser(context, userId).addBiometric(fingerprint);
}
@Override
@@ -83,10 +120,10 @@
private FingerprintUserState getStateForUser(Context ctx, int userId) {
synchronized (this) {
- FingerprintUserState state = mUsers.get(userId);
+ FingerprintUserState state = mUserStates.get(userId);
if (state == null) {
- state = new FingerprintUserState(ctx, userId);
- mUsers.put(userId, state);
+ state = new FingerprintUserState(ctx, userId, mFileName);
+ mUserStates.put(userId, state);
}
return state;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 1162c9c..c2315fd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -64,11 +64,11 @@
void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken);
- void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token,
+ void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, String opPackageName);
- void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
- @NonNull String opPackageName);
+ void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
+ @NonNull String opPackageName, long challenge);
void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
new file mode 100644
index 0000000..8acf4f5
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.TaskStackListener;
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
+
+import java.util.ArrayList;
+
+/**
+ * Fingerprint-specific authentication client supporting the
+ * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
+ */
+class FingerprintAuthenticationClient extends AuthenticationClient<ISession> implements
+ Udfps, LockoutConsumer {
+ private static final String TAG = "FingerprintAuthenticationClient";
+
+ @NonNull private final LockoutCache mLockoutCache;
+ @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+ @Nullable private ICancellationSignal mCancellationSignal;
+
+ FingerprintAuthenticationClient(@NonNull Context context,
+ @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+ @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
+ boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
+ int sensorId, boolean isStrongBiometric, int statsClient,
+ @Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache,
+ @Nullable IUdfpsOverlayController udfpsOverlayController) {
+ super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner,
+ cookie, requireConfirmation, sensorId, isStrongBiometric,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
+ lockoutCache);
+ mLockoutCache = lockoutCache;
+ mUdfpsOverlayController = udfpsOverlayController;
+ }
+
+ @Override
+ public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
+ boolean authenticated, ArrayList<Byte> token) {
+ super.onAuthenticated(identifier, authenticated, token);
+
+ if (authenticated) {
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mCallback.onClientFinished(this, true /* success */);
+ }
+ }
+
+ @Override
+ protected void startHalOperation() {
+ UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ try {
+ mCancellationSignal = getFreshDaemon().authenticate(mSequentialId, mOperationId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ @Override
+ protected void stopHalOperation() {
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ @Override
+ public void onPointerDown(int x, int y, float minor, float major) {
+ try {
+ getFreshDaemon().onPointerDown(0 /* pointerId */, x, y, minor, major);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onPointerUp() {
+ try {
+ getFreshDaemon().onPointerUp(0 /* pointerId */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onLockoutTimed(long durationMillis) {
+ mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
+ // Lockout metrics are logged as an error code.
+ final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
+ logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
+
+ try {
+ getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
+ public void onLockoutPermanent() {
+ mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT);
+ // Lockout metrics are logged as an error code.
+ final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
+ logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId());
+
+ try {
+ getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
new file mode 100644
index 0000000..3398323
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
+
+/**
+ * Performs fingerprint detection without exposing any matching information (e.g. accept/reject
+ * have the same haptic, lockout counter is not increased).
+ */
+class FingerprintDetectClient extends AcquisitionClient<ISession> {
+
+ private static final String TAG = "FingerprintDetectClient";
+
+ private final boolean mIsStrongBiometric;
+ @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+
+ @Nullable private ICancellationSignal mCancellationSignal;
+
+ FingerprintDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
+ @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
+ @NonNull String owner, int sensorId,
+ @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric,
+ int statsClient) {
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT, BiometricsProtoEnums.ACTION_AUTHENTICATE,
+ statsClient);
+ mIsStrongBiometric = isStrongBiometric;
+ mUdfpsOverlayController = udfpsOverlayController;
+ }
+
+ @Override
+ protected void stopHalOperation() {
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ @Override
+ protected void startHalOperation() {
+ UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ try {
+ mCancellationSignal = getFreshDaemon().detectInteraction(mSequentialId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting finger detect", e);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ void onInteractionDetected() {
+ vibrateSuccess();
+
+ try {
+ getListener().onDetected(getSensorId(), getTargetUserId(), mIsStrongBiometric);
+ mCallback.onClientFinished(this, true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when sending onDetected", e);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index f91a119..437ecd7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -21,14 +21,16 @@
import android.content.Context;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.hardware.keymaster.HardwareAuthToken;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
@@ -36,7 +38,7 @@
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
-public class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
+class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
private static final String TAG = "FingerprintEnrollClient";
@@ -44,21 +46,22 @@
@Nullable private ICancellationSignal mCancellationSignal;
private final int mMaxTemplatesPerUser;
- public FingerprintEnrollClient(@NonNull Context context,
+ FingerprintEnrollClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
- int statsModality, int sensorId,
+ @NonNull byte[] hardwareAuthToken, @NonNull String owner,
+ @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@Nullable IUdfpsOverlayController udfpsOvelayController, int maxTemplatesPerUser) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
- 0 /* timeoutSec */, statsModality, sensorId, true /* shouldVibrate */);
+ 0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
+ true /* shouldVibrate */);
mUdfpsOverlayController = udfpsOvelayController;
mMaxTemplatesPerUser = maxTemplatesPerUser;
}
@Override
protected boolean hasReachedEnrollmentLimit() {
- return FingerprintUtils.getInstance()
+ return FingerprintUtils.getInstance(getSensorId())
.getBiometricsForUser(getContext(), getTargetUserId()).size()
>= mMaxTemplatesPerUser;
}
@@ -82,8 +85,8 @@
protected void startHalOperation() {
UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
try {
- // TODO(b/170163175): Need a way to convert byte arrays to HardwareAuthToken
- getFreshDaemon().enroll(mSequentialId, null /* hat */);
+ mCancellationSignal = getFreshDaemon().enroll(mSequentialId,
+ HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken));
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enroll", e);
onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
index 3a6b113..402886b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.fingerprint.IFingerprint;
-import android.hardware.biometrics.fingerprint.IGenerateChallengeCallback;
+import android.hardware.biometrics.fingerprint.ISession;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -30,28 +30,12 @@
/**
* Fingerprint-specific generateChallenge client for the {@link IFingerprint} AIDL HAL interface.
*/
-public class FingerprintGenerateChallengeClient extends GenerateChallengeClient<IFingerprint> {
+class FingerprintGenerateChallengeClient extends GenerateChallengeClient<ISession> {
private static final String TAG = "FingerprintGenerateChallengeClient";
private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
- private IGenerateChallengeCallback mGenerateChallengeCallback =
- new IGenerateChallengeCallback.Stub() {
- @Override
- public void onChallengeGenerated(int sensorId, int userId, long challenge) {
- try {
- getListener().onChallengeGenerated(sensorId, challenge);
- mCallback.onClientFinished(FingerprintGenerateChallengeClient.this,
- true /* success */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to send challenge", e);
- mCallback.onClientFinished(FingerprintGenerateChallengeClient.this,
- false /* success */);
- }
- }
- };
-
- public FingerprintGenerateChallengeClient(@NonNull Context context,
- @NonNull LazyDaemon<IFingerprint> lazyDaemon,
+ FingerprintGenerateChallengeClient(@NonNull Context context,
+ @NonNull LazyDaemon<ISession> lazyDaemon,
@NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener,
@NonNull String owner, int sensorId) {
@@ -61,11 +45,21 @@
@Override
protected void startHalOperation() {
try {
- getFreshDaemon().generateChallenge(getSensorId(), getTargetUserId(),
- CHALLENGE_TIMEOUT_SEC,
- mGenerateChallengeCallback);
+ getFreshDaemon().generateChallenge(mSequentialId, CHALLENGE_TIMEOUT_SEC);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to generateChallenge", e);
}
}
+
+ void onChallengeGenerated(int sensorId, int userId, long challenge) {
+ try {
+ getListener().onChallengeGenerated(sensorId, challenge);
+ mCallback.onClientFinished(FingerprintGenerateChallengeClient.this,
+ true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send challenge", e);
+ mCallback.onClientFinished(FingerprintGenerateChallengeClient.this,
+ false /* success */);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
new file mode 100644
index 0000000..fec3cff
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitor;
+
+import java.util.Map;
+
+class FingerprintGetAuthenticatorIdClient extends ClientMonitor<ISession> {
+
+ private static final String TAG = "FingerprintGetAuthenticatorIdClient";
+
+ private final Map<Integer, Long> mAuthenticatorIds;
+
+ FingerprintGetAuthenticatorIdClient(@NonNull Context context,
+ @NonNull LazyDaemon<ISession> lazyDaemon, int userId, @NonNull String owner,
+ int sensorId, Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
+ 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT,
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ mAuthenticatorIds = authenticatorIds;
+ }
+
+ @Override
+ public void unableToStart() {
+ // Nothing to do here
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().getAuthenticatorId(mSequentialId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ void onAuthenticatorIdRetrieved(long authenticatorId) {
+ mAuthenticatorIds.put(getTargetUserId(), authenticatorId);
+ mCallback.onClientFinished(this, true /* success */);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
new file mode 100644
index 0000000..2a0e984
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.InternalCleanupClient;
+import com.android.server.biometrics.sensors.InternalEnumerateClient;
+import com.android.server.biometrics.sensors.RemovalClient;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Fingerprint-specific internal cleanup client supporting the
+ * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
+ */
+class FingerprintInternalCleanupClient extends InternalCleanupClient<Fingerprint, ISession> {
+
+ FingerprintInternalCleanupClient(@NonNull Context context,
+ @NonNull LazyDaemon<ISession> lazyDaemon, int userId, @NonNull String owner,
+ int sensorId, @NonNull List<Fingerprint> enrolledList,
+ @NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, userId, owner, sensorId,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
+ }
+
+ @Override
+ protected InternalEnumerateClient<ISession> getEnumerateClient(Context context,
+ LazyDaemon<ISession> lazyDaemon, IBinder token, int userId, String owner,
+ List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId) {
+ return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
+ enrolledList, utils, sensorId);
+ }
+
+ @Override
+ protected RemovalClient<Fingerprint, ISession> getRemovalClient(Context context,
+ LazyDaemon<ISession> lazyDaemon, IBinder token, int biometricId, int userId,
+ String owner, BiometricUtils<Fingerprint> utils, int sensorId,
+ Map<Integer, Long> authenticatorIds) {
+ return new FingerprintRemovalClient(context, lazyDaemon, token,
+ null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
+ sensorId, authenticatorIds);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
new file mode 100644
index 0000000..c930360
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.InternalEnumerateClient;
+
+import java.util.List;
+
+/**
+ * Fingerprint-specific internal client supporting the
+ * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface.
+ */
+class FingerprintInternalEnumerateClient extends InternalEnumerateClient<ISession> {
+ private static final String TAG = "FingerprintInternalEnumerateClient";
+
+ protected FingerprintInternalEnumerateClient(@NonNull Context context,
+ @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, int userId,
+ @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
+ @NonNull BiometricUtils<Fingerprint> utils, int sensorId) {
+ super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().enumerateEnrollments(mSequentialId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting enumerate", e);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 6245fd9..d713f98 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -19,8 +19,11 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
import android.content.Context;
-import android.hardware.biometrics.BiometricsProtoEnums;
+import android.content.pm.UserInfo;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
@@ -32,16 +35,21 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserManager;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Surface;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.ClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -51,6 +59,7 @@
/**
* Provider for a single instance of the {@link IFingerprint} HAL.
*/
+@SuppressWarnings("deprecation")
public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvider {
@NonNull private final Context mContext;
@@ -58,9 +67,50 @@
@NonNull private final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
@NonNull private final ClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon;
@NonNull private final Handler mHandler;
+ @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
+ @NonNull private final IActivityTaskManager mActivityTaskManager;
+ @NonNull private final BiometricTaskStackListener mTaskStackListener;
+ @Nullable private IFingerprint mDaemon;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
+ private final class BiometricTaskStackListener extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ mHandler.post(() -> {
+ for (int i = 0; i < mSensors.size(); i++) {
+ final ClientMonitor<?> client = mSensors.get(i).getScheduler()
+ .getCurrentClient();
+ if (!(client instanceof AuthenticationClient)) {
+ Slog.e(getTag(), "Task stack changed for client: " + client);
+ continue;
+ }
+ if (Utils.isKeyguard(mContext, client.getOwnerString())) {
+ continue; // Keyguard is always allowed
+ }
+
+ try {
+ final List<ActivityManager.RunningTaskInfo> runningTasks =
+ mActivityTaskManager.getTasks(1);
+ if (!runningTasks.isEmpty()) {
+ final String topPackage =
+ runningTasks.get(0).topActivity.getPackageName();
+ if (!topPackage.contentEquals(client.getOwnerString())
+ && !client.isAlreadyDone()) {
+ Slog.e(getTag(), "Stopping background authentication, top: "
+ + topPackage + " currentClient: " + client);
+ mSensors.get(i).getScheduler()
+ .cancelAuthentication(client.getToken());
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Unable to get running tasks", e);
+ }
+ }
+ });
+ }
+ }
+
public FingerprintProvider(@NonNull Context context, @NonNull SensorProps[] props,
@NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
@@ -69,6 +119,9 @@
mSensors = new SparseArray<>();
mLazyDaemon = this::getHalInstance;
mHandler = new Handler(Looper.getMainLooper());
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+ mActivityTaskManager = ActivityTaskManager.getService();
+ mTaskStackListener = new BiometricTaskStackListener();
for (SensorProps prop : props) {
final int sensorId = prop.commonProps.sensorId;
@@ -79,8 +132,8 @@
prop.commonProps.maxEnrollmentsPerUser,
prop.sensorType,
true /* resetLockoutRequiresHardwareAuthToken */);
- final Sensor sensor = new Sensor(getTag() + "/" + sensorId, internalProp,
- gestureAvailabilityDispatcher);
+ final Sensor sensor = new Sensor(getTag() + "/" + sensorId, mContext, mHandler,
+ internalProp, gestureAvailabilityDispatcher);
mSensors.put(sensorId, sensor);
Slog.d(getTag(), "Added: " + internalProp);
@@ -93,15 +146,21 @@
@Nullable
private synchronized IFingerprint getHalInstance() {
- final IFingerprint daemon = IFingerprint.Stub.asInterface(
+ if (mDaemon != null) {
+ return mDaemon;
+ }
+
+ Slog.d(getTag(), "Daemon was null, reconnecting");
+
+ mDaemon = IFingerprint.Stub.asInterface(
ServiceManager.waitForDeclaredService(mHalInstanceName));
- if (daemon == null) {
+ if (mDaemon == null) {
Slog.e(getTag(), "Unable to get daemon");
return null;
}
try {
- daemon.asBinder().linkToDeath(this, 0 /* flags */);
+ mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
} catch (RemoteException e) {
Slog.e(getTag(), "Unable to linkToDeath", e);
}
@@ -112,7 +171,7 @@
scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser());
}
- return daemon;
+ return mDaemon;
}
private void scheduleForSensor(int sensorId, @NonNull ClientMonitor<?> client) {
@@ -132,7 +191,7 @@
mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client, callback);
}
- private void scheduleCreateSessionWithoutHandler(@NonNull IFingerprint daemon, int sensorId,
+ private void createNewSessionWithoutHandler(@NonNull IFingerprint daemon, int sensorId,
int userId) throws RemoteException {
// Note that per IFingerprint createSession contract, this method will block until all
// existing operations are canceled/finished. However, also note that this is fine, since
@@ -141,14 +200,6 @@
mSensors.get(sensorId).createNewSession(daemon, sensorId, userId);
}
- private void scheduleLoadAuthenticatorIdsWithoutHandler(int sensorId) {
-
- }
-
- private void scheduleLoadAuthenticatorIds(int sensorId) {
-
- }
-
@Override
public boolean containsSensor(int sensorId) {
return mSensors.contains(sensorId);
@@ -157,33 +208,122 @@
@NonNull
@Override
public List<FingerprintSensorPropertiesInternal> getSensorProperties() {
- List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
+ final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
for (int i = 0; i < mSensors.size(); i++) {
props.add(mSensors.valueAt(i).getSensorProperties());
}
return props;
}
- @Override
- public void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
-
+ private void scheduleLoadAuthenticatorIds(int sensorId) {
+ for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
+ scheduleLoadAuthenticatorIdsForUser(sensorId, user.id);
+ }
}
- @Override
- public void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token,
- @NonNull IFingerprintServiceReceiver receiver, String opPackageName) {
+ private void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) {
mHandler.post(() -> {
- final FingerprintGenerateChallengeClient client =
- new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), opPackageName, sensorId);
- mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+ final IFingerprint daemon = getHalInstance();
+ if (daemon == null) {
+ Slog.e(getTag(), "Null daemon during loadAuthenticatorIds, sensorId: " + sensorId);
+ return;
+ }
+
+ try {
+ if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+ createNewSessionWithoutHandler(daemon, sensorId, userId);
+ }
+
+ final FingerprintGetAuthenticatorIdClient client =
+ new FingerprintGetAuthenticatorIdClient(mContext,
+ mSensors.get(sensorId).getLazySession(), userId,
+ mContext.getOpPackageName(), sensorId,
+ mSensors.get(sensorId).getAuthenticatorIds());
+ mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception when scheduling loadAuthenticatorId"
+ + ", sensorId: " + sensorId
+ + ", userId: " + userId, e);
+ }
});
}
@Override
- public void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
- @NonNull String opPackageName) {
+ public void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
+ mHandler.post(() -> {
+ final IFingerprint daemon = getHalInstance();
+ if (daemon == null) {
+ Slog.e(getTag(), "Null daemon during resetLockout, sensorId: " + sensorId);
+ return;
+ }
+ try {
+ if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+ createNewSessionWithoutHandler(daemon, sensorId, userId);
+ }
+
+ final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(
+ mContext, mSensors.get(sensorId).getLazySession(), userId,
+ mContext.getOpPackageName(), sensorId, hardwareAuthToken,
+ mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher);
+ scheduleForSensor(sensorId, client);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception when scheduling resetLockout", e);
+ }
+ });
+ }
+
+ @Override
+ public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, String opPackageName) {
+ mHandler.post(() -> {
+ final IFingerprint daemon = getHalInstance();
+ if (daemon == null) {
+ Slog.e(getTag(), "Null daemon during generateChallenge, sensorId: " + sensorId);
+ return;
+ }
+
+ try {
+ if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+ createNewSessionWithoutHandler(daemon, sensorId, userId);
+ }
+
+ final FingerprintGenerateChallengeClient client =
+ new FingerprintGenerateChallengeClient(mContext,
+ mSensors.get(sensorId).getLazySession(), token,
+ new ClientMonitorCallbackConverter(receiver), opPackageName,
+ sensorId);
+ scheduleForSensor(sensorId, client);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception when scheduling generateChallenge", e);
+ }
+ });
+ }
+
+ @Override
+ public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
+ @NonNull String opPackageName, long challenge) {
+ mHandler.post(() -> {
+ final IFingerprint daemon = getHalInstance();
+ if (daemon == null) {
+ Slog.e(getTag(), "Null daemon during revokeChallenge, sensorId: " + sensorId);
+ return;
+ }
+
+ try {
+ if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+ createNewSessionWithoutHandler(daemon, sensorId, userId);
+ }
+
+ final FingerprintRevokeChallengeClient client =
+ new FingerprintRevokeChallengeClient(mContext,
+ mSensors.get(sensorId).getLazySession(), token,
+ opPackageName, sensorId, challenge);
+ scheduleForSensor(sensorId, client);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception when scheduling revokeChallenge", e);
+ }
+ });
}
@Override
@@ -194,12 +334,15 @@
final IFingerprint daemon = getHalInstance();
if (daemon == null) {
Slog.e(getTag(), "Null daemon during enroll, sensorId: " + sensorId);
+ // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
+ // this operation. We should not send the callback yet, since the scheduler may
+ // be processing something else.
return;
}
try {
if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
- scheduleCreateSessionWithoutHandler(daemon, sensorId, userId);
+ createNewSessionWithoutHandler(daemon, sensorId, userId);
}
final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties()
@@ -207,15 +350,14 @@
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, FingerprintUtils.getInstance(),
- BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
+ opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
mUdfpsOverlayController, maxTemplatesPerUser);
scheduleForSensor(sensorId, client, new ClientMonitor.Callback() {
@Override
public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor,
boolean success) {
if (success) {
- scheduleLoadAuthenticatorIdsWithoutHandler(sensorId);
+ scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
}
}
});
@@ -227,14 +369,38 @@
@Override
public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
-
+ mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token));
}
@Override
public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
@Nullable Surface surface, int statsClient) {
+ mHandler.post(() -> {
+ final IFingerprint daemon = getHalInstance();
+ if (daemon == null) {
+ Slog.e(getTag(), "Null daemon during finger detect, sensorId: " + sensorId);
+ // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
+ // this operation. We should not send the callback yet, since the scheduler may
+ // be processing something else.
+ return;
+ }
+ try {
+ if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+ createNewSessionWithoutHandler(daemon, sensorId, userId);
+ }
+
+ final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
+ final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
+ mSensors.get(sensorId).getLazySession(), token, callback, userId,
+ opPackageName, sensorId, mUdfpsOverlayController, isStrongBiometric,
+ statsClient);
+ mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception when scheduling finger detect", e);
+ }
+ });
}
@Override
@@ -242,65 +408,151 @@
int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull String opPackageName, boolean restricted, int statsClient,
boolean isKeyguard) {
+ mHandler.post(() -> {
+ final IFingerprint daemon = getHalInstance();
+ if (daemon == null) {
+ Slog.e(getTag(), "Null daemon during authenticate, sensorId: " + sensorId);
+ // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
+ // this operation. We should not send the callback yet, since the scheduler may
+ // be processing something else.
+ return;
+ }
+ try {
+ if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+ createNewSessionWithoutHandler(daemon, sensorId, userId);
+ }
+
+ final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
+ final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
+ mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId,
+ operationId, restricted, opPackageName, cookie,
+ false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
+ mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
+ mUdfpsOverlayController);
+ mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception when scheduling authenticate", e);
+ }
+ });
}
@Override
public void startPreparedClient(int sensorId, int cookie) {
-
+ mHandler.post(() -> mSensors.get(sensorId).getScheduler().startPreparedClient(cookie));
}
@Override
public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
-
+ mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelAuthentication(token));
}
@Override
public void scheduleRemove(int sensorId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
@NonNull String opPackageName) {
+ mHandler.post(() -> {
+ final IFingerprint daemon = getHalInstance();
+ if (daemon == null) {
+ Slog.e(getTag(), "Null daemon during remove, sensorId: " + sensorId);
+ // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to
+ // this operation. We should not send the callback yet, since the scheduler may
+ // be processing something else.
+ return;
+ }
+ try {
+ if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+ createNewSessionWithoutHandler(daemon, sensorId, userId);
+ }
+
+ final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
+ mSensors.get(sensorId).getLazySession(), token,
+ new ClientMonitorCallbackConverter(receiver), fingerId, userId,
+ opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
+ mSensors.get(sensorId).getAuthenticatorIds());
+ mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception when scheduling remove", e);
+ }
+ });
}
@Override
- public void scheduleInternalCleanup(int userId, int sensorId) {
+ public void scheduleInternalCleanup(int sensorId, int userId) {
+ mHandler.post(() -> {
+ final IFingerprint daemon = getHalInstance();
+ if (daemon == null) {
+ Slog.e(getTag(), "Null daemon during internal cleanup, sensorId: " + sensorId);
+ return;
+ }
+ try {
+ if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+ createNewSessionWithoutHandler(daemon, sensorId, userId);
+ }
+
+ final List<Fingerprint> enrolledList = getEnrolledFingerprints(sensorId, userId);
+ final FingerprintInternalCleanupClient client =
+ new FingerprintInternalCleanupClient(mContext,
+ mSensors.get(sensorId).getLazySession(), userId,
+ mContext.getOpPackageName(), sensorId, enrolledList,
+ FingerprintUtils.getInstance(sensorId),
+ mSensors.get(sensorId).getAuthenticatorIds());
+ mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "Remote exception when scheduling internal cleanup", e);
+ }
+ });
}
@Override
public boolean isHardwareDetected(int sensorId) {
- return false;
+ return getHalInstance() != null;
}
@Override
public void rename(int sensorId, int fingerId, int userId, @NonNull String name) {
-
+ FingerprintUtils.getInstance(sensorId)
+ .renameBiometricForUser(mContext, userId, fingerId, name);
}
@NonNull
@Override
public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) {
- return new ArrayList<>();
+ return FingerprintUtils.getInstance(sensorId).getBiometricsForUser(mContext, userId);
}
@Override
public int getLockoutModeForUser(int sensorId, int userId) {
- return 0;
+ return mSensors.get(sensorId).getLockoutCache().getLockoutModeForUser(userId);
}
@Override
public long getAuthenticatorId(int sensorId, int userId) {
- return 0;
+ return mSensors.get(sensorId).getAuthenticatorIds().getOrDefault(userId, 0L);
}
@Override
public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
-
+ final ClientMonitor<?> client = mSensors.get(sensorId).getScheduler().getCurrentClient();
+ if (!(client instanceof Udfps)) {
+ Slog.e(getTag(), "onPointerDown received during client: " + client);
+ return;
+ }
+ final Udfps udfps = (Udfps) client;
+ udfps.onPointerDown(x, y, minor, major);
}
@Override
public void onPointerUp(int sensorId) {
-
+ final ClientMonitor<?> client = mSensors.get(sensorId).getScheduler().getCurrentClient();
+ if (!(client instanceof Udfps)) {
+ Slog.e(getTag(), "onPointerUp received during client: " + client);
+ return;
+ }
+ final Udfps udfps = (Udfps) client;
+ udfps.onPointerUp();
}
@Override
@@ -320,6 +572,14 @@
@Override
public void binderDied() {
+ Slog.e(getTag(), "HAL died");
+ mHandler.post(() -> {
+ mDaemon = null;
+ for (int i = 0; i < mSensors.size(); i++) {
+ final int sensorId = mSensors.keyAt(i);
+ PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
+ }
+ });
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
new file mode 100644
index 0000000..4a99a7b
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.Fingerprint;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.RemovalClient;
+
+import java.util.Map;
+
+/**
+ * Fingerprint-specific removal client supporting the
+ * {@link android.hardware.biometrics.fingerprint.IFingerprint} interface.
+ */
+class FingerprintRemovalClient extends RemovalClient<Fingerprint, ISession> {
+ private static final String TAG = "FingerprintRemovalClient";
+
+ FingerprintRemovalClient(@NonNull Context context,
+ @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+ @Nullable ClientMonitorCallbackConverter listener, int biometricId, int userId,
+ @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
+ @NonNull Map<Integer, Long> authenticatorIds) {
+ super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
+ authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ final int[] ids = new int[] {mBiometricId};
+ getFreshDaemon().removeEnrollments(mSequentialId, ids);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when requesting remove", e);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
new file mode 100644
index 0000000..1718126
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+
+/**
+ * Fingerprint-specific resetLockout client for the {@link IFingerprint} AIDL HAL interface.
+ * Updates the framework's lockout cache and notifies clients such as Keyguard when lockout is
+ * cleared.
+ */
+class FingerprintResetLockoutClient extends ClientMonitor<ISession> {
+
+ private static final String TAG = "FingerprintResetLockoutClient";
+
+ private final HardwareAuthToken mHardwareAuthToken;
+ private final LockoutCache mLockoutCache;
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+
+ FingerprintResetLockoutClient(@NonNull Context context,
+ @NonNull LazyDaemon<ISession> lazyDaemon, int userId, String owner, int sensorId,
+ @NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
+ 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+ BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+ mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
+ mLockoutCache = lockoutTracker;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+ }
+
+ @Override
+ public void unableToStart() {
+ // Nothing to do here
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().resetLockout(mSequentialId, mHardwareAuthToken);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to reset lockout", e);
+ mCallback.onClientFinished(this, false /* success */);
+ }
+ }
+
+ void onLockoutCleared() {
+ mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_NONE);
+ mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId());
+ mCallback.onClientFinished(this, true /* success */);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
new file mode 100644
index 0000000..ebb4fe6
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.RevokeChallengeClient;
+
+/**
+ * Fingerprint-specific revokeChallenge client for the {@link IFingerprint} AIDL HAL interface.
+ */
+class FingerprintRevokeChallengeClient extends RevokeChallengeClient<ISession> {
+
+ private static final String TAG = "FingerpirntRevokeChallengeClient";
+
+ private final long mChallenge;
+
+ FingerprintRevokeChallengeClient(@NonNull Context context,
+ @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+ @NonNull String owner, int sensorId, long challenge) {
+ super(context, lazyDaemon, token, owner, sensorId);
+ mChallenge = challenge;
+ }
+
+ @Override
+ protected void startHalOperation() {
+ try {
+ getFreshDaemon().revokeChallenge(mSequentialId, mChallenge);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to revokeChallenge", e);
+ }
+ }
+
+ void onChallengeRevoked(int sensorId, int userId, long challenge) {
+ final boolean success = challenge == mChallenge;
+ mCallback.onClientFinished(FingerprintRevokeChallengeClient.this, success);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java
new file mode 100644
index 0000000..2fae1f3
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint.aidl;
+
+import android.util.SparseIntArray;
+
+import com.android.server.biometrics.sensors.LockoutTracker;
+
+/**
+ * For a single sensor, caches lockout states for all users.
+ */
+class LockoutCache implements LockoutTracker {
+
+ // Map of userId to LockoutMode
+ private final SparseIntArray mUserLockoutStates;
+
+ LockoutCache() {
+ mUserLockoutStates = new SparseIntArray();
+ }
+
+ public void setLockoutModeForUser(int userId, @LockoutMode int mode) {
+ synchronized (this) {
+ mUserLockoutStates.put(userId, mode);
+ }
+ }
+
+ @Override
+ public int getLockoutModeForUser(int userId) {
+ synchronized (this) {
+ return mUserLockoutStates.get(userId, LOCKOUT_NONE);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 4151f5a..d4ce896 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -18,30 +18,79 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.Error;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.ISessionCallback;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.keymaster.HardwareAuthToken;
+import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.EnumerateConsumer;
+import com.android.server.biometrics.sensors.Interruptable;
+import com.android.server.biometrics.sensors.LockoutConsumer;
+import com.android.server.biometrics.sensors.RemovalConsumer;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
/**
* Maintains the state of a single sensor within an instance of the
* {@link android.hardware.biometrics.fingerprint.IFingerprint} HAL.
*/
-class Sensor {
+@SuppressWarnings("deprecation")
+class Sensor implements IBinder.DeathRecipient {
@NonNull private final String mTag;
+ @NonNull private final Context mContext;
+ @NonNull private final Handler mHandler;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
@NonNull private final BiometricScheduler mScheduler;
+ @NonNull private final LockoutCache mLockoutCache;
+ @NonNull private final Map<Integer, Long> mAuthenticatorIds;
- @Nullable private Session mCurrentSession; // TODO: Death recipient
+ @Nullable private Session mCurrentSession;
@NonNull private final ClientMonitor.LazyDaemon<ISession> mLazySession;
+ @Override
+ public void binderDied() {
+ Slog.e(mTag, "Binder died");
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (client instanceof Interruptable) {
+ Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+
+ mScheduler.recordCrashState();
+
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT,
+ BiometricsProtoEnums.ISSUE_HAL_DEATH);
+ mCurrentSession = null;
+ }
+ });
+ }
+
private static class Session {
@NonNull private final String mTag;
@NonNull private final ISession mSession;
@@ -58,12 +107,16 @@
}
}
- Sensor(@NonNull String tag,
+ Sensor(@NonNull String tag, @NonNull Context context, @NonNull Handler handler,
@NonNull FingerprintSensorPropertiesInternal sensorProperties,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
mTag = tag;
+ mContext = context;
+ mHandler = handler;
mSensorProperties = sensorProperties;
mScheduler = new BiometricScheduler(tag, gestureAvailabilityDispatcher);
+ mLockoutCache = new LockoutCache();
+ mAuthenticatorIds = new HashMap<>();
mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null;
}
@@ -75,6 +128,7 @@
return mSensorProperties;
}
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
boolean hasSessionForUser(int userId) {
return mCurrentSession != null && mCurrentSession.mUserId == userId;
}
@@ -84,80 +138,284 @@
final ISessionCallback callback = new ISessionCallback.Stub() {
@Override
public void onStateChanged(int cookie, byte state) {
+ // TODO(b/162973174)
+ }
+ @Override
+ public void onChallengeGenerated(long challenge) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintGenerateChallengeClient)) {
+ Slog.e(mTag, "onChallengeGenerated for wrong client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final FingerprintGenerateChallengeClient generateChallengeClient =
+ (FingerprintGenerateChallengeClient) client;
+ generateChallengeClient.onChallengeGenerated(sensorId, userId, challenge);
+ });
+ }
+
+ @Override
+ public void onChallengeRevoked(long challenge) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintRevokeChallengeClient)) {
+ Slog.e(mTag, "onChallengeRevoked for wrong client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final FingerprintRevokeChallengeClient revokeChallengeClient =
+ (FingerprintRevokeChallengeClient) client;
+ revokeChallengeClient.onChallengeRevoked(sensorId, userId, challenge);
+ });
}
@Override
public void onAcquired(byte info, int vendorCode) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AcquisitionClient)) {
+ Slog.e(mTag, "onAcquired for non-acquisition client: "
+ + Utils.getClientName(client));
+ return;
+ }
+ final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
+ acquisitionClient.onAcquired(info, vendorCode);
+ });
}
@Override
public void onError(byte error, int vendorCode) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ Slog.d(mTag, "onError"
+ + ", client: " + Utils.getClientName(client)
+ + ", error: " + error
+ + ", vendorCode: " + vendorCode);
+ if (!(client instanceof Interruptable)) {
+ Slog.e(mTag, "onError for non-error consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(error, vendorCode);
+
+ if (error == Error.HW_UNAVAILABLE) {
+ Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
+ mCurrentSession = null;
+ }
+ });
}
@Override
public void onEnrollmentProgress(int enrollmentId, int remaining) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintEnrollClient)) {
+ Slog.e(mTag, "onEnrollmentProgress for non-enroll client: "
+ + Utils.getClientName(client));
+ return;
+ }
+ final int currentUserId = client.getTargetUserId();
+ final CharSequence name = FingerprintUtils.getInstance(sensorId)
+ .getUniqueName(mContext, currentUserId);
+ final Fingerprint fingerprint = new Fingerprint(name, enrollmentId, sensorId);
+
+ final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
+ enrollClient.onEnrollResult(fingerprint, remaining);
+ });
}
@Override
public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AuthenticationConsumer)) {
+ Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+ final AuthenticationConsumer authenticationConsumer =
+ (AuthenticationConsumer) client;
+ final Fingerprint fp = new Fingerprint("", enrollmentId, sensorId);
+ final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
+ final ArrayList<Byte> byteList = new ArrayList<>();
+ for (byte b : byteArray) {
+ byteList.add(b);
+ }
+
+ authenticationConsumer.onAuthenticated(fp, true /* authenticated */, byteList);
+ });
}
@Override
public void onAuthenticationFailed() {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AuthenticationConsumer)) {
+ Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+ final AuthenticationConsumer authenticationConsumer =
+ (AuthenticationConsumer) client;
+ final Fingerprint fp = new Fingerprint("", 0 /* enrollmentId */, sensorId);
+ authenticationConsumer
+ .onAuthenticated(fp, false /* authenticated */, null /* hat */);
+ });
}
@Override
public void onLockoutTimed(long durationMillis) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof LockoutConsumer)) {
+ Slog.e(mTag, "onLockoutTimed for non-lockout consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+ final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
+ lockoutConsumer.onLockoutTimed(durationMillis);
+ });
}
@Override
public void onLockoutPermanent() {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof LockoutConsumer)) {
+ Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+ final LockoutConsumer lockoutConsumer = (LockoutConsumer) client;
+ lockoutConsumer.onLockoutPermanent();
+ });
}
@Override
public void onLockoutCleared() {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintResetLockoutClient)) {
+ Slog.e(mTag, "onLockoutCleared for non-resetLockout client: "
+ + Utils.getClientName(client));
+ return;
+ }
+ final FingerprintResetLockoutClient resetLockoutClient =
+ (FingerprintResetLockoutClient) client;
+ resetLockoutClient.onLockoutCleared();
+ });
}
@Override
public void onInteractionDetected() {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintDetectClient)) {
+ Slog.e(mTag, "onInteractionDetected for non-detect client: "
+ + Utils.getClientName(client));
+ return;
+ }
+ final FingerprintDetectClient fingerprintDetectClient =
+ (FingerprintDetectClient) client;
+ fingerprintDetectClient.onInteractionDetected();
+ });
}
@Override
public void onEnrollmentsEnumerated(int[] enrollmentIds) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof EnumerateConsumer)) {
+ Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+ final EnumerateConsumer enumerateConsumer =
+ (EnumerateConsumer) client;
+ if (enrollmentIds.length > 0) {
+ for (int i = 0; i < enrollmentIds.length; i++) {
+ final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId);
+ enumerateConsumer.onEnumerationResult(fp, enrollmentIds.length - i - 1);
+ }
+ } else {
+ enumerateConsumer.onEnumerationResult(null /* identifier */, 0);
+ }
+ });
}
@Override
public void onEnrollmentsRemoved(int[] enrollmentIds) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof RemovalConsumer)) {
+ Slog.e(mTag, "onRemoved for non-removal consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+ final RemovalConsumer removalConsumer = (RemovalConsumer) client;
+ if (enrollmentIds.length > 0) {
+ for (int i = 0; i < enrollmentIds.length; i++) {
+ final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId);
+ removalConsumer.onRemoved(fp, enrollmentIds.length - i - 1);
+ }
+ } else {
+ removalConsumer.onRemoved(null, 0);
+ }
+ });
}
@Override
public void onAuthenticatorIdRetrieved(long authenticatorId) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FingerprintGetAuthenticatorIdClient)) {
+ Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+ final FingerprintGetAuthenticatorIdClient getAuthenticatorIdClient =
+ (FingerprintGetAuthenticatorIdClient) client;
+ getAuthenticatorIdClient.onAuthenticatorIdRetrieved(authenticatorId);
+ });
}
@Override
public void onAuthenticatorIdInvalidated() {
-
+ // TODO(159667191)
}
};
final ISession newSession = daemon.createSession(sensorId, userId, callback);
+ newSession.asBinder().linkToDeath(this, 0 /* flags */);
mCurrentSession = new Session(mTag, newSession, userId, callback);
}
@NonNull BiometricScheduler getScheduler() {
return mScheduler;
}
+
+ @NonNull LockoutCache getLockoutCache() {
+ return mLockoutCache;
+ }
+
+ @NonNull Map<Integer, Long> getAuthenticatorIds() {
+ return mAuthenticatorIds;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 89d1d16..ab4427c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -246,7 +246,7 @@
mHandler.post(() -> {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
Slog.d(TAG, "handleError"
- + ", client: " + (client != null ? client.getOwnerString() : null)
+ + ", client: " + Utils.getClientName(client)
+ ", error: " + error
+ ", vendorCode: " + vendorCode);
if (!(client instanceof Interruptable)) {
@@ -505,7 +505,7 @@
}
@Override
- public void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token,
+ public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
final FingerprintGenerateChallengeClient client =
@@ -517,8 +517,8 @@
}
@Override
- public void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
- @NonNull String opPackageName) {
+ public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
+ @NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
mContext, mLazyDaemon, token, opPackageName, mSensorProperties.sensorId);
@@ -594,16 +594,12 @@
@Override
public void startPreparedClient(int sensorId, int cookie) {
- mHandler.post(() -> {
- mScheduler.startPreparedClient(cookie);
- });
+ mHandler.post(() -> mScheduler.startPreparedClient(cookie));
}
@Override
public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
- mHandler.post(() -> {
- mScheduler.cancelAuthentication(token);
- });
+ mHandler.post(() -> mScheduler.cancelAuthentication(token));
}
@Override
@@ -636,7 +632,7 @@
}
@Override
- public void scheduleInternalCleanup(int userId, int sensorId) {
+ public void scheduleInternalCleanup(int sensorId, int userId) {
scheduleInternalCleanup(userId);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 975ac3d..c750b90 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -22,6 +22,7 @@
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
@@ -48,8 +49,8 @@
FingerprintEnrollClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int userId,
- @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
- int timeoutSec, int sensorId,
+ @NonNull byte[] hardwareAuthToken, @NonNull String owner,
+ @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
@Nullable IUdfpsOverlayController udfpsOverlayController) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
index e061112..a42a8ae 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
@@ -42,7 +42,8 @@
FingerprintInternalCleanupClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId,
@NonNull String owner, int sensorId, @NonNull List<Fingerprint> enrolledList,
- @NonNull BiometricUtils utils, @NonNull Map<Integer, Long> authenticatorIds) {
+ @NonNull BiometricUtils<Fingerprint> utils,
+ @NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, userId, owner, sensorId,
BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds);
}
@@ -50,18 +51,17 @@
@Override
protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient(
Context context, LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
- int userId, String owner,
- List<Fingerprint> enrolledList, BiometricUtils utils,
- int sensorId) {
+ int userId, String owner, List<Fingerprint> enrolledList,
+ BiometricUtils<Fingerprint> utils, int sensorId) {
return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner,
enrolledList, utils, sensorId);
}
@Override
- protected RemovalClient<IBiometricsFingerprint> getRemovalClient(Context context,
+ protected RemovalClient<Fingerprint, IBiometricsFingerprint> getRemovalClient(Context context,
LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token,
- int biometricId, int userId, String owner, BiometricUtils utils, int sensorId,
- Map<Integer, Long> authenticatorIds) {
+ int biometricId, int userId, String owner, BiometricUtils<Fingerprint> utils,
+ int sensorId, Map<Integer, Long> authenticatorIds) {
// Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
// is all done internally.
return new FingerprintRemovalClient(context, lazyDaemon, token,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
index 5fd1d1e..7117cf3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
@@ -41,7 +41,7 @@
FingerprintInternalEnumerateClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList,
- @NonNull BiometricUtils utils, int sensorId) {
+ @NonNull BiometricUtils<Fingerprint> utils, int sensorId) {
super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId,
BiometricsProtoEnums.MODALITY_FINGERPRINT);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
index 4bbb7ef..f6a22f5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.fingerprint.Fingerprint;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -35,13 +36,13 @@
* {@link android.hardware.biometrics.fingerprint.V2_1} and
* {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
*/
-class FingerprintRemovalClient extends RemovalClient<IBiometricsFingerprint> {
+class FingerprintRemovalClient extends RemovalClient<Fingerprint, IBiometricsFingerprint> {
private static final String TAG = "FingerprintRemovalClient";
FingerprintRemovalClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
- @NonNull String owner, @NonNull BiometricUtils utils, int sensorId,
+ @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId,
authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT);
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index cf6a7f6..271ec4e 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -50,7 +50,6 @@
import java.net.InetAddress;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -245,7 +244,6 @@
private final Map<Integer, LinkProperties> mLinkPropertiesMap;
private final Map<Integer, int[]> mTransportsMap;
- private int mNumDnsEntries;
private int mSampleValidity;
private int mSuccessThreshold;
private int mMinSamples;
@@ -409,18 +407,6 @@
}
}
- public void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
- int last = 0;
- for (InetAddress dns : dnses) {
- ++last;
- setNetDnsProperty(last, dns.getHostAddress());
- }
- for (int i = last + 1; i <= mNumDnsEntries; ++i) {
- setNetDnsProperty(i, "");
- }
- mNumDnsEntries = last;
- }
-
/**
* Flush DNS caches and events work before boot has completed.
*/
@@ -476,16 +462,6 @@
return Settings.Global.getInt(mContentResolver, which, dflt);
}
- private void setNetDnsProperty(int which, String value) {
- final String key = "net.dns" + which;
- // Log and forget errors setting unsupported properties.
- try {
- mSystemProperties.set(key, value);
- } catch (Exception e) {
- Slog.e(TAG, "Error setting unsupported net.dns property: ", e);
- }
- }
-
private static String getPrivateDnsMode(ContentResolver cr) {
String mode = getStringSetting(cr, PRIVATE_DNS_MODE);
if (TextUtils.isEmpty(mode)) mode = getStringSetting(cr, PRIVATE_DNS_DEFAULT_MODE);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 09c01d7..1ed6b35 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2152,7 +2152,11 @@
break;
}
- // Prepare arguments for mtpd.
+ // Prepare arguments for mtpd. MTU/MRU calculated conservatively. Only IPv4 supported
+ // because LegacyVpn.
+ // 1500 - 60 (Carrier-internal IPv6 + UDP + GTP) - 10 (PPP) - 16 (L2TP) - 8 (UDP)
+ // - 77 (IPsec w/ SHA-2 512, 256b trunc-len, AES-CBC) - 8 (UDP encap) - 20 (IPv4)
+ // - 28 (464xlat)
String[] mtpd = null;
switch (profile.type) {
case VpnProfile.TYPE_PPTP:
@@ -2160,7 +2164,7 @@
iface, "pptp", profile.server, "1723",
"name", profile.username, "password", profile.password,
"linkname", "vpn", "refuse-eap", "nodefaultroute",
- "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+ "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270",
(profile.mppe ? "+mppe" : "nomppe"),
};
break;
@@ -2170,7 +2174,7 @@
iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
"name", profile.username, "password", profile.password,
"linkname", "vpn", "refuse-eap", "nodefaultroute",
- "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+ "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270",
};
break;
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 4cff5c0..7e3c1ab 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -16,8 +16,11 @@
package com.android.server.devicestate;
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+
import android.annotation.NonNull;
import android.content.Context;
+import android.hardware.devicestate.IDeviceStateManager;
import android.util.IntArray;
import android.util.Slog;
@@ -49,9 +52,6 @@
* @see DeviceStatePolicy
*/
public final class DeviceStateManagerService extends SystemService {
- /** Invalid device state. */
- public static final int INVALID_DEVICE_STATE = -1;
-
private static final String TAG = "DeviceStateManagerService";
private static final boolean DEBUG = false;
@@ -88,6 +88,7 @@
@Override
public void onStart() {
mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
+ publishBinderService(Context.DEVICE_STATE_SERVICE, new BinderService());
}
/**
@@ -267,4 +268,9 @@
requestState(state);
}
}
+
+ /** Implementation of {@link IDeviceStateManager} published as a binder service. */
+ private final class BinderService extends IDeviceStateManager.Stub {
+
+ }
}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index eb2c7e6..93cada7 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -208,7 +208,6 @@
private PackageManager mPackageManager;
private Context mContext;
- private DisplayDeviceConfig mDisplayDeviceConfig;
private final Injector mInjector;
AutomaticBrightnessController(Callbacks callbacks, Looper looper,
@@ -217,14 +216,13 @@
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
- HysteresisLevels screenBrightnessThresholds, Context context, DisplayDeviceConfig
- displayDeviceConfig) {
+ HysteresisLevels screenBrightnessThresholds, Context context) {
this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
- ambientBrightnessThresholds, screenBrightnessThresholds, context,
- displayDeviceConfig);
+ ambientBrightnessThresholds, screenBrightnessThresholds, context
+ );
}
@VisibleForTesting
@@ -234,8 +232,7 @@
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
- HysteresisLevels screenBrightnessThresholds, Context context, DisplayDeviceConfig
- displayDeviceConfig) {
+ HysteresisLevels screenBrightnessThresholds, Context context) {
mInjector = injector;
mContext = context;
mCallbacks = callbacks;
@@ -257,7 +254,6 @@
mScreenBrightnessThresholds = screenBrightnessThresholds;
mShortTermModelValid = true;
mShortTermModelAnchor = -1;
- mDisplayDeviceConfig = displayDeviceConfig;
mHandler = new AutomaticBrightnessHandler(looper);
mAmbientLightRingBuffer =
new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
diff --git a/services/core/java/com/android/server/display/DisplayBlanker.java b/services/core/java/com/android/server/display/DisplayBlanker.java
index d294898..e2129ba 100644
--- a/services/core/java/com/android/server/display/DisplayBlanker.java
+++ b/services/core/java/com/android/server/display/DisplayBlanker.java
@@ -20,5 +20,5 @@
* Interface used to update the actual display state.
*/
public interface DisplayBlanker {
- void requestDisplayState(int state, float brightness);
+ void requestDisplayState(int displayId, int state, float brightness);
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
index aa4db29..5c0fceb 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -112,6 +112,17 @@
}
}
+ public DisplayDevice getByIdLocked(@NonNull String uniqueId) {
+ final int count = mDisplayDevices.size();
+ for (int i = 0; i < count; i++) {
+ final DisplayDevice d = mDisplayDevices.get(i);
+ if (uniqueId.equals(d.getUniqueId())) {
+ return d;
+ }
+ }
+ return null;
+ }
+
private void handleDisplayDeviceAdded(DisplayDevice device) {
synchronized (mSyncRoot) {
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index af62aeb..a600292 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -324,7 +324,7 @@
mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
mUiHandler = UiThread.getHandler();
mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
- mLogicalDisplayMapper = new LogicalDisplayMapper(mDisplayDeviceRepo,
+ mLogicalDisplayMapper = new LogicalDisplayMapper(context, mDisplayDeviceRepo,
new LogicalDisplayListener(), mPersistentDataStore);
mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
Resources resources = mContext.getResources();
@@ -576,6 +576,7 @@
Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestGlobalDisplayState("
+ Display.stateToString(state)
+ ", brightness=" + brightnessState + ")");
+
mGlobalDisplayState = state;
mGlobalDisplayBrightness = brightnessState;
applyGlobalDisplayStateLocked(mTempDisplayStateWorkQueue);
@@ -983,6 +984,15 @@
scheduleTraversalLocked(false);
}
+ private void handleLogicalDisplaySwappedLocked(@NonNull LogicalDisplay display) {
+ final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+ final Runnable work = updateDisplayStateLocked(device);
+ if (work != null) {
+ mHandler.post(work);
+ }
+ handleLogicalDisplayChangedLocked(display);
+ }
+
private void applyGlobalDisplayStateLocked(List<Runnable> workQueue) {
mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> {
Runnable runnable = updateDisplayStateLocked(device);
@@ -997,10 +1007,15 @@
// by the display power controller (if known).
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
- // TODO - multi-display - The rules regarding what display state to apply to each
+ // TODO - b/170498827 The rules regarding what display state to apply to each
// display will depend on the configuration/mapping of logical displays.
- return device.requestDisplayStateLocked(
- mGlobalDisplayState, mGlobalDisplayBrightness);
+ // Clean up LogicalDisplay.isEnabled() mechanism once this is fixed.
+ int state = mGlobalDisplayState;
+ final LogicalDisplay display = mLogicalDisplayMapper.getLocked(device);
+ if (display != null && !display.isEnabled()) {
+ state = Display.STATE_OFF;
+ }
+ return device.requestDisplayStateLocked(state, mGlobalDisplayBrightness);
}
return null;
}
@@ -1346,6 +1361,12 @@
}
}
+ void setFoldOverride(Boolean isFolded) {
+ synchronized (mSyncRoot) {
+ mLogicalDisplayMapper.setFoldOverrideLocked(isFolded);
+ }
+ }
+
private void clearViewportsLocked() {
mViewports.clear();
}
@@ -1698,6 +1719,10 @@
case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED:
handleLogicalDisplayRemovedLocked(display);
break;
+
+ case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_SWAPPED:
+ handleLogicalDisplaySwappedLocked(display);
+ break;
}
}
@@ -1768,7 +1793,9 @@
final int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid);
+ synchronized (mSyncRoot) {
+ return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid);
+ }
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -2380,7 +2407,7 @@
synchronized (mSyncRoot) {
DisplayBlanker blanker = new DisplayBlanker() {
@Override
- public void requestDisplayState(int state, float brightness) {
+ public void requestDisplayState(int displayId, int state, float brightness) {
// The order of operations is important for legacy reasons.
if (state == Display.STATE_OFF) {
requestGlobalDisplayStateInternal(state, brightness);
@@ -2393,11 +2420,9 @@
}
}
};
- LogicalDisplay defaultDisplay =
- mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY);
- DisplayDevice defaultDevice = defaultDisplay.getPrimaryDisplayDeviceLocked();
mDisplayPowerController = new DisplayPowerController(
- mContext, callbacks, handler, sensorManager, blanker, defaultDevice);
+ mContext, callbacks, handler, sensorManager, blanker,
+ Display.DEFAULT_DISPLAY);
mSensorManager = sensorManager;
}
@@ -2538,6 +2563,13 @@
public void ignoreProximitySensorUntilChanged() {
mDisplayPowerController.ignoreProximitySensorUntilChanged();
}
+
+ @Override
+ public void setDeviceFolded(boolean isFolded) {
+ synchronized (mSyncRoot) {
+ mLogicalDisplayMapper.setDeviceFoldedLocked(isFolded);
+ }
+ }
}
class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 0c6c797..111664a 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -56,6 +56,8 @@
return setDisplayWhiteBalanceLoggingEnabled(false);
case "dwb-set-cct":
return setAmbientColorTemperatureOverride();
+ case "set-fold":
+ return setFoldOverride();
default:
return handleDefaultCommands(cmd);
}
@@ -82,6 +84,8 @@
pw.println(" Disable display white-balance logging.");
pw.println(" dwb-set-cct CCT");
pw.println(" Sets the ambient color temperature override to CCT (use -1 to disable).");
+ pw.println(" set-fold [fold|unfold|reset]");
+ pw.println(" Simulates the 'fold' state of a device. 'reset' for default behavior.");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -148,4 +152,26 @@
mService.setAmbientColorTemperatureOverride(cct);
return 0;
}
+
+ private int setFoldOverride() {
+ String state = getNextArg();
+ if (state == null) {
+ getErrPrintWriter().println("Error: no parameter specified for set-fold");
+ return 1;
+ }
+ final Boolean isFolded;
+ if ("fold".equals(state)) {
+ isFolded = true;
+ } else if ("unfold".equals(state)) {
+ isFolded = false;
+ } else if ("reset".equals(state)) {
+ isFolded = null;
+ } else {
+ getErrPrintWriter().println("Error: Invalid fold state request: " + state);
+ return 1;
+ }
+
+ mService.setFoldOverride(isFolded);
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 58ef9d1..0211876 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -163,8 +163,8 @@
// The display blanker.
private final DisplayBlanker mBlanker;
- // The display device.
- private final DisplayDevice mDisplayDevice;
+ // The ID of the LogicalDisplay tied to this DisplayPowerController.
+ private final int mDisplayId;
// Tracker for brightness changes.
private final BrightnessTracker mBrightnessTracker;
@@ -406,7 +406,7 @@
*/
public DisplayPowerController(Context context,
DisplayPowerCallbacks callbacks, Handler handler,
- SensorManager sensorManager, DisplayBlanker blanker, DisplayDevice displayDevice) {
+ SensorManager sensorManager, DisplayBlanker blanker, int displayId) {
mHandler = new DisplayControllerHandler(handler.getLooper());
mBrightnessTracker = new BrightnessTracker(context, null);
mSettingsObserver = new SettingsObserver(mHandler);
@@ -417,10 +417,9 @@
mBlanker = blanker;
mContext = context;
mBrightnessSynchronizer = new BrightnessSynchronizer(context);
- mDisplayDevice = displayDevice;
+ mDisplayId = displayId;
PowerManager pm = context.getSystemService(PowerManager.class);
- DisplayDeviceConfig displayDeviceConfig = mDisplayDevice.getDisplayDeviceConfig();
final Resources resources = context.getResources();
@@ -515,7 +514,7 @@
mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
- screenBrightnessThresholds, context, displayDeviceConfig);
+ screenBrightnessThresholds, context);
} else {
mUseSoftwareAutoBrightnessConfig = false;
}
@@ -684,7 +683,7 @@
// Initialize the power state object for the default display.
// In the future, we might manage multiple displays independently.
mPowerState = new DisplayPowerState(mBlanker,
- mColorFadeEnabled ? new ColorFade(Display.DEFAULT_DISPLAY) : null);
+ mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId);
if (mColorFadeEnabled) {
mColorFadeOnAnimator = ObjectAnimator.ofFloat(
@@ -1153,7 +1152,7 @@
if (ready && state != Display.STATE_OFF
&& mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_ON) {
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_ON);
- mWindowManagerPolicy.screenTurnedOn();
+ mWindowManagerPolicy.screenTurnedOn(mDisplayId);
}
// Grab a wake lock if we have unfinished business.
@@ -1277,7 +1276,7 @@
if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON) {
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);
blockScreenOff();
- mWindowManagerPolicy.screenTurningOff(mPendingScreenOffUnblocker);
+ mWindowManagerPolicy.screenTurningOff(mDisplayId, mPendingScreenOffUnblocker);
unblockScreenOff();
} else if (mPendingScreenOffUnblocker != null) {
// Abort doing the state change until screen off is unblocked.
@@ -1309,14 +1308,14 @@
&& !mScreenOffBecauseOfProximity) {
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
unblockScreenOn();
- mWindowManagerPolicy.screenTurnedOff();
+ mWindowManagerPolicy.screenTurnedOff(mDisplayId);
} else if (!isOff
&& mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_OFF) {
// We told policy already that screen was turning off, but now we changed our minds.
// Complete the full state transition on -> turningOff -> off.
unblockScreenOff();
- mWindowManagerPolicy.screenTurnedOff();
+ mWindowManagerPolicy.screenTurnedOff(mDisplayId);
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
}
if (!isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF) {
@@ -1326,7 +1325,7 @@
} else {
unblockScreenOn();
}
- mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker);
+ mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker);
}
// Return true if the screen isn't blocked.
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 4b6430d..54f30a9 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -58,6 +58,7 @@
private final DisplayBlanker mBlanker;
private final ColorFade mColorFade;
private final PhotonicModulator mPhotonicModulator;
+ private final int mDisplayId;
private int mScreenState;
private float mScreenBrightness;
@@ -71,13 +72,14 @@
private Runnable mCleanListener;
- public DisplayPowerState(DisplayBlanker blanker, ColorFade colorFade) {
+ public DisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, int displayId) {
mHandler = new Handler(true /*async*/);
mChoreographer = Choreographer.getInstance();
mBlanker = blanker;
mColorFade = colorFade;
mPhotonicModulator = new PhotonicModulator();
mPhotonicModulator.start();
+ mDisplayId = displayId;
// At boot time, we know that the screen is on and the electron beam
// animation is not playing. We don't know the screen's brightness though,
@@ -434,10 +436,10 @@
// Apply pending change.
if (DEBUG) {
- Slog.d(TAG, "Updating screen state: state="
+ Slog.d(TAG, "Updating screen state: id=" + mDisplayId + ", state="
+ Display.stateToString(state) + ", backlight=" + brightnessState);
}
- mBlanker.requestDisplayState(state, brightnessState);
+ mBlanker.requestDisplayState(mDisplayId, state, brightnessState);
}
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 507a265..858cce4 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -969,6 +969,10 @@
}
private int findMatchingModeIdLocked(int configId) {
+ if (configId < 0 || configId >= mDisplayConfigs.length) {
+ Slog.e(TAG, "Invalid display config index " + configId);
+ return NO_DISPLAY_MODE_ID;
+ }
SurfaceControl.DisplayConfig config = mDisplayConfigs[configId];
for (int i = 0; i < mSupportedModes.size(); i++) {
DisplayModeRecord record = mSupportedModes.valueAt(i);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index bf8b891..a17a294 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -16,9 +16,11 @@
package com.android.server.display;
+import android.annotation.NonNull;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerInternal;
+import android.util.Slog;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -57,6 +59,7 @@
* </p>
*/
final class LogicalDisplay {
+ private static final String TAG = "LogicalDisplay";
private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
// The layer stack we use when the display has been blanked to prevent any
@@ -114,6 +117,12 @@
private final Rect mTempLayerStackRect = new Rect();
private final Rect mTempDisplayRect = new Rect();
+ /**
+ * Indicates that the Logical display is enabled (default). See {@link #setEnabled} for
+ * more information.
+ */
+ private boolean mIsEnabled = true;
+
public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
mDisplayId = displayId;
mLayerStack = layerStack;
@@ -575,6 +584,44 @@
mDisplayScalingDisabled = disableScaling;
}
+ /**
+ * Swap the underlying {@link DisplayDevice} with the specificed LogicalDisplay.
+ *
+ * @param targetDisplay The display with which to swap display-devices.
+ * @return {@code true} if the displays were swapped, {@code false} otherwise.
+ */
+ public boolean swapDisplaysLocked(@NonNull LogicalDisplay targetDisplay) {
+ final DisplayDevice targetDevice = targetDisplay.getPrimaryDisplayDeviceLocked();
+ if (mPrimaryDisplayDevice == null || targetDevice == null) {
+ Slog.e(TAG, "Missing display device during swap: " + mPrimaryDisplayDevice + " , "
+ + targetDevice);
+ return false;
+ }
+
+ final DisplayDevice tmpDevice = mPrimaryDisplayDevice;
+ mPrimaryDisplayDevice = targetDisplay.mPrimaryDisplayDevice;
+ targetDisplay.mPrimaryDisplayDevice = tmpDevice;
+ return true;
+ }
+
+ /**
+ * Sets the LogicalDisplay to be enabled or disabled. If the display is not enabled,
+ * the system will always set the display to power off, regardless of the global state of the
+ * device.
+ * TODO: b/170498827 - Remove when updateDisplayStateLocked is updated.
+ */
+ public void setEnabled(boolean isEnabled) {
+ mIsEnabled = isEnabled;
+ }
+
+ /**
+ * @return {@code true} iff the LogicalDisplay is enabled or {@code false}
+ * if disabled indicating that the display has been forced to be OFF.
+ */
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
+
public void dumpLocked(PrintWriter pw) {
pw.println("mDisplayId=" + mDisplayId);
pw.println("mLayerStack=" + mLayerStack);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 8a90a14..942a12e 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -16,7 +16,9 @@
package com.android.server.display;
+import android.content.Context;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -26,6 +28,7 @@
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.Objects;
import java.util.function.Consumer;
/**
@@ -39,9 +42,12 @@
class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
private static final String TAG = "LogicalDisplayMapper";
+ private static final boolean DEBUG = false;
+
public static final int LOGICAL_DISPLAY_EVENT_ADDED = 1;
public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 2;
public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 3;
+ public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4;
/**
* Temporary display info, used for comparing display configurations.
@@ -59,6 +65,24 @@
private final boolean mSingleDisplayDemoMode;
/**
+ * Physical Display ID of the DisplayDevice to associate with the default LogicalDisplay
+ * when {@link mIsFolded} is set to {@code true}.
+ */
+ private String mDisplayIdToUseWhenFolded;
+
+ /**
+ * Physical Display ID of the DisplayDevice to associate with the default LogicalDisplay
+ * when {@link mIsFolded} is set to {@code false}.
+ */
+ private String mDisplayIdToUseWhenUnfolded;
+
+ /** Overrides the folded state of the device. For use with ADB commands. */
+ private Boolean mIsFoldedOverride;
+
+ /** Saves the last device fold state. */
+ private boolean mIsFolded;
+
+ /**
* List of all logical displays indexed by logical display id.
* Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
* TODO: multi-display - Move the aforementioned comment?
@@ -71,13 +95,15 @@
private final PersistentDataStore mPersistentDataStore;
private final Listener mListener;
- LogicalDisplayMapper(DisplayDeviceRepository repo, Listener listener,
+ LogicalDisplayMapper(Context context, DisplayDeviceRepository repo, Listener listener,
PersistentDataStore persistentDataStore) {
mDisplayDeviceRepo = repo;
mPersistentDataStore = persistentDataStore;
mListener = listener;
mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
mDisplayDeviceRepo.addListener(this);
+
+ loadFoldedDisplayConfig(context);
}
@Override
@@ -141,23 +167,104 @@
}
}
+ public void setDeviceFoldedLocked(boolean isFolded) {
+ mIsFolded = isFolded;
+ if (mIsFoldedOverride != null) {
+ isFolded = mIsFoldedOverride.booleanValue();
+ }
+
+ if (mDisplayIdToUseWhenFolded == null || mDisplayIdToUseWhenUnfolded == null
+ || mLogicalDisplays.size() < 2) {
+ // Do nothing if this behavior is disabled or there are less than two displays.
+ return;
+ }
+
+ final DisplayDevice deviceFolded =
+ mDisplayDeviceRepo.getByIdLocked(mDisplayIdToUseWhenFolded);
+ final DisplayDevice deviceUnfolded =
+ mDisplayDeviceRepo.getByIdLocked(mDisplayIdToUseWhenUnfolded);
+ if (deviceFolded == null || deviceUnfolded == null) {
+ // If the expected devices for folding functionality are not present, return early.
+ return;
+ }
+
+ // Find the associated LogicalDisplays for the configured "folding" DeviceDisplays.
+ final LogicalDisplay displayFolded = getLocked(deviceFolded);
+ final LogicalDisplay displayUnfolded = getLocked(deviceUnfolded);
+ if (displayFolded == null || displayFolded == null) {
+ // If the expected displays are not present, return early.
+ return;
+ }
+
+ // Find out which display is currently default and which is disabled.
+ final LogicalDisplay defaultDisplay = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
+ final LogicalDisplay disabledDisplay;
+ if (defaultDisplay == displayFolded) {
+ disabledDisplay = displayUnfolded;
+ } else if (defaultDisplay == displayUnfolded) {
+ disabledDisplay = displayFolded;
+ } else {
+ // If neither folded or unfolded displays are currently set to the default display, we
+ // are in an unknown state and it's best to log the error and bail.
+ Slog.e(TAG, "Unexpected: when attempting to swap displays, neither of the two"
+ + " configured displays were set up as the default display. Default: "
+ + defaultDisplay.getDisplayInfoLocked() + ", ConfiguredDisplays: [ folded="
+ + displayFolded.getDisplayInfoLocked() + ", unfolded="
+ + displayUnfolded.getDisplayInfoLocked() + " ]");
+ return;
+ }
+
+ if (isFolded == (defaultDisplay == displayFolded)) {
+ // Nothing to do, already in the right state.
+ return;
+ }
+
+ // Everything was checked and we need to swap, lets swap.
+ displayFolded.swapDisplaysLocked(displayUnfolded);
+
+ // We ensure that the non-default Display is always forced to be off. This was likely
+ // already done in a previous iteration, but we do it with each swap in case something in
+ // the underlying LogicalDisplays changed: like LogicalDisplay recreation, for example.
+ defaultDisplay.setEnabled(true);
+ disabledDisplay.setEnabled(false);
+
+ // Update the world
+ updateLogicalDisplaysLocked();
+
+ if (DEBUG) {
+ Slog.d(TAG, "Folded displays: isFolded: " + isFolded + ", defaultDisplay? "
+ + defaultDisplay.getDisplayInfoLocked());
+ }
+ }
+
public void dumpLocked(PrintWriter pw) {
pw.println("LogicalDisplayMapper:");
- pw.println(" mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
- pw.println(" mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.increaseIndent();
+
+ ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
+ ipw.println("mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
final int logicalDisplayCount = mLogicalDisplays.size();
- pw.println();
- pw.println(" Logical Displays: size=" + logicalDisplayCount);
+ ipw.println();
+ ipw.println("Logical Displays: size=" + logicalDisplayCount);
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.increaseIndent();
for (int i = 0; i < logicalDisplayCount; i++) {
int displayId = mLogicalDisplays.keyAt(i);
LogicalDisplay display = mLogicalDisplays.valueAt(i);
- pw.println(" Display " + displayId + ":");
+ ipw.println("Display " + displayId + ":");
+ ipw.increaseIndent();
display.dumpLocked(ipw);
+ ipw.decreaseIndent();
+ ipw.println();
+ }
+ }
+
+ void setFoldOverrideLocked(Boolean isFolded) {
+ if (!Objects.equals(isFolded, mIsFoldedOverride)) {
+ mIsFoldedOverride = isFolded;
+ setDeviceFoldedLocked(mIsFolded);
}
}
@@ -211,8 +318,11 @@
mListener.onLogicalDisplayEventLocked(display,
LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED);
} else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
- mListener.onLogicalDisplayEventLocked(display,
- LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED);
+ final String oldUniqueId = mTempDisplayInfo.uniqueId;
+ final String newUniqueId = display.getDisplayInfoLocked().uniqueId;
+ final int eventMsg = TextUtils.equals(oldUniqueId, newUniqueId)
+ ? LOGICAL_DISPLAY_EVENT_CHANGED : LOGICAL_DISPLAY_EVENT_SWAPPED;
+ mListener.onLogicalDisplayEventLocked(display, eventMsg);
} else {
// While applications shouldn't know nor care about the non-overridden info, we
// still need to let WindowManager know so it can update its own internal state for
@@ -236,6 +346,21 @@
return displayId;
}
+ private void loadFoldedDisplayConfig(Context context) {
+ final String[] displayIds = context.getResources().getStringArray(
+ com.android.internal.R.array.config_internalFoldedPhysicalDisplayIds);
+
+ if (displayIds.length != 2 || TextUtils.isEmpty(displayIds[0])
+ || TextUtils.isEmpty(displayIds[1])) {
+ Slog.w(TAG, "Folded display configuration invalid: [" + Arrays.toString(displayIds)
+ + "]");
+ return;
+ }
+
+ mDisplayIdToUseWhenFolded = displayIds[0];
+ mDisplayIdToUseWhenUnfolded = displayIds[1];
+ }
+
public interface Listener {
void onLogicalDisplayEventLocked(LogicalDisplay display, int event);
void onTraversalRequested();
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 264c611..fb23e01 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -17,7 +17,10 @@
package com.android.server.location;
import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.database.ContentObserver;
import android.hardware.contexthub.V1_0.AsyncEventType;
import android.hardware.contexthub.V1_0.ContextHub;
@@ -26,8 +29,6 @@
import android.hardware.contexthub.V1_0.IContexthubCallback;
import android.hardware.contexthub.V1_0.Result;
import android.hardware.contexthub.V1_0.TransactionResult;
-import android.hardware.contexthub.V1_1.Setting;
-import android.hardware.contexthub.V1_1.SettingValue;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubMessage;
import android.hardware.location.ContextHubTransaction;
@@ -43,6 +44,7 @@
import android.hardware.location.NanoAppMessage;
import android.hardware.location.NanoAppState;
import android.location.LocationManager;
+import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -111,6 +113,12 @@
// The manager for the internal nanoapp state cache
private final NanoAppStateManager mNanoAppStateManager = new NanoAppStateManager();
+ // True if WiFi is available for the Context Hub
+ private boolean mIsWifiAvailable = false;
+
+ // Lock object for sendWifiSettingUpdate()
+ private final Object mSendWifiSettingUpdateLock = new Object();
+
/**
* Class extending the callback to register with a Context Hub.
*/
@@ -196,7 +204,7 @@
}
mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap);
- if (mContextHubWrapper.supportsSettingNotifications()) {
+ if (mContextHubWrapper.supportsLocationSettingNotifications()) {
sendLocationSettingUpdate();
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE),
@@ -208,6 +216,32 @@
}
}, UserHandle.USER_ALL);
}
+
+ if (mContextHubWrapper.supportsWifiSettingNotifications()) {
+ sendWifiSettingUpdate(true /* forceUpdate */);
+
+ BroadcastReceiver wifiReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
+ sendWifiSettingUpdate(false /* forceUpdate */);
+ }
+ }
+ };
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ mContext.registerReceiver(wifiReceiver, filter);
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE),
+ true /* notifyForDescendants */,
+ new ContentObserver(null /* handler */) {
+ @Override
+ public void onChange(boolean selfChange) {
+ sendWifiSettingUpdate(false /* forceUpdate */);
+ }
+ }, UserHandle.USER_ALL);
+ }
}
/**
@@ -260,7 +294,10 @@
* @return the IContextHubWrapper interface
*/
private IContextHubWrapper getContextHubWrapper() {
- IContextHubWrapper wrapper = IContextHubWrapper.maybeConnectTo1_1();
+ IContextHubWrapper wrapper = IContextHubWrapper.maybeConnectTo1_2();
+ if (wrapper == null) {
+ wrapper = IContextHubWrapper.maybeConnectTo1_1();
+ }
if (wrapper == null) {
wrapper = IContextHubWrapper.maybeConnectTo1_0();
}
@@ -454,7 +491,6 @@
*
* @param contextHubId the ID of the hub to do the query
* @return the result of the query
- *
* @throws IllegalStateException if the transaction queue is full
*/
private int queryNanoAppsInternal(int contextHubId) {
@@ -528,7 +564,6 @@
/**
* A helper function to handle a load response from the Context Hub for the old API.
- *
* TODO(b/69270990): Remove this once the old APIs are obsolete.
*/
private void handleLoadResponseOldApi(
@@ -578,6 +613,7 @@
private void handleHubEventCallback(int contextHubId, int eventType) {
if (eventType == AsyncEventType.RESTARTED) {
sendLocationSettingUpdate();
+ sendWifiSettingUpdate(true /* forceUpdate */);
mTransactionManager.onHubReset();
queryNanoAppsInternal(contextHubId);
@@ -628,10 +664,9 @@
* @param contextHubId the ID of the hub this client is attached to
* @param clientCallback the client interface to register with the service
* @return the generated client interface, null if registration was unsuccessful
- *
* @throws IllegalArgumentException if contextHubId is not a valid ID
- * @throws IllegalStateException if max number of clients have already registered
- * @throws NullPointerException if clientCallback is null
+ * @throws IllegalStateException if max number of clients have already registered
+ * @throws NullPointerException if clientCallback is null
*/
@Override
public IContextHubClient createClient(
@@ -655,7 +690,6 @@
* @param pendingIntent the PendingIntent associated with this client
* @param nanoAppId the ID of the nanoapp PendingIntent events will be sent for
* @return the generated client interface
- *
* @throws IllegalArgumentException if hubInfo does not represent a valid hub
* @throws IllegalStateException if there were too many registered clients at the service
*/
@@ -677,7 +711,6 @@
* @param contextHubId the ID of the hub to load the binary
* @param transactionCallback the client-facing transaction callback interface
* @param nanoAppBinary the binary to load
- *
* @throws IllegalStateException if the transaction queue is full
*/
@Override
@@ -707,7 +740,6 @@
* @param contextHubId the ID of the hub to unload the nanoapp
* @param transactionCallback the client-facing transaction callback interface
* @param nanoAppId the ID of the nanoapp to unload
- *
* @throws IllegalStateException if the transaction queue is full
*/
@Override
@@ -731,7 +763,6 @@
* @param contextHubId the ID of the hub to enable the nanoapp
* @param transactionCallback the client-facing transaction callback interface
* @param nanoAppId the ID of the nanoapp to enable
- *
* @throws IllegalStateException if the transaction queue is full
*/
@Override
@@ -755,7 +786,6 @@
* @param contextHubId the ID of the hub to disable the nanoapp
* @param transactionCallback the client-facing transaction callback interface
* @param nanoAppId the ID of the nanoapp to disable
- *
* @throws IllegalStateException if the transaction queue is full
*/
@Override
@@ -778,7 +808,6 @@
*
* @param contextHubId the ID of the hub to query
* @param transactionCallback the client-facing transaction callback interface
- *
* @throws IllegalStateException if the transaction queue is full
*/
@Override
@@ -889,7 +918,6 @@
* @param contextHubId the ID of the hub to start the transaction
* @param callback the client transaction callback interface
* @param transactionType the type of the transaction
- *
* @return {@code true} if mContextHubWrapper and contextHubId is valid, {@code false} otherwise
*/
private boolean checkHalProxyAndContextHubId(
@@ -920,14 +948,28 @@
}
/**
- * Obtains the latest location setting value and notifies the Contexthub.
+ * Obtains the latest location setting value and notifies the Context Hub.
*/
private void sendLocationSettingUpdate() {
boolean enabled = mContext.getSystemService(LocationManager.class)
.isLocationEnabledForUser(UserHandle.CURRENT);
+ mContextHubWrapper.onLocationSettingChanged(enabled);
+ }
- mContextHubWrapper.onSettingChanged(Setting.LOCATION,
- enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+ /**
+ * Obtains the latest WiFi availability setting value and notifies the Context Hub.
+ * @param forceUpdate True to force send update to the Context Hub, otherwise only send the
+ * update when the WiFi availability changes.
+ */
+ private void sendWifiSettingUpdate(boolean forceUpdate) {
+ synchronized (mSendWifiSettingUpdateLock) {
+ WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
+ boolean enabled = wifiManager.isWifiEnabled() || wifiManager.isScanAlwaysAvailable();
+ if (forceUpdate || mIsWifiAvailable != enabled) {
+ mIsWifiAvailable = enabled;
+ mContextHubWrapper.onWifiSettingChanged(enabled);
+ }
+ }
}
private String getCallingPackageName() {
diff --git a/services/core/java/com/android/server/location/IContextHubWrapper.java b/services/core/java/com/android/server/location/IContextHubWrapper.java
index 79fa5c7..613964a 100644
--- a/services/core/java/com/android/server/location/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/IContextHubWrapper.java
@@ -45,12 +45,7 @@
Log.i(TAG, "Context Hub HAL service not found");
}
- ContextHubWrapperV1_0 wrapper = null;
- if (proxy != null) {
- wrapper = new ContextHubWrapperV1_0(proxy);
- }
-
- return wrapper;
+ return (proxy == null) ? null : new ContextHubWrapperV1_0(proxy);
}
/**
@@ -69,12 +64,26 @@
Log.i(TAG, "Context Hub HAL service not found");
}
- ContextHubWrapperV1_1 wrapper = null;
- if (proxy != null) {
- wrapper = new ContextHubWrapperV1_1(proxy);
+ return (proxy == null) ? null : new ContextHubWrapperV1_1(proxy);
+ }
+
+ /**
+ * Attempts to connect to the Contexthub HAL 1.2 service, if it exists.
+ *
+ * @return A valid IContextHubWrapper if the connection was successful, null otherwise.
+ */
+ @Nullable
+ public static IContextHubWrapper maybeConnectTo1_2() {
+ android.hardware.contexthub.V1_2.IContexthub proxy = null;
+ try {
+ proxy = android.hardware.contexthub.V1_2.IContexthub.getService(true /* retry */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy", e);
+ } catch (NoSuchElementException e) {
+ Log.i(TAG, "Context Hub HAL service not found");
}
- return wrapper;
+ return (proxy == null) ? null : new ContextHubWrapperV1_2(proxy);
}
/**
@@ -83,19 +92,29 @@
public abstract android.hardware.contexthub.V1_0.IContexthub getHub();
/**
- * @return True if this version of the Contexthub HAL supports setting notifications.
+ * @return True if this version of the Contexthub HAL supports Location setting notifications.
*/
- public abstract boolean supportsSettingNotifications();
+ public abstract boolean supportsLocationSettingNotifications();
/**
- * Notifies the Contexthub implementation of a user setting change.
+ * Notifies the Contexthub implementation of a user Location setting change.
*
- * @param setting The user setting that has changed. MUST be one of the values from the
- * {@link Setting} enum
- * @param newValue The value of the user setting that changed. MUST be one of the values
- * from the {@link SettingValue} enum.
+ * @param enabled True if the Location setting has been enabled.
*/
- public abstract void onSettingChanged(byte setting, byte newValue);
+ public abstract void onLocationSettingChanged(boolean enabled);
+
+ /**
+ * @return True if this version of the Contexthub HAL supports WiFi availability setting
+ * notifications.
+ */
+ public abstract boolean supportsWifiSettingNotifications();
+
+ /**
+ * Notifies the Contexthub implementation of a user WiFi availability setting change.
+ *
+ * @param enabled true if the WiFi availability setting has been enabled.
+ */
+ public abstract void onWifiSettingChanged(boolean enabled);
private static class ContextHubWrapperV1_0 extends IContextHubWrapper {
private android.hardware.contexthub.V1_0.IContexthub mHub;
@@ -108,11 +127,19 @@
return mHub;
}
- public boolean supportsSettingNotifications() {
+ public boolean supportsLocationSettingNotifications() {
return false;
}
- public void onSettingChanged(byte setting, byte newValue) {}
+ public boolean supportsWifiSettingNotifications() {
+ return false;
+ }
+
+ public void onLocationSettingChanged(boolean enabled) {
+ }
+
+ public void onWifiSettingChanged(boolean enabled) {
+ }
}
private static class ContextHubWrapperV1_1 extends IContextHubWrapper {
@@ -126,14 +153,60 @@
return mHub;
}
- public boolean supportsSettingNotifications() {
+ public boolean supportsLocationSettingNotifications() {
return true;
}
- public void onSettingChanged(byte setting, byte newValue) {
+ public boolean supportsWifiSettingNotifications() {
+ return false;
+ }
+
+ public void onLocationSettingChanged(boolean enabled) {
try {
- mHub.onSettingChanged(setting, newValue);
- } catch (RemoteException e) {
+ mHub.onSettingChanged(Setting.LOCATION,
+ enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send setting change to Contexthub", e);
+ }
+ }
+
+ public void onWifiSettingChanged(boolean enabled) {
+ }
+ }
+
+ private static class ContextHubWrapperV1_2 extends IContextHubWrapper {
+ private android.hardware.contexthub.V1_2.IContexthub mHub;
+
+ ContextHubWrapperV1_2(android.hardware.contexthub.V1_2.IContexthub hub) {
+ mHub = hub;
+ }
+
+ public android.hardware.contexthub.V1_0.IContexthub getHub() {
+ return mHub;
+ }
+
+ public boolean supportsLocationSettingNotifications() {
+ return true;
+ }
+
+ public boolean supportsWifiSettingNotifications() {
+ return true;
+ }
+
+ public void onLocationSettingChanged(boolean enabled) {
+ sendSettingChanged(Setting.LOCATION,
+ enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+ }
+
+ public void onWifiSettingChanged(boolean enabled) {
+ sendSettingChanged(android.hardware.contexthub.V1_2.Setting.WIFI_AVAILABLE,
+ enabled ? SettingValue.ENABLED : SettingValue.DISABLED);
+ }
+
+ private void sendSettingChanged(byte setting, byte newValue) {
+ try {
+ mHub.onSettingChanged_1_2(setting, newValue);
+ } catch (RemoteException e) {
Log.e(TAG, "Failed to send setting change to Contexthub", e);
}
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index f72fee6..cdb73d8 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -216,7 +216,7 @@
private final LocalService mLocalService;
private final GeofenceManager mGeofenceManager;
- @Nullable private volatile GnssManagerService mGnssManagerService = null;
+ private volatile @Nullable GnssManagerService mGnssManagerService = null;
private GeocoderProxy mGeocodeProvider;
@GuardedBy("mLock")
@@ -604,7 +604,8 @@
|| !request.getWorkSource().isEmpty();
if (usesSystemApi
&& isChangeEnabled(PREVENT_PENDING_INTENT_SYSTEM_API_USAGE, identity.getUid())) {
- throw new SecurityException("PendingIntent location requests may not use system APIs");
+ throw new SecurityException(
+ "PendingIntent location requests may not use system APIs: " + request);
}
request = validateLocationRequest(request, identity);
@@ -1091,19 +1092,6 @@
}
@Override
- @NonNull
- public List<LocationRequest> getTestProviderCurrentRequests(String provider) {
- mContext.enforceCallingOrSelfPermission(permission.READ_DEVICE_CONFIG, null);
-
- LocationProviderManager manager = getLocationProviderManager(provider);
- if (manager == null) {
- throw new IllegalArgumentException("provider doesn't exist: " + provider);
- }
-
- return manager.getMockProviderRequests();
- }
-
- @Override
public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
ParcelFileDescriptor err, String[] args) {
return new LocationShellCommand(this).exec(
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index 3b3c6f0..179fb7d 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -23,15 +23,12 @@
import static android.location.LocationManager.KEY_LOCATION_CHANGED;
import static android.location.LocationManager.KEY_PROVIDER_ENABLED;
import static android.location.LocationManager.PASSIVE_PROVIDER;
-import static android.location.LocationRequest.PASSIVE_INTERVAL;
import static android.os.IPowerManager.LOCATION_MODE_NO_CHANGE;
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 com.android.internal.location.ProviderRequest.EMPTY_REQUEST;
-import static com.android.internal.location.ProviderRequest.INTERVAL_DISABLED;
import static com.android.server.location.LocationManagerService.D;
import static com.android.server.location.LocationManagerService.TAG;
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
@@ -503,14 +500,7 @@
LocationRequest.Builder builder = new LocationRequest.Builder(baseRequest);
if (mPermissionLevel < PERMISSION_FINE) {
- switch (baseRequest.getQuality()) {
- case LocationRequest.ACCURACY_FINE:
- builder.setQuality(LocationRequest.ACCURACY_BLOCK);
- break;
- case LocationRequest.POWER_HIGH:
- builder.setQuality(LocationRequest.POWER_LOW);
- break;
- }
+ builder.setQuality(LocationRequest.QUALITY_LOW_POWER);
if (baseRequest.getIntervalMillis() < MIN_COARSE_INTERVAL_MS) {
builder.setIntervalMillis(MIN_COARSE_INTERVAL_MS);
}
@@ -1391,16 +1381,6 @@
}
}
- public List<LocationRequest> getMockProviderRequests() {
- synchronized (mLock) {
- if (!mProvider.isMock()) {
- throw new IllegalArgumentException(mName + " provider is not a test provider");
- }
-
- return mProvider.getCurrentRequest().getLocationRequests();
- }
- }
-
@Nullable
public Location getLastLocation(CallerIdentity identity, @PermissionLevel int permissionLevel,
boolean ignoreLocationSettings) {
@@ -1719,7 +1699,7 @@
@Override
protected boolean registerWithService(ProviderRequest request,
Collection<Registration> registrations) {
- return reregisterWithService(EMPTY_REQUEST, request, registrations);
+ return reregisterWithService(ProviderRequest.EMPTY_REQUEST, request, registrations);
}
@GuardedBy("mLock")
@@ -1788,8 +1768,8 @@
Preconditions.checkState(Thread.holdsLock(mLock));
}
- mLocationEventLog.logProviderUpdateRequest(mName, EMPTY_REQUEST);
- mProvider.setRequest(EMPTY_REQUEST);
+ mLocationEventLog.logProviderUpdateRequest(mName, ProviderRequest.EMPTY_REQUEST);
+ mProvider.setRequest(ProviderRequest.EMPTY_REQUEST);
}
@GuardedBy("mLock")
@@ -1849,27 +1829,27 @@
Preconditions.checkState(Thread.holdsLock(mLock));
}
- long intervalMs = INTERVAL_DISABLED;
+ long intervalMs = ProviderRequest.INTERVAL_DISABLED;
+ int quality = LocationRequest.QUALITY_LOW_POWER;
boolean locationSettingsIgnored = false;
boolean lowPower = true;
- ArrayList<LocationRequest> locationRequests = new ArrayList<>(registrations.size());
for (Registration registration : registrations) {
LocationRequest request = registration.getRequest();
// passive requests do not contribute to the provider request
- if (request.getIntervalMillis() == PASSIVE_INTERVAL) {
+ if (request.getIntervalMillis() == LocationRequest.PASSIVE_INTERVAL) {
continue;
}
intervalMs = min(request.getIntervalMillis(), intervalMs);
+ quality = min(request.getQuality(), quality);
locationSettingsIgnored |= request.isLocationSettingsIgnored();
lowPower &= request.isLowPower();
- locationRequests.add(request);
}
- if (intervalMs == INTERVAL_DISABLED) {
- return EMPTY_REQUEST;
+ if (intervalMs == ProviderRequest.INTERVAL_DISABLED) {
+ return ProviderRequest.EMPTY_REQUEST;
}
// calculate who to blame for power in a somewhat arbitrary fashion. we pick a threshold
@@ -1882,7 +1862,7 @@
} catch (ArithmeticException e) {
// check for and handle overflow by setting to one below the passive interval so passive
// requests are automatically skipped
- thresholdIntervalMs = PASSIVE_INTERVAL - 1;
+ thresholdIntervalMs = LocationRequest.PASSIVE_INTERVAL - 1;
}
WorkSource workSource = new WorkSource();
@@ -1894,9 +1874,9 @@
return new ProviderRequest.Builder()
.setIntervalMillis(intervalMs)
+ .setQuality(quality)
.setLocationSettingsIgnored(locationSettingsIgnored)
.setLowPower(lowPower)
- .setLocationRequests(locationRequests)
.setWorkSource(workSource)
.build();
}
diff --git a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
index fc10d5f..b771861 100644
--- a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
+++ b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
@@ -63,15 +63,7 @@
@Override
protected ProviderRequest mergeRegistrations(Collection<Registration> registrations) {
- boolean locationSettingsIgnored = false;
- for (Registration registration : registrations) {
- locationSettingsIgnored |= registration.getRequest().isLocationSettingsIgnored();
- }
-
- return new ProviderRequest.Builder()
- .setIntervalMillis(0)
- .setLocationSettingsIgnored(locationSettingsIgnored)
- .build();
+ return new ProviderRequest.Builder().setIntervalMillis(0).build();
}
@Override
@@ -79,4 +71,9 @@
Collection<Registration> registrations) {
return 0;
}
+
+ @Override
+ protected String getServiceState() {
+ return mProvider.getCurrentRequest().isActive() ? "registered" : "unregistered";
+ }
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index b94f155..ec48d4c 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -67,7 +67,7 @@
/**
* Registration object for GNSS listeners.
*/
- protected final class GnssListenerRegistration extends
+ protected class GnssListenerRegistration extends
BinderListenerRegistration<TRequest, TListener> {
// we store these values because we don't trust the listeners not to give us dupes, not to
@@ -232,13 +232,21 @@
final long identity = Binder.clearCallingIdentity();
try {
addRegistration(listener.asBinder(),
- new GnssListenerRegistration(request, callerIdentity, listener));
+ createRegistration(request, callerIdentity, listener));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
+ * May be overridden by subclasses to change the registration type.
+ */
+ protected GnssListenerRegistration createRegistration(TRequest request,
+ CallerIdentity callerIdentity, TListener listener) {
+ return new GnssListenerRegistration(request, callerIdentity, listener);
+ }
+
+ /**
* Removes the given listener.
*/
public void removeListener(TListener listener) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index e144b40..e25e605 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -708,12 +708,12 @@
// For fast GNSS TTFF
provider = LocationManager.NETWORK_PROVIDER;
locationListener = mNetworkLocationListener;
- locationRequest.setQuality(LocationRequest.POWER_LOW);
+ locationRequest.setQuality(LocationRequest.QUALITY_LOW_POWER);
} else {
// For Device-Based Hybrid (E911)
provider = LocationManager.FUSED_PROVIDER;
locationListener = mFusedLocationListener;
- locationRequest.setQuality(LocationRequest.ACCURACY_FINE);
+ locationRequest.setQuality(LocationRequest.QUALITY_HIGH_ACCURACY);
}
// Ignore location settings if in emergency mode. This is only allowed for
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 2faa15f..74284f3 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -19,6 +19,7 @@
import static com.android.server.location.gnss.GnssManagerService.D;
import static com.android.server.location.gnss.GnssManagerService.TAG;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.location.GnssMeasurementsEvent;
import android.location.GnssRequest;
@@ -32,6 +33,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationAttributionHelper;
import com.android.server.location.util.LocationUsageLogger;
import com.android.server.location.util.SettingsHelper;
@@ -44,11 +46,40 @@
*
* @hide
*/
-public class GnssMeasurementsProvider extends
+public final class GnssMeasurementsProvider extends
GnssListenerMultiplexer<GnssRequest, IGnssMeasurementsListener, Boolean> implements
SettingsHelper.GlobalSettingChangedListener {
+ private class GnssMeasurementListenerRegistration extends GnssListenerRegistration {
+
+ private static final String GNSS_MEASUREMENTS_BUCKET = "gnss_measurement";
+
+ protected GnssMeasurementListenerRegistration(
+ @Nullable GnssRequest gnssRequest,
+ CallerIdentity callerIdentity,
+ IGnssMeasurementsListener iGnssMeasurementsListener) {
+ super(gnssRequest, callerIdentity, iGnssMeasurementsListener);
+ }
+
+ @Nullable
+ @Override
+ protected ListenerOperation<IGnssMeasurementsListener> onActive() {
+ mLocationAttributionHelper.reportHighPowerLocationStart(
+ getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
+ return null;
+ }
+
+ @Nullable
+ @Override
+ protected ListenerOperation<IGnssMeasurementsListener> onInactive() {
+ mLocationAttributionHelper.reportHighPowerLocationStop(
+ getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
+ return null;
+ }
+ }
+
private final AppOpsHelper mAppOpsHelper;
+ private final LocationAttributionHelper mLocationAttributionHelper;
private final LocationUsageLogger mLogger;
private final GnssMeasurementProviderNative mNative;
@@ -60,6 +91,7 @@
public GnssMeasurementsProvider(Injector injector, GnssMeasurementProviderNative aNative) {
super(injector);
mAppOpsHelper = injector.getAppOpsHelper();
+ mLocationAttributionHelper = injector.getLocationAttributionHelper();
mLogger = injector.getLocationUsageLogger();
mNative = aNative;
}
@@ -76,6 +108,12 @@
}
@Override
+ protected GnssListenerRegistration createRegistration(GnssRequest request,
+ CallerIdentity callerIdentity, IGnssMeasurementsListener listener) {
+ return new GnssMeasurementListenerRegistration(request, callerIdentity, listener);
+ }
+
+ @Override
protected boolean registerWithService(Boolean fullTrackingRequest,
Collection<GnssListenerRegistration> registrations) {
Preconditions.checkState(mNative.isMeasurementSupported());
diff --git a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
index bc3ac0f..36b3ef3 100644
--- a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
+++ b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
@@ -38,12 +38,12 @@
*/
public class LocationAttributionHelper {
- private static class ProviderListener {
- private final String mProvider;
+ private static class BucketKey {
+ private final String mBucket;
private final Object mKey;
- private ProviderListener(String provider, Object key) {
- mProvider = Objects.requireNonNull(provider);
+ private BucketKey(String bucket, Object key) {
+ mBucket = Objects.requireNonNull(bucket);
mKey = Objects.requireNonNull(key);
}
@@ -56,23 +56,23 @@
return false;
}
- ProviderListener that = (ProviderListener) o;
- return mProvider.equals(that.mProvider)
+ BucketKey that = (BucketKey) o;
+ return mBucket.equals(that.mBucket)
&& mKey.equals(that.mKey);
}
@Override
public int hashCode() {
- return Objects.hash(mProvider, mKey);
+ return Objects.hash(mBucket, mKey);
}
}
private final AppOpsHelper mAppOpsHelper;
@GuardedBy("this")
- private final Map<CallerIdentity, Set<ProviderListener>> mAttributions;
+ private final Map<CallerIdentity, Set<BucketKey>> mAttributions;
@GuardedBy("this")
- private final Map<CallerIdentity, Set<ProviderListener>> mHighPowerAttributions;
+ private final Map<CallerIdentity, Set<BucketKey>> mHighPowerAttributions;
public LocationAttributionHelper(AppOpsHelper appOpsHelper) {
mAppOpsHelper = appOpsHelper;
@@ -82,14 +82,14 @@
}
/**
- * Report normal location usage for the given caller on the given provider, with a unique key.
+ * Report normal location usage for the given caller in the given bucket, with a unique key.
*/
- public synchronized void reportLocationStart(CallerIdentity identity, String provider,
+ public synchronized void reportLocationStart(CallerIdentity identity, String bucket,
Object key) {
- Set<ProviderListener> keySet = mAttributions.computeIfAbsent(identity,
+ Set<BucketKey> keySet = mAttributions.computeIfAbsent(identity,
i -> new ArraySet<>());
boolean empty = keySet.isEmpty();
- if (keySet.add(new ProviderListener(provider, key)) && empty) {
+ if (keySet.add(new BucketKey(bucket, key)) && empty) {
if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
mAttributions.remove(identity);
}
@@ -97,13 +97,13 @@
}
/**
- * Report normal location usage has stopped for the given caller on the given provider, with a
+ * Report normal location usage has stopped for the given caller in the given bucket, with a
* unique key.
*/
- public synchronized void reportLocationStop(CallerIdentity identity, String provider,
+ public synchronized void reportLocationStop(CallerIdentity identity, String bucket,
Object key) {
- Set<ProviderListener> keySet = mAttributions.get(identity);
- if (keySet != null && keySet.remove(new ProviderListener(provider, key))
+ Set<BucketKey> keySet = mAttributions.get(identity);
+ if (keySet != null && keySet.remove(new BucketKey(bucket, key))
&& keySet.isEmpty()) {
mAttributions.remove(identity);
mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
@@ -111,15 +111,15 @@
}
/**
- * Report high power location usage for the given caller on the given provider, with a unique
+ * Report high power location usage for the given caller in the given bucket, with a unique
* key.
*/
- public synchronized void reportHighPowerLocationStart(CallerIdentity identity, String provider,
+ public synchronized void reportHighPowerLocationStart(CallerIdentity identity, String bucket,
Object key) {
- Set<ProviderListener> keySet = mHighPowerAttributions.computeIfAbsent(identity,
+ Set<BucketKey> keySet = mHighPowerAttributions.computeIfAbsent(identity,
i -> new ArraySet<>());
boolean empty = keySet.isEmpty();
- if (keySet.add(new ProviderListener(provider, key)) && empty) {
+ if (keySet.add(new BucketKey(bucket, key)) && empty) {
if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
if (D) {
Log.v(TAG, "starting high power location attribution for " + identity);
@@ -131,13 +131,13 @@
}
/**
- * Report high power location usage has stopped for the given caller on the given provider,
+ * Report high power location usage has stopped for the given caller in the given bucket,
* with a unique key.
*/
- public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String provider,
+ public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String bucket,
Object key) {
- Set<ProviderListener> keySet = mHighPowerAttributions.get(identity);
- if (keySet != null && keySet.remove(new ProviderListener(provider, key))
+ Set<BucketKey> keySet = mHighPowerAttributions.get(identity);
+ if (keySet != null && keySet.remove(new BucketKey(bucket, key))
&& keySet.isEmpty()) {
if (D) {
Log.v(TAG, "stopping high power location attribution for " + identity);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index dcfd3f2..35d3621 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3900,7 +3900,7 @@
// quick check: if this uid doesn't have INTERNET permission, it
// doesn't have network access anyway, so it is a waste to mess
// with it here.
- if (hasInternetPermissionUL(uid)) {
+ if (hasInternetPermissionUL(uid) && !isUidForegroundOnRestrictPowerUL(uid)) {
uidRules.put(uid, FIREWALL_RULE_DENY);
}
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsAccess.java b/services/core/java/com/android/server/net/NetworkStatsAccess.java
index 5a8fbf3..7cdc4cc 100644
--- a/services/core/java/com/android/server/net/NetworkStatsAccess.java
+++ b/services/core/java/com/android/server/net/NetworkStatsAccess.java
@@ -27,6 +27,7 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Process;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
@@ -110,11 +111,12 @@
boolean hasCarrierPrivileges = tm != null &&
tm.checkCarrierPrivilegesForPackageAnyPhone(callingPackage) ==
TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
- boolean isDeviceOwner = dpmi != null && dpmi.isActiveDeviceOwner(callingUid);
+ final boolean isDeviceOwner = dpmi != null && dpmi.isActiveDeviceOwner(callingUid);
+ final int appId = UserHandle.getAppId(callingUid);
if (hasCarrierPrivileges || isDeviceOwner
- || UserHandle.getAppId(callingUid) == android.os.Process.SYSTEM_UID) {
- // Carrier-privileged apps and device owners, and the system can access data usage for
- // all apps on the device.
+ || appId == Process.SYSTEM_UID || appId == Process.NETWORK_STACK_UID) {
+ // Carrier-privileged apps and device owners, and the system (including the
+ // network stack) can access data usage for all apps on the device.
return NetworkStatsAccess.Level.DEVICE;
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index d202a2a..5646c75 100644
--- a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -30,6 +30,7 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.CollectionUtils;
@@ -94,39 +95,41 @@
// also needed to track CBRS.
final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager);
- for (final int subId : newSubs) {
- final RatTypeListener match = CollectionUtils.find(mRatListeners,
- it -> it.mSubId == subId);
- if (match != null) continue;
+ // IMSI is needed for every newly added sub. Listener stores subscriberId into it to
+ // prevent binder call to telephony when querying RAT. Keep listener registration with empty
+ // IMSI is meaningless since the RAT type changed is ambiguous for multi-SIM if reported
+ // with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration.
+ final List<Pair<Integer, String>> filteredNewSubs =
+ CollectionUtils.mapNotNull(newSubs, subId -> {
+ final String subscriberId = mTeleManager.getSubscriberId(subId);
+ return TextUtils.isEmpty(subscriberId) ? null : new Pair(subId, subscriberId);
+ });
- // Create listener for every newly added sub. Also store subscriberId into it to
- // prevent binder call to telephony when querying RAT. If the subscriberId is empty
- // for any reason, such as SIM PIN locked, skip registration.
- // SubscriberId will be unavailable again if 1. modem crashed 2. reboot
- // 3. re-insert SIM. If that happens, the listeners will be eventually synchronized
- // with active sub list once all subscriberIds are ready.
- final String subscriberId = mTeleManager.getSubscriberId(subId);
- if (TextUtils.isEmpty(subscriberId)) {
- Log.d(NetworkStatsService.TAG, "Empty subscriberId for newly added sub "
- + subId + ", skip listener registration");
+ for (final Pair<Integer, String> sub : filteredNewSubs) {
+ // Fully match listener with subId and IMSI, since in some rare cases, IMSI might be
+ // suddenly change regardless of subId, such as switch IMSI feature in modem side.
+ // If that happens, register new listener with new IMSI and remove old one later.
+ if (CollectionUtils.find(mRatListeners,
+ it -> it.equalsKey(sub.first, sub.second)) != null) {
continue;
}
+
final RatTypeListener listener =
- new RatTypeListener(mExecutor, this, subId, subscriberId);
+ new RatTypeListener(mExecutor, this, sub.first, sub.second);
mRatListeners.add(listener);
// Register listener to the telephony manager that associated with specific sub.
- mTeleManager.createForSubscriptionId(subId)
+ mTeleManager.createForSubscriptionId(sub.first)
.listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
- Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + subId);
+ Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + sub.first);
}
for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
- // If the new list contains the subId of the listener, keeps it.
- final Integer match = CollectionUtils.find(newSubs, it -> it == listener.mSubId);
- if (match != null) continue;
-
- handleRemoveRatTypeListener(listener);
+ // If there is no subId and IMSI matched the listener, removes it.
+ if (CollectionUtils.find(filteredNewSubs,
+ it -> listener.equalsKey(it.first, it.second)) == null) {
+ handleRemoveRatTypeListener(listener);
+ }
}
}
@@ -232,5 +235,9 @@
public int getSubId() {
return mSubId;
}
+
+ boolean equalsKey(int subId, @NonNull String subscriberId) {
+ return mSubId == subId && TextUtils.equals(mSubscriberId, subscriberId);
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 74b7bd7..4040f41 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -833,6 +833,7 @@
public void onUserSwitched(int user) {
if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
+ unbindOtherUserServices(user);
rebindServices(true, user);
}
@@ -1219,6 +1220,27 @@
bindToServices(componentsToBind);
}
+ /**
+ * Called when user switched to unbind all services from other users.
+ */
+ @VisibleForTesting
+ void unbindOtherUserServices(int currentUser) {
+ final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+ synchronized (mMutex) {
+ final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
+ for (ManagedServiceInfo info : removableBoundServices) {
+ if (info.userid != currentUser) {
+ Set<ComponentName> toUnbind =
+ componentsToUnbind.get(info.userid, new ArraySet<>());
+ toUnbind.add(info.component);
+ componentsToUnbind.put(info.userid, toUnbind);
+ }
+ }
+ }
+ unbindFromServices(componentsToUnbind);
+ }
+
protected void unbindFromServices(SparseArray<Set<ComponentName>> componentsToUnbind) {
for (int i = 0; i < componentsToUnbind.size(); i++) {
final int userId = componentsToUnbind.keyAt(i);
@@ -1264,7 +1286,8 @@
/**
* Version of registerService that takes the name of a service component to bind to.
*/
- private void registerService(final ComponentName name, final int userid) {
+ @VisibleForTesting
+ void registerService(final ComponentName name, final int userid) {
synchronized (mMutex) {
registerServiceLocked(name, userid);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b776e58..4d6b760f 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -140,6 +140,7 @@
import android.app.UriGrantsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.backup.BackupManager;
+import android.app.compat.CompatChanges;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
import android.app.usage.UsageEvents;
@@ -408,9 +409,19 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private static final long CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK = 128611929L;
+ /**
+ * Activity starts coming from broadcast receivers or services in response to notification and
+ * notification action clicks will be blocked for UX and performance reasons. Instead start the
+ * activity directly from the PendingIntent.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ private static final long NOTIFICATION_TRAMPOLINE_BLOCK = 167676448L;
+
private IActivityManager mAm;
private ActivityTaskManagerInternal mAtm;
private ActivityManager mActivityManager;
+ private ActivityManagerInternal mAmi;
private IPackageManager mPackageManager;
private PackageManager mPackageManagerClient;
AudioManager mAudioManager;
@@ -1887,7 +1898,7 @@
DevicePolicyManagerInternal dpm, IUriGrantsManager ugm,
UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager,
NotificationHistoryManager historyManager, StatsManager statsManager,
- TelephonyManager telephonyManager) {
+ TelephonyManager telephonyManager, ActivityManagerInternal ami) {
mHandler = handler;
Resources resources = getContext().getResources();
mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -1909,6 +1920,7 @@
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mCompanionManager = companionManager;
mActivityManager = activityManager;
+ mAmi = ami;
mDeviceIdleManager = getContext().getSystemService(DeviceIdleManager.class);
mDpm = dpm;
mUm = userManager;
@@ -2187,7 +2199,8 @@
new NotificationHistoryManager(getContext(), handler),
mStatsManager = (StatsManager) getContext().getSystemService(
Context.STATS_MANAGER),
- getContext().getSystemService(TelephonyManager.class));
+ getContext().getSystemService(TelephonyManager.class),
+ LocalServices.getService(ActivityManagerInternal.class));
publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
@@ -4756,7 +4769,7 @@
@Override
public List<ComponentName> getEnabledNotificationListeners(int userId) {
- checkCallerIsSystem();
+ checkNotificationListenerAccess();
return mListeners.getAllowedComponents(userId);
}
@@ -4825,7 +4838,7 @@
public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId,
boolean granted) {
Objects.requireNonNull(listener);
- checkCallerIsSystemOrShell();
+ checkNotificationListenerAccess();
final long identity = Binder.clearCallingIdentity();
try {
if (mAllowedManagedServicePackages.test(
@@ -5098,6 +5111,14 @@
}
};
+ protected void checkNotificationListenerAccess() {
+ if (!isCallerSystemOrPhone()) {
+ getContext().enforceCallingPermission(
+ permission.MANAGE_NOTIFICATION_LISTENERS,
+ "Caller must hold " + permission.MANAGE_NOTIFICATION_LISTENERS);
+ }
+ }
+
@VisibleForTesting
protected void setNotificationAssistantAccessGrantedForUserInternal(
ComponentName assistant, int baseUserId, boolean granted) {
@@ -5229,8 +5250,8 @@
if (!summaries.containsKey(pkg)) {
// Add summary
final ApplicationInfo appInfo =
- adjustedSbn.getNotification().extras.getParcelable(
- Notification.EXTRA_BUILDER_APPLICATION_INFO);
+ adjustedSbn.getNotification().extras.getParcelable(
+ Notification.EXTRA_BUILDER_APPLICATION_INFO);
final Bundle extras = new Bundle();
extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
final String channelId = notificationRecord.getChannel().getId();
@@ -5266,11 +5287,11 @@
notificationRecord.getIsAppImportanceLocked());
summaries.put(pkg, summarySbn.getKey());
}
- }
- if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
- summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
- true)) {
- mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord, isAppForeground));
+ if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
+ summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
+ true)) {
+ mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord, isAppForeground));
+ }
}
}
@@ -6009,13 +6030,17 @@
+ " cannot post for pkg " + targetPkg + " in user " + userId);
}
+ public boolean hasFlag(final int flags, final int flag) {
+ return (flags & flag) != 0;
+ }
/**
* Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
*
* Has side effects.
*/
- private boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
+ boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
NotificationRecord r, boolean isAutogroup) {
+ Notification n = r.getNotification();
final String pkg = r.getSbn().getPackageName();
final boolean isSystemNotification =
isUidSystemOrPhone(uid) || ("android".equals(pkg));
@@ -6024,71 +6049,101 @@
// Limit the number of notifications that any given package except the android
// package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification && !isNotificationFromListener) {
- synchronized (mNotificationLock) {
- final int callingUid = Binder.getCallingUid();
- if (mNotificationsByKey.get(r.getSbn().getKey()) == null
- && isCallerInstantApp(callingUid, userId)) {
- // Ephemeral apps have some special constraints for notifications.
- // They are not allowed to create new notifications however they are allowed to
- // update notifications created by the system (e.g. a foreground service
- // notification).
- throw new SecurityException("Instant app " + pkg
- + " cannot create notifications");
- }
+ final int callingUid = Binder.getCallingUid();
+ if (mNotificationsByKey.get(r.getSbn().getKey()) == null
+ && isCallerInstantApp(callingUid, userId)) {
+ // Ephemeral apps have some special constraints for notifications.
+ // They are not allowed to create new notifications however they are allowed to
+ // update notifications created by the system (e.g. a foreground service
+ // notification).
+ throw new SecurityException("Instant app " + pkg
+ + " cannot create notifications");
+ }
- // rate limit updates that aren't completed progress notifications
- if (mNotificationsByKey.get(r.getSbn().getKey()) != null
- && !r.getNotification().hasCompletedProgress()
- && !isAutogroup) {
+ // rate limit updates that aren't completed progress notifications
+ if (mNotificationsByKey.get(r.getSbn().getKey()) != null
+ && !r.getNotification().hasCompletedProgress()
+ && !isAutogroup) {
- final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
- if (appEnqueueRate > mMaxPackageEnqueueRate) {
- mUsageStats.registerOverRateQuota(pkg);
- final long now = SystemClock.elapsedRealtime();
- if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
- Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
- + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
- mLastOverRateLogTime = now;
- }
- return false;
+ final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
+ if (appEnqueueRate > mMaxPackageEnqueueRate) {
+ mUsageStats.registerOverRateQuota(pkg);
+ final long now = SystemClock.elapsedRealtime();
+ if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
+ Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
+ + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
+ mLastOverRateLogTime = now;
}
+ return false;
}
+ }
- // limit the number of non-fgs outstanding notificationrecords an app can have
- if (!r.getNotification().isForegroundService()) {
- int count = getNotificationCountLocked(pkg, userId, id, tag);
- if (count >= MAX_PACKAGE_NOTIFICATIONS) {
- mUsageStats.registerOverCountQuota(pkg);
- Slog.e(TAG, "Package has already posted or enqueued " + count
- + " notifications. Not showing more. package=" + pkg);
- return false;
- }
+ // limit the number of non-fgs outstanding notificationrecords an app can have
+ if (!n.isForegroundService()) {
+ int count = getNotificationCountLocked(pkg, userId, id, tag);
+ if (count >= MAX_PACKAGE_NOTIFICATIONS) {
+ mUsageStats.registerOverCountQuota(pkg);
+ Slog.e(TAG, "Package has already posted or enqueued " + count
+ + " notifications. Not showing more. package=" + pkg);
+ return false;
}
}
}
- synchronized (mNotificationLock) {
- // snoozed apps
- if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
- MetricsLogger.action(r.getLogMaker()
- .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
- .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
- mNotificationRecordLogger.log(
- NotificationRecordLogger.NotificationEvent.NOTIFICATION_NOT_POSTED_SNOOZED,
- r);
- if (DBG) {
- Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
+ // bubble or inline reply that's immutable?
+ if (n.getBubbleMetadata() != null
+ && n.getBubbleMetadata().getIntent() != null
+ && hasFlag(mAmi.getPendingIntentFlags(
+ n.getBubbleMetadata().getIntent().getTarget()),
+ PendingIntent.FLAG_IMMUTABLE)) {
+ throw new IllegalArgumentException(r.getKey() + " Not posted."
+ + " PendingIntents attached to bubbles must be mutable");
+ }
+
+ if (n.actions != null) {
+ for (Notification.Action action : n.actions) {
+ if ((action.getRemoteInputs() != null || action.getDataOnlyRemoteInputs() != null)
+ && hasFlag(mAmi.getPendingIntentFlags(action.actionIntent.getTarget()),
+ PendingIntent.FLAG_IMMUTABLE)) {
+ throw new IllegalArgumentException(r.getKey() + " Not posted."
+ + " PendingIntents attached to actions with remote"
+ + " inputs must be mutable");
}
- mSnoozeHelper.update(userId, r);
- handleSavePolicyFile();
- return false;
}
+ }
+
+ if (r.getSystemGeneratedSmartActions() != null) {
+ for (Notification.Action action : r.getSystemGeneratedSmartActions()) {
+ if ((action.getRemoteInputs() != null || action.getDataOnlyRemoteInputs() != null)
+ && hasFlag(mAmi.getPendingIntentFlags(action.actionIntent.getTarget()),
+ PendingIntent.FLAG_IMMUTABLE)) {
+ throw new IllegalArgumentException(r.getKey() + " Not posted."
+ + " PendingIntents attached to contextual actions with remote inputs"
+ + " must be mutable");
+ }
+ }
+ }
+
+ // snoozed apps
+ if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
+ MetricsLogger.action(r.getLogMaker()
+ .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
+ .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.NOTIFICATION_NOT_POSTED_SNOOZED,
+ r);
+ if (DBG) {
+ Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
+ }
+ mSnoozeHelper.update(userId, r);
+ handleSavePolicyFile();
+ return false;
+ }
- // blocked apps
- if (isBlocked(r, mUsageStats)) {
- return false;
- }
+ // blocked apps
+ if (isBlocked(r, mUsageStats)) {
+ return false;
}
return true;
@@ -6597,7 +6652,7 @@
// Log event to statsd
mNotificationRecordLogger.maybeLogNotificationPosted(r, old, position,
- buzzBeepBlinkLoggingCode, getGroupInstanceId(n.getGroupKey()));
+ buzzBeepBlinkLoggingCode, getGroupInstanceId(r.getSbn().getGroupKey()));
} finally {
int N = mEnqueuedNotifications.size();
for (int i = 0; i < N; i++) {
@@ -10013,7 +10068,7 @@
* TODO(b/161957908): Remove dogfooder toast.
*/
private class NotificationTrampolineCallback implements BackgroundActivityStartCallback {
- private Set<String> mPackagesShown = new ArraySet<>();
+ private final Set<String> mPackagesShown = new ArraySet<>();
@Override
public IBinder getToken() {
@@ -10021,20 +10076,25 @@
}
@Override
- public void onExclusiveTokenActivityStart(String packageName) {
- Slog.w(TAG, "Indirect notification activity start from " + packageName);
- boolean isFirstOccurrence = mPackagesShown.add(packageName);
- if (!isFirstOccurrence) {
- return;
+ public boolean isActivityStartAllowed(int uid, String packageName) {
+ boolean block = CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid);
+ if (block || mPackagesShown.add(packageName)) {
+ mUiHandler.post(() ->
+ Toast.makeText(getUiContext(),
+ "Indirect activity start from "
+ + packageName + ". "
+ + "This will be blocked in S.\n"
+ + "See go/s-trampolines.",
+ Toast.LENGTH_LONG).show());
}
-
- mUiHandler.post(() ->
- Toast.makeText(getUiContext(),
- "Indirect activity start from "
- + packageName + ". "
- + "This will be blocked in S.\n"
- + "See go/s-trampolines.",
- Toast.LENGTH_LONG).show());
+ String message =
+ "Indirect notification activity start (trampoline) from " + packageName;
+ if (block) {
+ Slog.e(TAG, message + " blocked");
+ return false;
+ }
+ Slog.w(TAG, message + ", this should be avoided for performance reasons");
+ return true;
}
}
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index ad022c7..7992fea 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -777,12 +777,15 @@
void registerApkInApex(AndroidPackage pkg) {
synchronized (mLock) {
for (ActiveApexInfo aai : mActiveApexInfosCache) {
- if (pkg.getBaseApkPath().startsWith(aai.apexDirectory.getAbsolutePath())) {
+ if (pkg.getBaseApkPath().startsWith(
+ aai.apexDirectory.getAbsolutePath() + File.separator)) {
List<String> apks = mApksInApex.get(aai.apexModuleName);
if (apks == null) {
apks = Lists.newArrayList();
mApksInApex.put(aai.apexModuleName, apks);
}
+ Slog.i(TAG, "Registering " + pkg.getPackageName() + " as apk-in-apex of "
+ + aai.apexModuleName);
apks.add(pkg.getPackageName());
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9caa91b..d529f0e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17269,13 +17269,13 @@
private final PackageSetting mPackageSetting;
private final String mPackageName;
private final String mPathString;
- private final int mUserId;
+ private final int mUid;
private final int[] mInstalledUserIds;
IncrementalStatesCallback(PackageSetting packageSetting, int userId) {
mPackageSetting = packageSetting;
mPackageName = packageSetting.name;
- mUserId = userId;
+ mUid = UserHandle.getUid(userId, packageSetting.appId);
mPathString = packageSetting.getPathString();
final int[] allUserIds = resolveUserIds(userId);
final ArrayList<Integer> installedUserIds = new ArrayList<>();
@@ -17311,7 +17311,7 @@
ps, mInstalledUserIds, mSettings.mPackages);
}
Bundle extras = new Bundle();
- extras.putInt(Intent.EXTRA_UID, mUserId);
+ extras.putInt(Intent.EXTRA_UID, mUid);
extras.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_LOADED, mPackageName,
extras, 0 /*flags*/,
@@ -17331,7 +17331,7 @@
ps, mInstalledUserIds, mSettings.mPackages);
}
Bundle extras = new Bundle();
- extras.putInt(Intent.EXTRA_UID, mUserId);
+ extras.putInt(Intent.EXTRA_UID, mUid);
extras.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
extras.putInt(Intent.EXTRA_REASON, reason);
// send broadcast to users with this app installed
@@ -17353,7 +17353,7 @@
ps, mInstalledUserIds, mSettings.mPackages);
}
Bundle extras = new Bundle();
- extras.putInt(Intent.EXTRA_UID, mUserId);
+ extras.putInt(Intent.EXTRA_UID, mUid);
extras.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
// send broadcast to users with this app installed
sendPackageBroadcast(Intent.ACTION_PACKAGE_STARTABLE, mPackageName,
@@ -17756,7 +17756,7 @@
// also includes the "updating the same package" case, of course.
// "updating same package" could also involve key-rotation.
final boolean sigsOk;
- final String sourcePackageName = bp.getSourcePackageName();
+ final String sourcePackageName = bp.getPackageName();
final PackageSetting sourcePackageSetting;
synchronized (mLock) {
sourcePackageSetting = mSettings.getPackageLPr(sourcePackageName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index c356fc7..c46a7ef 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -24,6 +24,7 @@
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import android.accounts.IAccountManager;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.role.IRoleManager;
@@ -2686,9 +2687,18 @@
}
}
+ // pm remove-user [--set-ephemeral-if-in-use] USER_ID
public int runRemoveUser() throws RemoteException {
int userId;
- String arg = getNextArg();
+ String arg;
+ boolean setEphemeralIfInUse = false;
+ while ((arg = getNextOption()) != null) {
+ if (arg.equals("--set-ephemeral-if-in-use")) {
+ setEphemeralIfInUse = true;
+ }
+ }
+
+ arg = getNextArg();
if (arg == null) {
getErrPrintWriter().println("Error: no user id specified.");
return 1;
@@ -2696,6 +2706,15 @@
userId = UserHandle.parseUserArg(arg);
IUserManager um = IUserManager.Stub.asInterface(
ServiceManager.getService(Context.USER_SERVICE));
+ if (setEphemeralIfInUse) {
+ return removeUserOrSetEphemeral(um, userId);
+ } else {
+ return removeUser(um, userId);
+ }
+ }
+
+ private int removeUser(IUserManager um, @UserIdInt int userId) throws RemoteException {
+ Slog.i(TAG, "Removing user " + userId);
if (um.removeUser(userId)) {
getOutPrintWriter().println("Success: removed user");
return 0;
@@ -2705,6 +2724,27 @@
}
}
+ private int removeUserOrSetEphemeral(IUserManager um, @UserIdInt int userId)
+ throws RemoteException {
+ Slog.i(TAG, "Removing " + userId + " or set as ephemeral if in use.");
+ int result = um.removeUserOrSetEphemeral(userId);
+ switch (result) {
+ case UserManagerService.REMOVE_RESULT_REMOVED:
+ getOutPrintWriter().printf("Success: user %d removed\n", userId);
+ return 0;
+ case UserManagerService.REMOVE_RESULT_SET_EPHEMERAL:
+ getOutPrintWriter().printf("Success: user %d set as ephemeral\n", userId);
+ return 0;
+ case UserManagerService.REMOVE_RESULT_ALREADY_BEING_REMOVED:
+ getOutPrintWriter().printf("Success: user %d is already being removed\n", userId);
+ return 0;
+ default:
+ getErrPrintWriter().printf("Error: couldn't remove or mark ephemeral user id %d\n",
+ userId);
+ return 1;
+ }
+ }
+
public int runSetUserRestriction() throws RemoteException {
int userId = UserHandle.USER_SYSTEM;
String opt = getNextOption();
@@ -3769,9 +3809,13 @@
pw.println(" --restricted is shorthand for '--user-type android.os.usertype.full.RESTRICTED'.");
pw.println(" --guest is shorthand for '--user-type android.os.usertype.full.GUEST'.");
pw.println("");
- pw.println(" remove-user USER_ID");
+ pw.println(" remove-user [--set-ephemeral-if-in-use] USER_ID");
pw.println(" Remove the user with the given USER_IDENTIFIER, deleting all data");
- pw.println(" associated with that user");
+ pw.println(" associated with that user.");
+ pw.println(" --set-ephemeral-if-in-use: If the user is currently running and");
+ pw.println(" therefore cannot be removed immediately, mark the user as ephemeral");
+ pw.println(" so that it will be automatically removed when possible (after user");
+ pw.println(" switch or reboot)");
pw.println("");
pw.println(" set-user-restriction [--user USER_ID] RESTRICTION VALUE");
pw.println("");
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2ada613..66e84b1 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -22,6 +22,7 @@
import android.Manifest;
import android.annotation.ColorRes;
import android.annotation.DrawableRes;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
@@ -110,7 +111,6 @@
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import com.android.server.am.UserState;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.utils.TimingsTraceAndSlog;
@@ -132,6 +132,8 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
@@ -246,6 +248,43 @@
static final int WRITE_USER_MSG = 1;
static final int WRITE_USER_DELAY = 2*1000; // 2 seconds
+ /**
+ * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified
+ * user has been successfully removed.
+ */
+ public static final int REMOVE_RESULT_REMOVED = 0;
+
+ /**
+ * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified
+ * user has had its {@link UserInfo#FLAG_EPHEMERAL} flag set to {@code true}, so that it will be
+ * removed when the user is stopped or on boot.
+ */
+ public static final int REMOVE_RESULT_SET_EPHEMERAL = 1;
+
+ /**
+ * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified
+ * user is already in the process of being removed.
+ */
+ public static final int REMOVE_RESULT_ALREADY_BEING_REMOVED = 2;
+
+ /**
+ * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that an error occurred
+ * that prevented the user from being removed or set as ephemeral.
+ */
+ public static final int REMOVE_RESULT_ERROR = 3;
+
+ /**
+ * Possible response codes from {@link #removeUserOrSetEphemeral(int)}.
+ */
+ @IntDef(prefix = { "REMOVE_RESULT_" }, value = {
+ REMOVE_RESULT_REMOVED,
+ REMOVE_RESULT_SET_EPHEMERAL,
+ REMOVE_RESULT_ALREADY_BEING_REMOVED,
+ REMOVE_RESULT_ERROR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RemoveResult {}
+
// Tron counters
private static final String TRON_GUEST_CREATED = "users_guest_created";
private static final String TRON_USER_CREATED = "users_user_created";
@@ -253,9 +292,17 @@
private final Context mContext;
private final PackageManagerService mPm;
+
+ /**
+ * Lock for packages. If using with {@link #mUsersLock}, {@link #mPackagesLock} should be
+ * acquired first.
+ */
private final Object mPackagesLock;
private final UserDataPreparer mUserDataPreparer;
- // Short-term lock for internal state, when interaction/sync with PM is not required
+ /**
+ * Short-term lock for internal state, when interaction/sync with PM is not required. If using
+ * with {@link #mPackagesLock}, {@link #mPackagesLock} should be acquired first.
+ */
private final Object mUsersLock = LockGuard.installNewLock(LockGuard.INDEX_USER);
private final Object mRestrictionsLock = new Object();
// Used for serializing access to app restriction files
@@ -3859,16 +3906,10 @@
*/
@Override
public boolean removeUser(@UserIdInt int userId) {
- Slog.i(LOG_TAG, "removeUser u" + userId);
+ Slog.i(LOG_TAG, "removeUser u" + userId, new Exception());
checkManageOrCreateUsersPermission("Only the system can remove users");
- final boolean isManagedProfile;
- synchronized (mUsersLock) {
- UserInfo userInfo = getUserInfoLU(userId);
- isManagedProfile = userInfo != null && userInfo.isManagedProfile();
- }
- String restriction = isManagedProfile
- ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER;
+ final String restriction = getUserRemovalRestriction(userId);
if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
return false;
@@ -3882,6 +3923,21 @@
return removeUserUnchecked(userId);
}
+ /**
+ * Returns the string name of the restriction to check for user removal. The restriction name
+ * varies depending on whether the user is a managed profile.
+ */
+ private String getUserRemovalRestriction(@UserIdInt int userId) {
+ final boolean isManagedProfile;
+ final UserInfo userInfo;
+ synchronized (mUsersLock) {
+ userInfo = getUserInfoLU(userId);
+ }
+ isManagedProfile = userInfo != null && userInfo.isManagedProfile();
+ return isManagedProfile
+ ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER;
+ }
+
private boolean removeUserUnchecked(@UserIdInt int userId) {
final long ident = Binder.clearCallingIdentity();
try {
@@ -3974,6 +4030,64 @@
}
}
+ @Override
+ public @RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId) {
+ Slog.i(LOG_TAG, "removeUserOrSetEphemeral u" + userId);
+ checkManageUsersPermission("Only the system can remove users");
+ final String restriction = getUserRemovalRestriction(userId);
+ if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
+ Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
+ return REMOVE_RESULT_ERROR;
+ }
+ if (userId == UserHandle.USER_SYSTEM) {
+ Slog.e(LOG_TAG, "System user cannot be removed.");
+ return REMOVE_RESULT_ERROR;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ final UserData userData;
+ synchronized (mPackagesLock) {
+ synchronized (mUsersLock) {
+ userData = mUsers.get(userId);
+ if (userData == null) {
+ Slog.e(LOG_TAG,
+ "Cannot remove user " + userId + ", invalid user id provided.");
+ return REMOVE_RESULT_ERROR;
+ }
+
+ if (mRemovingUserIds.get(userId)) {
+ Slog.e(LOG_TAG, "User " + userId + " is already scheduled for removal.");
+ return REMOVE_RESULT_ALREADY_BEING_REMOVED;
+ }
+ }
+
+ // Attempt to immediately remove a non-current user
+ final int currentUser = ActivityManager.getCurrentUser();
+ if (currentUser != userId) {
+ // Attempt to remove the user. This will fail if the user is the current user
+ if (removeUser(userId)) {
+ return REMOVE_RESULT_REMOVED;
+ }
+
+ Slog.w(LOG_TAG, "Unable to immediately remove non-current user: " + userId
+ + ". User is still set as ephemeral and will be removed on user "
+ + "switch or reboot.");
+ }
+
+ // If the user was not immediately removed, make sure it is marked as ephemeral.
+ // Don't mark as disabled since, per UserInfo.FLAG_DISABLED documentation, an
+ // ephemeral user should only be marked as disabled when its removal is in progress.
+ userData.info.flags |= UserInfo.FLAG_EPHEMERAL;
+ writeUserLP(userData);
+
+ return REMOVE_RESULT_SET_EPHEMERAL;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
void finishRemoveUser(final @UserIdInt int userId) {
if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userId);
@@ -4708,9 +4822,12 @@
final UserInfo user = users.get(i);
final boolean running = am.isUserRunning(user.id, 0);
final boolean current = user.id == currentUser;
+ final boolean hasParent = user.profileGroupId != user.id
+ && user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID;
if (verbose) {
- pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s\n", i, user.id, user.name,
+ pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s%s\n", i, user.id, user.name,
UserInfo.flagsToString(user.flags),
+ hasParent ? " (parentId=" + user.profileGroupId + ")" : "",
running ? " (running)" : "",
user.partial ? " (partial)" : "",
user.preCreated ? " (pre-created)" : "",
@@ -4791,6 +4908,11 @@
pw.print(" "); pw.print(userInfo);
pw.print(" serialNo="); pw.print(userInfo.serialNumber);
pw.print(" isPrimary="); pw.print(userInfo.isPrimary());
+ if (userInfo.profileGroupId != userInfo.id
+ && userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
+ pw.print(" parentId="); pw.print(userInfo.profileGroupId);
+ }
+
if (mRemovingUserIds.get(userId)) {
pw.print(" <removing> ");
}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index c086017..bebb676 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -29,17 +29,17 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
import android.content.pm.parsing.component.ParsedPermission;
+import android.os.Build;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
import com.android.server.pm.DumpState;
import com.android.server.pm.PackageManagerService;
-import com.android.server.pm.PackageSettingBase;
-import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import libcore.util.EmptyArray;
@@ -80,269 +80,282 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ProtectionLevel {}
- final String name;
+ @NonNull
+ private final String mName;
- final @PermissionType int type;
+ private final @PermissionType int mType;
- String sourcePackageName;
+ private String mPackageName;
- int protectionLevel;
+ private int mProtectionLevel;
- ParsedPermission perm;
+ @Nullable
+ private PermissionInfo mPermissionInfo;
- PermissionInfo pendingPermissionInfo;
+ @Nullable
+ private PermissionInfo mPendingPermissionInfo;
/** UID that owns the definition of this permission */
- int uid;
+ private int mUid;
/** Additional GIDs given to apps granted this permission */
@NonNull
- private int[] gids = EmptyArray.INT;
+ private int[] mGids = EmptyArray.INT;
/**
- * Flag indicating that {@link #gids} should be adjusted based on the
+ * Flag indicating that {@link #mGids} should be adjusted based on the
* {@link UserHandle} the granted app is running as.
*/
- private boolean perUser;
+ private boolean mGidsPerUser;
- public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) {
- name = _name;
- sourcePackageName = _sourcePackageName;
- type = _type;
+ public BasePermission(@NonNull String name, String packageName, @PermissionType int type) {
+ mName = name;
+ mPackageName = packageName;
+ mType = type;
// Default to most conservative protection level.
- protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
+ mProtectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
}
@Override
public String toString() {
- return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
+ return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + mName
+ "}";
}
+ @NonNull
public String getName() {
- return name;
+ return mName;
}
+
public int getProtectionLevel() {
- return protectionLevel;
+ return mProtectionLevel;
}
- public String getSourcePackageName() {
- return sourcePackageName;
+
+ public String getPackageName() {
+ return mPackageName;
}
+
public int getType() {
- return type;
+ return mType;
}
+
public int getUid() {
- return uid;
+ return mUid;
}
- public void setGids(@NonNull int[] gids, boolean perUser) {
- this.gids = gids;
- this.perUser = perUser;
+
+ public void setGids(@NonNull int[] gids, boolean gidsPerUser) {
+ mGids = gids;
+ mGidsPerUser = gidsPerUser;
}
- public void setPermission(@Nullable ParsedPermission perm) {
- this.perm = perm;
+
+ public void setPermissionInfo(@Nullable PermissionInfo permissionInfo) {
+ mPermissionInfo = permissionInfo;
}
public boolean hasGids() {
- return gids.length != 0;
+ return mGids.length != 0;
}
@NonNull
public int[] computeGids(int userId) {
- if (perUser) {
- final int[] userGids = new int[gids.length];
- for (int i = 0; i < gids.length; i++) {
- final int gid = gids[i];
+ if (mGidsPerUser) {
+ final int[] userGids = new int[mGids.length];
+ for (int i = 0; i < mGids.length; i++) {
+ final int gid = mGids[i];
userGids[i] = UserHandle.getUid(userId, gid);
}
return userGids;
} else {
- return gids.length != 0 ? gids.clone() : gids;
+ return mGids.length != 0 ? mGids.clone() : mGids;
}
}
public int calculateFootprint(BasePermission perm) {
- if (uid == perm.uid) {
- return perm.name.length() + perm.perm.calculateFootprint();
+ if (mUid == perm.mUid) {
+ return perm.mName.length() + perm.mPermissionInfo.calculateFootprint();
}
return 0;
}
public boolean isPermission(ParsedPermission perm) {
- if (this.perm == null) {
+ if (mPermissionInfo == null) {
return false;
}
- return Objects.equals(this.perm.getPackageName(), perm.getPackageName())
- && Objects.equals(this.perm.getName(), perm.getName());
+ return Objects.equals(mPermissionInfo.packageName, perm.getPackageName())
+ && Objects.equals(mPermissionInfo.name, perm.getName());
}
public boolean isDynamic() {
- return type == TYPE_DYNAMIC;
+ return mType == TYPE_DYNAMIC;
}
-
public boolean isNormal() {
- return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ return (mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_NORMAL;
}
public boolean isRuntime() {
- return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ return (mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS;
}
public boolean isRemoved() {
- return perm != null && (perm.getFlags() & PermissionInfo.FLAG_REMOVED) != 0;
+ return mPermissionInfo != null
+ && (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0;
}
public boolean isSoftRestricted() {
- return perm != null && (perm.getFlags() & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
+ return mPermissionInfo != null
+ && (mPermissionInfo.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
}
public boolean isHardRestricted() {
- return perm != null && (perm.getFlags() & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
+ return mPermissionInfo != null
+ && (mPermissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
}
public boolean isHardOrSoftRestricted() {
- return perm != null && (perm.getFlags() & (PermissionInfo.FLAG_HARD_RESTRICTED
- | PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0;
+ return mPermissionInfo != null && (mPermissionInfo.flags
+ & (PermissionInfo.FLAG_HARD_RESTRICTED | PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0;
}
public boolean isImmutablyRestricted() {
- return perm != null && (perm.getFlags() & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0;
+ return mPermissionInfo != null
+ && (mPermissionInfo.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0;
}
public boolean isInstallerExemptIgnored() {
- return perm != null
- && (perm.getFlags() & PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED) != 0;
+ return mPermissionInfo != null
+ && (mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED) != 0;
}
public boolean isSignature() {
- return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) ==
- PermissionInfo.PROTECTION_SIGNATURE;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_SIGNATURE;
}
public boolean isAppOp() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
}
public boolean isDevelopment() {
return isSignature()
- && (protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
+ && (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0;
}
public boolean isInstaller() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0;
}
public boolean isInstant() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
}
public boolean isOEM() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0;
}
public boolean isPre23() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0;
}
public boolean isPreInstalled() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0;
}
public boolean isPrivileged() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
}
public boolean isRuntimeOnly() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
}
public boolean isSetup() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0;
}
public boolean isVerifier() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0;
}
public boolean isVendorPrivileged() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0;
}
public boolean isSystemTextClassifier() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
!= 0;
}
public boolean isWellbeing() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0;
}
public boolean isDocumenter() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
}
public boolean isConfigurator() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR)
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR)
!= 0;
}
public boolean isIncidentReportApprover() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0;
}
public boolean isAppPredictor() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0;
}
public boolean isCompanion() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0;
}
public boolean isRetailDemo() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0;
+ return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0;
}
public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
- if (!origPackageName.equals(sourcePackageName)) {
+ if (!origPackageName.equals(mPackageName)) {
return;
}
- sourcePackageName = newPackageName;
- perm = null;
- if (pendingPermissionInfo != null) {
- pendingPermissionInfo.packageName = newPackageName;
+ mPackageName = newPackageName;
+ mPermissionInfo = null;
+ if (mPendingPermissionInfo != null) {
+ mPendingPermissionInfo.packageName = newPackageName;
}
- uid = 0;
- gids = EmptyArray.INT;
- perUser = false;
+ mUid = 0;
+ mGids = EmptyArray.INT;
+ mGidsPerUser = false;
}
public boolean addToTree(@ProtectionLevel int protectionLevel,
- @NonNull PermissionInfo info, @NonNull BasePermission tree) {
+ @NonNull PermissionInfo permissionInfo, @NonNull BasePermission tree) {
final boolean changed =
- (this.protectionLevel != protectionLevel
- || perm == null
- || uid != tree.uid
- || !Objects.equals(perm.getPackageName(), tree.perm.getPackageName())
- || !comparePermissionInfos(perm, info));
- this.protectionLevel = protectionLevel;
- info = new PermissionInfo(info);
- info.protectionLevel = protectionLevel;
- perm = new ParsedPermission(tree.perm);
- uid = tree.uid;
+ (mProtectionLevel != protectionLevel
+ || mPermissionInfo == null
+ || mUid != tree.mUid
+ || !Objects.equals(mPermissionInfo.packageName,
+ tree.mPermissionInfo.packageName)
+ || !comparePermissionInfos(mPermissionInfo, permissionInfo));
+ mProtectionLevel = protectionLevel;
+ mPermissionInfo = new PermissionInfo(permissionInfo);
+ mPermissionInfo.protectionLevel = protectionLevel;
+ mPermissionInfo.packageName = tree.mPermissionInfo.packageName;
+ mUid = tree.mUid;
return changed;
}
public void updateDynamicPermission(Collection<BasePermission> permissionTrees) {
if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
- + getName() + " pkg=" + getSourcePackageName()
- + " info=" + pendingPermissionInfo);
- if (pendingPermissionInfo != null) {
- final BasePermission tree = findPermissionTree(permissionTrees, name);
- if (tree != null && tree.perm != null) {
- perm = new ParsedPermission(tree.perm, pendingPermissionInfo,
- tree.perm.getPackageName(), name);
- uid = tree.uid;
+ + getName() + " pkg=" + getPackageName()
+ + " info=" + mPendingPermissionInfo);
+ if (mPendingPermissionInfo != null) {
+ final BasePermission tree = findPermissionTree(permissionTrees, mName);
+ if (tree != null && tree.mPermissionInfo != null) {
+ mPermissionInfo = new PermissionInfo(mPendingPermissionInfo);
+ mPermissionInfo.packageName = tree.mPermissionInfo.packageName;
+ mPermissionInfo.name = mName;
+ mUid = tree.mUid;
}
}
}
static BasePermission createOrUpdate(PackageManagerInternal packageManagerInternal,
- @Nullable BasePermission bp, @NonNull ParsedPermission p,
+ @Nullable BasePermission bp, @NonNull PermissionInfo p,
@NonNull AndroidPackage pkg, Collection<BasePermission> permissionTrees,
boolean chatty) {
- final PackageSettingBase pkgSetting =
- (PackageSettingBase) packageManagerInternal.getPackageSetting(pkg.getPackageName());
// Allow system apps to redefine non-system permissions
- if (bp != null && !Objects.equals(bp.sourcePackageName, p.getPackageName())) {
+ if (bp != null && !Objects.equals(bp.mPackageName, p.packageName)) {
final boolean currentOwnerIsSystem;
- if (bp.perm == null) {
+ if (bp.mPermissionInfo == null) {
currentOwnerIsSystem = false;
} else {
AndroidPackage currentPackage = packageManagerInternal.getPackage(
- bp.perm.getPackageName());
+ bp.mPermissionInfo.packageName);
if (currentPackage == null) {
currentOwnerIsSystem = false;
} else {
@@ -351,52 +364,52 @@
}
if (pkg.isSystem()) {
- if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
+ if (bp.mType == BasePermission.TYPE_BUILTIN && bp.mPermissionInfo == null) {
// It's a built-in permission and no owner, take ownership now
- p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED);
- bp.perm = p;
- bp.uid = pkg.getUid();
- bp.sourcePackageName = p.getPackageName();
+ p.flags |= PermissionInfo.FLAG_INSTALLED;
+ bp.mPermissionInfo = p;
+ bp.mUid = pkg.getUid();
+ bp.mPackageName = p.packageName;
} else if (!currentOwnerIsSystem) {
String msg = "New decl " + pkg + " of permission "
- + p.getName() + " is system; overriding " + bp.sourcePackageName;
+ + p.name + " is system; overriding " + bp.mPackageName;
PackageManagerService.reportSettingsProblem(Log.WARN, msg);
bp = null;
}
}
}
if (bp == null) {
- bp = new BasePermission(p.getName(), p.getPackageName(), TYPE_NORMAL);
+ bp = new BasePermission(p.name, p.packageName, TYPE_NORMAL);
}
StringBuilder r = null;
- if (bp.perm == null) {
- if (bp.sourcePackageName == null
- || bp.sourcePackageName.equals(p.getPackageName())) {
- final BasePermission tree = findPermissionTree(permissionTrees, p.getName());
+ if (bp.mPermissionInfo == null) {
+ if (bp.mPackageName == null
+ || bp.mPackageName.equals(p.packageName)) {
+ final BasePermission tree = findPermissionTree(permissionTrees, p.name);
if (tree == null
- || tree.sourcePackageName.equals(p.getPackageName())) {
- p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED);
- bp.perm = p;
- bp.uid = pkg.getUid();
- bp.sourcePackageName = p.getPackageName();
+ || tree.mPackageName.equals(p.packageName)) {
+ p.flags |= PermissionInfo.FLAG_INSTALLED;
+ bp.mPermissionInfo = p;
+ bp.mUid = pkg.getUid();
+ bp.mPackageName = p.packageName;
if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
- r.append(p.getName());
+ r.append(p.name);
}
} else {
- Slog.w(TAG, "Permission " + p.getName() + " from package "
- + p.getPackageName() + " ignored: base tree "
- + tree.name + " is from package "
- + tree.sourcePackageName);
+ Slog.w(TAG, "Permission " + p.name + " from package "
+ + p.packageName + " ignored: base tree "
+ + tree.mName + " is from package "
+ + tree.mPackageName);
}
} else {
- Slog.w(TAG, "Permission " + p.getName() + " from package "
- + p.getPackageName() + " ignored: original from "
- + bp.sourcePackageName);
+ Slog.w(TAG, "Permission " + p.name + " from package "
+ + p.packageName + " ignored: original from "
+ + bp.mPackageName);
}
} else if (chatty) {
if (r == null) {
@@ -405,11 +418,10 @@
r.append(' ');
}
r.append("DUP:");
- r.append(p.getName());
+ r.append(p.name);
}
- if (bp.perm != null && Objects.equals(bp.perm.getPackageName(), p.getPackageName())
- && Objects.equals(bp.perm.getName(), p.getName())) {
- bp.protectionLevel = p.getProtectionLevel();
+ if (bp.mPermissionInfo == p) {
+ bp.mProtectionLevel = p.protectionLevel;
}
if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
Log.d(TAG, " Permissions: " + r);
@@ -422,12 +434,12 @@
if (permName != null) {
BasePermission bp = findPermissionTree(permissionTrees, permName);
if (bp != null) {
- if (bp.uid == UserHandle.getAppId(callingUid)) {
+ if (bp.mUid == UserHandle.getAppId(callingUid)) {
return bp;
}
throw new SecurityException("Calling uid " + callingUid
+ " is not allowed to add to permission tree "
- + bp.name + " owned by uid " + bp.uid);
+ + bp.mName + " owned by uid " + bp.mUid);
}
}
throw new SecurityException("No permission tree found for " + permName);
@@ -436,45 +448,63 @@
private static BasePermission findPermissionTree(
Collection<BasePermission> permissionTrees, String permName) {
for (BasePermission bp : permissionTrees) {
- if (permName.startsWith(bp.name) &&
- permName.length() > bp.name.length() &&
- permName.charAt(bp.name.length()) == '.') {
+ if (permName.startsWith(bp.mName)
+ && permName.length() > bp.mName.length()
+ && permName.charAt(bp.mName.length()) == '.') {
return bp;
}
}
return null;
}
- public @Nullable PermissionInfo generatePermissionInfo(@NonNull String groupName, int flags) {
- if (groupName == null) {
- if (perm == null || perm.getGroup() == null) {
- return generatePermissionInfo(protectionLevel, flags);
- }
- } else {
- if (perm != null && groupName.equals(perm.getGroup())) {
- return PackageInfoUtils.generatePermissionInfo(perm, flags);
- }
- }
- return null;
+ @Nullable
+ public String getBackgroundPermission() {
+ return mPermissionInfo != null ? mPermissionInfo.backgroundPermission : null;
}
- public @NonNull PermissionInfo generatePermissionInfo(int adjustedProtectionLevel, int flags) {
+ @Nullable
+ public String getGroup() {
+ return mPermissionInfo != null ? mPermissionInfo.group : null;
+ }
+
+ public int getProtection() {
+ return mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+ }
+
+ public int getProtectionFlags() {
+ return mProtectionLevel & PermissionInfo.PROTECTION_MASK_FLAGS;
+ }
+
+ @NonNull
+ public PermissionInfo generatePermissionInfo(int flags) {
+ return generatePermissionInfo(flags, Build.VERSION_CODES.CUR_DEVELOPMENT);
+ }
+
+ @NonNull
+ public PermissionInfo generatePermissionInfo(int flags, int targetSdkVersion) {
PermissionInfo permissionInfo;
- if (perm != null) {
- final boolean protectionLevelChanged = protectionLevel != adjustedProtectionLevel;
- permissionInfo = PackageInfoUtils.generatePermissionInfo(perm, flags);
- if (protectionLevelChanged) {
- // if we return different protection level, don't use the cached info
- permissionInfo = new PermissionInfo(permissionInfo);
- permissionInfo.protectionLevel = adjustedProtectionLevel;
+ if (mPermissionInfo != null) {
+ permissionInfo = new PermissionInfo(mPermissionInfo);
+ if ((flags & PackageManager.GET_META_DATA) != PackageManager.GET_META_DATA) {
+ permissionInfo.metaData = null;
}
- return permissionInfo;
+ } else {
+ permissionInfo = new PermissionInfo();
+ permissionInfo.name = mName;
+ permissionInfo.packageName = mPackageName;
+ permissionInfo.nonLocalizedLabel = mName;
}
- permissionInfo = new PermissionInfo();
- permissionInfo.name = name;
- permissionInfo.packageName = sourcePackageName;
- permissionInfo.nonLocalizedLabel = name;
- permissionInfo.protectionLevel = protectionLevel;
+ if (targetSdkVersion >= Build.VERSION_CODES.O) {
+ permissionInfo.protectionLevel = mProtectionLevel;
+ } else {
+ final int protection = mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+ if (protection == PermissionInfo.PROTECTION_SIGNATURE) {
+ // Signature permission's protection flags are always reported.
+ permissionInfo.protectionLevel = mProtectionLevel;
+ } else {
+ permissionInfo.protectionLevel = protection;
+ }
+ }
return permissionInfo;
}
@@ -496,23 +526,23 @@
final boolean dynamic = "dynamic".equals(ptype);
BasePermission bp = out.get(name);
// If the permission is builtin, do not clobber it.
- if (bp == null || bp.type != TYPE_BUILTIN) {
+ if (bp == null || bp.mType != TYPE_BUILTIN) {
bp = new BasePermission(name.intern(), sourcePackage,
dynamic ? TYPE_DYNAMIC : TYPE_NORMAL);
}
- bp.protectionLevel = readInt(parser, null, "protection",
+ bp.mProtectionLevel = readInt(parser, null, "protection",
PermissionInfo.PROTECTION_NORMAL);
- bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
+ bp.mProtectionLevel = PermissionInfo.fixProtectionLevel(bp.mProtectionLevel);
if (dynamic) {
final PermissionInfo pi = new PermissionInfo();
pi.packageName = sourcePackage.intern();
pi.name = name.intern();
pi.icon = readInt(parser, null, "icon", 0);
pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
- pi.protectionLevel = bp.protectionLevel;
- bp.pendingPermissionInfo = pi;
+ pi.protectionLevel = bp.mProtectionLevel;
+ bp.mPendingPermissionInfo = pi;
}
- out.put(bp.name, bp);
+ out.put(bp.mName, bp);
return true;
}
@@ -533,22 +563,23 @@
}
public void writeLPr(@NonNull XmlSerializer serializer) throws IOException {
- if (sourcePackageName == null) {
+ if (mPackageName == null) {
return;
}
serializer.startTag(null, TAG_ITEM);
- serializer.attribute(null, ATTR_NAME, name);
- serializer.attribute(null, ATTR_PACKAGE, sourcePackageName);
- if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
- serializer.attribute(null, "protection", Integer.toString(protectionLevel));
+ serializer.attribute(null, ATTR_NAME, mName);
+ serializer.attribute(null, ATTR_PACKAGE, mPackageName);
+ if (mProtectionLevel != PermissionInfo.PROTECTION_NORMAL) {
+ serializer.attribute(null, "protection", Integer.toString(mProtectionLevel));
}
- if (type == BasePermission.TYPE_DYNAMIC) {
- if (perm != null || pendingPermissionInfo != null) {
+ if (mType == BasePermission.TYPE_DYNAMIC) {
+ if (mPermissionInfo != null || mPendingPermissionInfo != null) {
serializer.attribute(null, "type", "dynamic");
- int icon = perm != null ? perm.getIcon() : pendingPermissionInfo.icon;
- CharSequence nonLocalizedLabel = perm != null
- ? perm.getNonLocalizedLabel()
- : pendingPermissionInfo.nonLocalizedLabel;
+ int icon = mPermissionInfo != null ? mPermissionInfo.icon
+ : mPendingPermissionInfo.icon;
+ CharSequence nonLocalizedLabel = mPermissionInfo != null
+ ? mPermissionInfo.nonLocalizedLabel
+ : mPendingPermissionInfo.nonLocalizedLabel;
if (icon != 0) {
serializer.attribute(null, "icon", Integer.toString(icon));
@@ -561,27 +592,14 @@
serializer.endTag(null, TAG_ITEM);
}
- private static boolean compareStrings(CharSequence s1, CharSequence s2) {
- if (s1 == null) {
- return s2 == null;
- }
- if (s2 == null) {
- return false;
- }
- if (s1.getClass() != s2.getClass()) {
- return false;
- }
- return s1.equals(s2);
- }
-
- private static boolean comparePermissionInfos(ParsedPermission pi1, PermissionInfo pi2) {
- if (pi1.getIcon() != pi2.icon) return false;
- if (pi1.getLogo() != pi2.logo) return false;
- if (pi1.getProtectionLevel() != pi2.protectionLevel) return false;
- if (!compareStrings(pi1.getName(), pi2.name)) return false;
- if (!compareStrings(pi1.getNonLocalizedLabel(), pi2.nonLocalizedLabel)) return false;
+ private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) {
+ if (pi1.icon != pi2.icon) return false;
+ if (pi1.logo != pi2.logo) return false;
+ if (pi1.protectionLevel != pi2.protectionLevel) return false;
+ if (!Objects.equals(pi1.name, pi2.name)) return false;
+ if (!Objects.equals(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
// We'll take care of setting this one.
- if (!compareStrings(pi1.getPackageName(), pi2.packageName)) return false;
+ if (!Objects.equals(pi1.packageName, pi2.packageName)) return false;
// These are not currently stored in settings.
//if (!compareStrings(pi1.group, pi2.group)) return false;
//if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
@@ -593,36 +611,34 @@
public boolean dumpPermissionsLPr(@NonNull PrintWriter pw, @NonNull String packageName,
@NonNull Set<String> permissionNames, boolean readEnforced,
boolean printedSomething, @NonNull DumpState dumpState) {
- if (packageName != null && !packageName.equals(sourcePackageName)) {
+ if (packageName != null && !packageName.equals(mPackageName)) {
return false;
}
- if (permissionNames != null && !permissionNames.contains(name)) {
+ if (permissionNames != null && !permissionNames.contains(mName)) {
return false;
}
if (!printedSomething) {
if (dumpState.onTitlePrinted())
pw.println();
pw.println("Permissions:");
- printedSomething = true;
}
- pw.print(" Permission ["); pw.print(name); pw.print("] (");
+ pw.print(" Permission ["); pw.print(mName); pw.print("] (");
pw.print(Integer.toHexString(System.identityHashCode(this)));
pw.println("):");
- pw.print(" sourcePackage="); pw.println(sourcePackageName);
- pw.print(" uid="); pw.print(uid);
- pw.print(" gids="); pw.print(Arrays.toString(
- computeGids(UserHandle.USER_SYSTEM)));
- pw.print(" type="); pw.print(type);
- pw.print(" prot=");
- pw.println(PermissionInfo.protectionToString(protectionLevel));
- if (perm != null) {
- pw.print(" perm="); pw.println(perm);
- if ((perm.getFlags() & PermissionInfo.FLAG_INSTALLED) == 0
- || (perm.getFlags() & PermissionInfo.FLAG_REMOVED) != 0) {
- pw.print(" flags=0x"); pw.println(Integer.toHexString(perm.getFlags()));
+ pw.print(" sourcePackage="); pw.println(mPackageName);
+ pw.print(" uid="); pw.print(mUid);
+ pw.print(" gids="); pw.print(Arrays.toString(computeGids(UserHandle.USER_SYSTEM)));
+ pw.print(" type="); pw.print(mType);
+ pw.print(" prot=");
+ pw.println(PermissionInfo.protectionToString(mProtectionLevel));
+ if (mPermissionInfo != null) {
+ pw.print(" perm="); pw.println(mPermissionInfo);
+ if ((mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0
+ || (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) {
+ pw.print(" flags=0x"); pw.println(Integer.toHexString(mPermissionInfo.flags));
}
}
- if (READ_EXTERNAL_STORAGE.equals(name)) {
+ if (READ_EXTERNAL_STORAGE.equals(mName)) {
pw.print(" enforced=");
pw.println(readEnforced);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 25e1848..19a5650 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -378,8 +378,9 @@
@NonNull Injector injector) {
mInjector = injector;
// The package info cache is the cache for package and permission information.
+ // Disable the package info and package permission caches locally but leave the
+ // checkPermission cache active.
mInjector.invalidatePackageInfoCache();
- mInjector.disablePermissionCache();
mInjector.disablePackageNamePermissionCache();
mContext = context;
@@ -544,24 +545,37 @@
@Override
@Nullable
- public PermissionInfo getPermissionInfo(String permName, String packageName,
+ public PermissionInfo getPermissionInfo(@NonNull String permName, @NonNull String opPackageName,
@PermissionInfoFlags int flags) {
final int callingUid = getCallingUid();
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
return null;
}
- final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
+ final AndroidPackage opPackage = mPackageManagerInt.getPackage(opPackageName);
+ final int targetSdkVersion = getPermissionInfoCallingTargetSdkVersion(opPackage,
+ callingUid);
synchronized (mLock) {
final BasePermission bp = mSettings.getPermissionLocked(permName);
if (bp == null) {
return null;
}
- final int adjustedProtectionLevel = adjustPermissionProtectionFlagsLocked(
- bp.getProtectionLevel(), pkg, callingUid);
- return bp.generatePermissionInfo(adjustedProtectionLevel, flags);
+ return bp.generatePermissionInfo(flags, targetSdkVersion);
}
}
+ private int getPermissionInfoCallingTargetSdkVersion(@Nullable AndroidPackage pkg, int uid) {
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID
+ || appId == Process.SHELL_UID) {
+ // System sees all flags.
+ return Build.VERSION_CODES.CUR_DEVELOPMENT;
+ }
+ if (pkg == null) {
+ return Build.VERSION_CODES.CUR_DEVELOPMENT;
+ }
+ return pkg.getTargetSdkVersion();
+ }
+
@Override
@Nullable
public ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String groupName,
@@ -576,9 +590,8 @@
}
final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
for (BasePermission bp : mSettings.mPermissions.values()) {
- final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags);
- if (pi != null) {
- out.add(pi);
+ if (Objects.equals(bp.getGroup(), groupName)) {
+ out.add(bp.generatePermissionInfo(flags));
}
}
return new ParceledListSlice<>(out);
@@ -603,7 +616,7 @@
int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
if (added) {
enforcePermissionCapLocked(info, tree);
- bp = new BasePermission(info.name, tree.getSourcePackageName(),
+ bp = new BasePermission(info.name, tree.getPackageName(),
BasePermission.TYPE_DYNAMIC);
} else if (!bp.isDynamic()) {
throw new SecurityException("Not allowed to modify non-dynamic permission "
@@ -2235,32 +2248,6 @@
}
}
- private int adjustPermissionProtectionFlagsLocked(int protectionLevel,
- @Nullable AndroidPackage pkg, int uid) {
- // Signature permission flags area always reported
- final int protectionLevelMasked = protectionLevel
- & (PermissionInfo.PROTECTION_NORMAL
- | PermissionInfo.PROTECTION_DANGEROUS
- | PermissionInfo.PROTECTION_SIGNATURE);
- if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) {
- return protectionLevel;
- }
- // System sees all flags.
- final int appId = UserHandle.getAppId(uid);
- if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID
- || appId == Process.SHELL_UID) {
- return protectionLevel;
- }
- if (pkg == null) {
- return protectionLevel;
- }
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
- return protectionLevelMasked;
- }
- // Apps that target O see flags for all protection levels.
- return protectionLevel;
- }
-
/**
* We might auto-grant permissions if any permission of the group is already granted. Hence if
* the group of a granted permission changes we need to revoke it to avoid having permissions of
@@ -2363,17 +2350,19 @@
}
}
+ final PermissionInfo permissionInfo = PackageInfoUtils.generatePermissionInfo(p,
+ PackageManager.GET_META_DATA);
if (p.isTree()) {
final BasePermission bp = BasePermission.createOrUpdate(
mPackageManagerInt,
- mSettings.getPermissionTreeLocked(p.getName()), p, pkg,
+ mSettings.getPermissionTreeLocked(p.getName()), permissionInfo, pkg,
mSettings.getAllPermissionTreesLocked(), chatty);
mSettings.putPermissionTreeLocked(p.getName(), bp);
} else {
final BasePermission bp = BasePermission.createOrUpdate(
mPackageManagerInt,
mSettings.getPermissionLocked(p.getName()),
- p, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
+ permissionInfo, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
mSettings.putPermissionLocked(p.getName(), bp);
}
}
@@ -2433,7 +2422,7 @@
bp = mSettings.mPermissionTrees.get(p.getName());
}
if (bp != null && bp.isPermission(p)) {
- bp.setPermission(null);
+ bp.setPermissionInfo(null);
if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
@@ -2624,7 +2613,7 @@
if (permission == null) {
continue;
}
- if (Objects.equals(permission.getSourcePackageName(), PLATFORM_PACKAGE_NAME)
+ if (Objects.equals(permission.getPackageName(), PLATFORM_PACKAGE_NAME)
&& permission.isRuntime() && !permission.isRemoved()) {
if (permission.isHardOrSoftRestricted()
|| permission.isImmutablyRestricted()) {
@@ -2817,10 +2806,10 @@
boolean wasChanged = false;
boolean restrictionExempt =
- (origState.getPermissionFlags(bp.name)
+ (origState.getPermissionFlags(bp.getName())
& FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
boolean restrictionApplied = (origState.getPermissionFlags(
- bp.name) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+ bp.getName()) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
if (appSupportsRuntimePermissions) {
// If hard restricted we don't allow holding it
@@ -2868,7 +2857,7 @@
if (origPermState == null) {
// New permission
if (PLATFORM_PACKAGE_NAME.equals(
- bp.getSourcePackageName())) {
+ bp.getPackageName())) {
if (!bp.isRemoved()) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED
| FLAG_PERMISSION_REVOKED_COMPAT;
@@ -2877,7 +2866,7 @@
}
}
- if (!uidState.isPermissionGranted(bp.name)
+ if (!uidState.isPermissionGranted(bp.getName())
&& uidState.grantPermission(bp)) {
wasChanged = true;
}
@@ -2914,7 +2903,7 @@
flags);
} else {
if (DEBUG_PERMISSIONS) {
- boolean wasGranted = uidState.isPermissionGranted(bp.name);
+ boolean wasGranted = uidState.isPermissionGranted(bp.getName());
if (wasGranted || bp.isAppOp()) {
Slog.i(TAG, (wasGranted ? "Un-granting" : "Not granting")
+ " permission " + perm
@@ -2926,7 +2915,7 @@
+ ")");
}
}
- if (uidState.removePermissionState(bp.name)) {
+ if (uidState.removePermissionState(bp.getName())) {
changedInstallPermission = true;
}
}
@@ -3309,7 +3298,7 @@
final boolean isPrivilegedPermission = bp.isPrivileged() || isVendorPrivilegedPermission;
final boolean isOemPermission = bp.isOEM();
if (!allowed && (isPrivilegedPermission || isOemPermission) && pkg.isSystem()) {
- final String permissionName = bp.name;
+ final String permissionName = bp.getName();
// For updated system applications, a privileged/oem permission
// is granted only if it had been defined by the original application.
if (pkgSetting.getPkgState().isUpdatedSystemApp()) {
@@ -3457,7 +3446,7 @@
@Nullable
private PackageSetting getSourcePackageSetting(@NonNull BasePermission bp) {
- final String sourcePackageName = bp.getSourcePackageName();
+ final String sourcePackageName = bp.getPackageName();
return mPackageManagerInt.getPackageSetting(sourcePackageName);
}
@@ -3467,14 +3456,14 @@
return false;
}
final boolean isPlatformPermission = PLATFORM_PACKAGE_NAME.equals(
- permission.getSourcePackageName());
+ permission.getPackageName());
if (!isPlatformPermission) {
return true;
}
if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) {
return true;
}
- final String permissionName = permission.name;
+ final String permissionName = permission.getName();
if (isInSystemConfigPrivAppPermissions(pkg, permissionName)) {
return true;
}
@@ -3861,7 +3850,7 @@
// TODO(zhanghai): Why are we only killing the UID when GIDs changed, instead of any
// permission change?
- if (uidState.removePermissionState(bp.name) && bp.hasGids()) {
+ if (uidState.removePermissionState(bp.getName()) && bp.hasGids()) {
affectedUserId = userId;
}
}
@@ -3902,7 +3891,8 @@
if (!usedPermissions.contains(permissionState.getName())) {
BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
if (bp != null) {
- if (uidState.removePermissionState(bp.name) && permissionState.isRuntime()) {
+ if (uidState.removePermissionState(bp.getName())
+ && permissionState.isRuntime()) {
runtimePermissionChanged = true;
}
}
@@ -3973,9 +3963,9 @@
// Only system declares background permissions, hence mapping does never change.
mBackgroundPermissions = new ArrayMap<>();
for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
- if (bp.perm != null && bp.perm.getBackgroundPermission() != null) {
- String fgPerm = bp.name;
- String bgPerm = bp.perm.getBackgroundPermission();
+ if (bp.getBackgroundPermission() != null) {
+ String fgPerm = bp.getName();
+ String bgPerm = bp.getBackgroundPermission();
List<String> fgPerms = mBackgroundPermissions.get(bgPerm);
if (fgPerms == null) {
@@ -4124,7 +4114,7 @@
if (bp.isDynamic()) {
bp.updateDynamicPermission(mSettings.mPermissionTrees.values());
}
- if (!packageName.equals(bp.getSourcePackageName())) {
+ if (!packageName.equals(bp.getPackageName())) {
// Not checking sourcePackageSetting because it can be null when
// the permission source package is the target package and the target package is
// being uninstalled,
@@ -4145,7 +4135,7 @@
// From all other packages
if (pkg == null || !hasPermission(pkg, bp.getName())) {
Slog.i(TAG, "Removing permission " + bp.getName()
- + " that used to be declared by " + bp.getSourcePackageName());
+ + " that used to be declared by " + bp.getPackageName());
if (bp.isRuntime()) {
final int[] userIds = mUserManagerInt.getUserIds();
final int numUserIds = userIds.length;
@@ -4167,7 +4157,7 @@
+ p.getPackageName() + " and user " + userId);
continue;
}
- uidState.removePermissionState(bp.name);
+ uidState.removePermissionState(bp.getName());
}
}
});
@@ -4176,16 +4166,16 @@
continue;
}
final AndroidPackage sourcePkg =
- mPackageManagerInt.getPackage(bp.getSourcePackageName());
+ mPackageManagerInt.getPackage(bp.getPackageName());
final PackageSetting sourcePs =
(PackageSetting) mPackageManagerInt.getPackageSetting(
- bp.getSourcePackageName());
+ bp.getPackageName());
synchronized (mLock) {
if (sourcePkg != null && sourcePs != null) {
continue;
}
Slog.w(TAG, "Removing dangling permission: " + bp.getName()
- + " from package " + bp.getSourcePackageName());
+ + " from package " + bp.getPackageName());
mSettings.removePermissionLocked(bp.getName());
}
}
@@ -4257,7 +4247,7 @@
final Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
while (it.hasNext()) {
final BasePermission bp = it.next();
- if (!packageName.equals(bp.getSourcePackageName())) {
+ if (!packageName.equals(bp.getPackageName())) {
// Not checking sourcePackageSetting because it can be null when
// the permission source package is the target package and the target package is
// being uninstalled,
@@ -4268,7 +4258,7 @@
changed = true;
if (pkg == null || !hasPermission(pkg, bp.getName())) {
Slog.i(TAG, "Removing permission tree " + bp.getName()
- + " that used to be declared by " + bp.getSourcePackageName());
+ + " that used to be declared by " + bp.getPackageName());
it.remove();
}
if (needsUpdate == null) {
@@ -4280,16 +4270,16 @@
if (needsUpdate != null) {
for (final BasePermission bp : needsUpdate) {
final AndroidPackage sourcePkg =
- mPackageManagerInt.getPackage(bp.getSourcePackageName());
+ mPackageManagerInt.getPackage(bp.getPackageName());
final PackageSetting sourcePs =
(PackageSetting) mPackageManagerInt.getPackageSetting(
- bp.getSourcePackageName());
+ bp.getPackageName());
synchronized (mLock) {
if (sourcePkg != null && sourcePs != null) {
continue;
}
Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
- + " from package " + bp.getSourcePackageName());
+ + " from package " + bp.getPackageName());
mSettings.removePermissionLocked(bp.getName());
}
}
@@ -4902,9 +4892,8 @@
for (int i = 0; i < numTotalPermissions; i++) {
BasePermission bp = mSettings.mPermissions.valueAt(i);
- if (bp.perm != null && bp.perm.getProtection() == protection) {
- matchingPermissions.add(
- PackageInfoUtils.generatePermissionInfo(bp.perm, 0));
+ if (bp.getProtection() == protection) {
+ matchingPermissions.add(bp.generatePermissionInfo(0));
}
}
}
@@ -4923,10 +4912,8 @@
for (int i = 0; i < numTotalPermissions; i++) {
BasePermission bp = mSettings.mPermissions.valueAt(i);
- if (bp.perm != null && (bp.perm.getProtectionFlags() & protectionFlags)
- == protectionFlags) {
- matchingPermissions.add(
- PackageInfoUtils.generatePermissionInfo(bp.perm, 0));
+ if ((bp.getProtectionFlags() & protectionFlags) == protectionFlags) {
+ matchingPermissions.add(bp.generatePermissionInfo(0));
}
}
}
diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
index 38e8955..9727a54 100644
--- a/services/core/java/com/android/server/pm/permission/UidPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
@@ -284,7 +284,7 @@
final PermissionState permissionState = getOrCreatePermissionState(permission);
final boolean changed = permissionState.updateFlags(flagMask, flagValues);
if (changed && permissionState.isDefault()) {
- removePermissionState(permission.name);
+ removePermissionState(permission.getName());
}
return changed;
}
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index 0db3d78..0ca6e59 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -80,6 +80,8 @@
}
void setDeviceFolded(boolean folded) {
+ mDisplayManagerInternal.setDeviceFolded(folded);
+
if (mFolded != null && mFolded == folded) {
return;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index df283e2..b74de13 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -117,6 +117,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.hdmi.HdmiAudioSystemClient;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
@@ -366,6 +367,7 @@
StatusBarManagerInternal mStatusBarManagerInternal;
AudioManagerInternal mAudioManagerInternal;
DisplayManager mDisplayManager;
+ DisplayManagerInternal mDisplayManagerInternal;
boolean mPreloadedRecentApps;
final Object mServiceAquireLock = new Object();
Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
@@ -469,6 +471,7 @@
int mLidKeyboardAccessibility;
int mLidNavigationAccessibility;
private boolean mLidControlsDisplayFold;
+ private boolean mShouldSwapDisplaysOnLidSwitch;
int mShortPressOnPowerBehavior;
int mLongPressOnPowerBehavior;
int mVeryLongPressOnPowerBehavior;
@@ -1752,6 +1755,7 @@
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mPackageManager = mContext.getPackageManager();
mHasFeatureWatch = mPackageManager.hasSystemFeature(FEATURE_WATCH);
mHasFeatureLeanback = mPackageManager.hasSystemFeature(FEATURE_LEANBACK);
@@ -1845,6 +1849,8 @@
com.android.internal.R.integer.config_lidNavigationAccessibility);
mLidControlsDisplayFold = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_lidControlsDisplayFold);
+ mShouldSwapDisplaysOnLidSwitch = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_internalFoldedPhysicalDisplayIds).length == 2;
mAllowTheaterModeWakeFromKey = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromKey);
@@ -4506,7 +4512,11 @@
// Called on the DisplayManager's DisplayPowerController thread.
@Override
- public void screenTurnedOff() {
+ public void screenTurnedOff(int displayId) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+
if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off...");
updateScreenOffSleepToken(true);
@@ -4529,7 +4539,11 @@
// Called on the DisplayManager's DisplayPowerController thread.
@Override
- public void screenTurningOn(final ScreenOnListener screenOnListener) {
+ public void screenTurningOn(int displayId, final ScreenOnListener screenOnListener) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+
if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on...");
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", 0 /* cookie */);
@@ -4552,7 +4566,11 @@
// Called on the DisplayManager's DisplayPowerController thread.
@Override
- public void screenTurnedOn() {
+ public void screenTurnedOn(int displayId) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+
synchronized (mLock) {
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onScreenTurnedOn();
@@ -4562,7 +4580,11 @@
}
@Override
- public void screenTurningOff(ScreenOffListener screenOffListener) {
+ public void screenTurningOff(int displayId, ScreenOffListener screenOffListener) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+
mWindowManagerFuncs.screenTurningOff(screenOffListener);
synchronized (mLock) {
if (mKeyguardDelegate != null) {
@@ -4824,8 +4846,8 @@
}
startedWakingUp(ON_BECAUSE_OF_UNKNOWN);
finishedWakingUp(ON_BECAUSE_OF_UNKNOWN);
- screenTurningOn(null);
- screenTurnedOn();
+ screenTurningOn(DEFAULT_DISPLAY, null);
+ screenTurnedOn(DEFAULT_DISPLAY);
}
@Override
@@ -5019,6 +5041,8 @@
final int lidState = mDefaultDisplayPolicy.getLidState();
if (mLidControlsDisplayFold && mDisplayFoldController != null) {
mDisplayFoldController.requestDeviceFolded(lidState == LID_CLOSED);
+ } else if (mShouldSwapDisplaysOnLidSwitch) {
+ mDisplayManagerInternal.setDeviceFolded(lidState == LID_CLOSED);
} else if (lidState == LID_CLOSED) {
int lidBehavior = getLidBehavior();
switch (lidBehavior) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index b96d65c..0d8d347 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -831,7 +831,7 @@
public void finishedGoingToSleep(int why);
/**
- * Called when the device is about to turn on the screen to show content.
+ * Called when the display is about to turn on to show content.
* When waking up, this method will be called once after the call to wakingUp().
* When dozing, the method will be called sometime after the call to goingToSleep() and
* may be called repeatedly in the case where the screen is pulsing on and off.
@@ -839,13 +839,13 @@
* Must call back on the listener to tell it when the higher-level system
* is ready for the screen to go on (i.e. the lock screen is shown).
*/
- public void screenTurningOn(ScreenOnListener screenOnListener);
+ public void screenTurningOn(int displayId, ScreenOnListener screenOnListener);
/**
- * Called when the device has actually turned on the screen, i.e. the display power state has
- * been set to ON and the screen is unblocked.
+ * Called when the display has actually turned on, i.e. the display power state has been set to
+ * ON and the screen is unblocked.
*/
- public void screenTurnedOn();
+ public void screenTurnedOn(int displayId);
/**
* Called when the display would like to be turned off. This gives policy a chance to do some
@@ -854,12 +854,12 @@
* @param screenOffListener Must be called to tell that the display power state can actually be
* changed now after policy has done its work.
*/
- public void screenTurningOff(ScreenOffListener screenOffListener);
+ public void screenTurningOff(int displayId, ScreenOffListener screenOffListener);
/**
- * Called when the device has turned the screen off.
+ * Called when the display has turned off.
*/
- public void screenTurnedOff();
+ public void screenTurnedOff(int displayId);
public interface ScreenOnListener {
void onScreenOn();
diff --git a/services/core/java/com/android/server/power/batterysaver/TEST_MAPPING b/services/core/java/com/android/server/power/batterysaver/TEST_MAPPING
new file mode 100644
index 0000000..17dba7d
--- /dev/null
+++ b/services/core/java/com/android/server/power/batterysaver/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsLocationCoarseTestCases"
+ },
+ {
+ "name": "CtsLocationFineTestCases"
+ },
+ {
+ "name": "CtsLocationNoneTestCases"
+ },
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.location"}
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index a4fa745..44a6336 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -25,6 +25,7 @@
import android.content.rollback.PackageRollbackInfo.RestoreInfo;
import android.content.rollback.RollbackInfo;
import android.os.UserHandle;
+import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -37,6 +38,7 @@
import org.json.JSONObject;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
@@ -66,8 +68,6 @@
//
// * XXX, YYY are the rollbackIds for the corresponding rollbacks.
// * rollback.json contains all relevant metadata for the rollback.
- //
- // TODO: Use AtomicFile for all the .json files?
private final File mRollbackDataDir;
RollbackStore(File rollbackDataDir) {
@@ -259,6 +259,8 @@
* Saves the given rollback to persistent storage.
*/
static void saveRollback(Rollback rollback) {
+ FileOutputStream fos = null;
+ AtomicFile file = new AtomicFile(new File(rollback.getBackupDir(), "rollback.json"));
try {
JSONObject dataJson = new JSONObject();
dataJson.put("info", rollbackInfoToJson(rollback.info));
@@ -272,11 +274,16 @@
dataJson.putOpt(
"extensionVersions", extensionVersionsToJson(rollback.getExtensionVersions()));
- PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json"));
+ fos = file.startWrite();
+ PrintWriter pw = new PrintWriter(fos);
pw.println(dataJson.toString());
pw.close();
+ file.finishWrite(fos);
} catch (JSONException | IOException e) {
Slog.e(TAG, "Unable to save rollback for: " + rollback.info.getRollbackId(), e);
+ if (fos != null) {
+ file.failWrite(fos);
+ }
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
index 7b6c656..51b00fa 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -236,7 +236,8 @@
@Override
public void unloadModel(int modelHandle) throws RemoteException {
- enforcePermissions();
+ // Unloading a model does not require special permissions. Having a handle to the
+ // session is sufficient.
mDelegate.unloadModel(modelHandle);
}
@@ -250,7 +251,8 @@
@Override
public void stopRecognition(int modelHandle) throws RemoteException {
- enforcePermissions();
+ // Stopping a model does not require special permissions. Having a handle to the
+ // session is sufficient.
mDelegate.stopRecognition(modelHandle);
}
@@ -284,7 +286,8 @@
@Override
public void detach() throws RemoteException {
- enforcePermissions();
+ // Detaching does not require special permissions. Having a handle to the session is
+ // sufficient.
mDelegate.detach();
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index e0b671f..f43a4ce 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1630,13 +1630,14 @@
if (modemInfo == null) {
return StatsManager.PULL_SKIP;
}
- pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, modemInfo.getTimestamp(),
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ modemInfo.getTimestampMillis(),
modemInfo.getSleepTimeMillis(), modemInfo.getIdleTimeMillis(),
- modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis(),
- modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis(),
- modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis(),
- modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis(),
- modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis(),
+ modemInfo.getTransmitDurationMillisAtPowerLevel(0),
+ modemInfo.getTransmitDurationMillisAtPowerLevel(1),
+ modemInfo.getTransmitDurationMillisAtPowerLevel(2),
+ modemInfo.getTransmitDurationMillisAtPowerLevel(3),
+ modemInfo.getTransmitDurationMillisAtPowerLevel(4),
modemInfo.getReceiveTimeMillis(),
-1 /*`energy_used` field name deprecated, use -1 to indicate as unused.*/));
} finally {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index e9215f9..ebfffec 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -86,6 +86,13 @@
void toggleSplitScreen();
void appTransitionFinished(int displayId);
+ /**
+ * Notifies the status bar that a Emergency Action launch gesture has been detected.
+ *
+ * TODO (b/169175022) Update method name and docs when feature name is locked.
+ */
+ void onEmergencyActionLaunchGestureDetected();
+
void toggleRecentApps();
void setCurrentUser(int newUserId);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 6a68dc1..55cb7f3 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -264,6 +264,23 @@
}
}
+ /**
+ * Notifies the status bar that a Emergency Action launch gesture has been detected.
+ *
+ * TODO (b/169175022) Update method name and docs when feature name is locked.
+ */
+ @Override
+ public void onEmergencyActionLaunchGestureDetected() {
+ if (SPEW) Slog.d(TAG, "Launching emergency action");
+ if (mBar != null) {
+ try {
+ mBar.onEmergencyActionLaunchGestureDetected();
+ } catch (RemoteException e) {
+ if (SPEW) Slog.d(TAG, "Failed to launch emergency action");
+ }
+ }
+ }
+
@Override
public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) {
StatusBarManagerService.this.topAppWindowChanged(displayId, isFullscreen, isImmersive);
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index 08eaa29..1f73977 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -28,6 +28,8 @@
import android.app.time.TimeZoneConfiguration;
import android.os.UserHandle;
+import com.android.internal.util.Preconditions;
+
import java.util.Objects;
/**
@@ -40,6 +42,7 @@
private final @UserIdInt int mUserId;
private final boolean mUserConfigAllowed;
private final boolean mAutoDetectionSupported;
+ private final boolean mGeoDetectionSupported;
private final boolean mAutoDetectionEnabled;
private final boolean mLocationEnabled;
private final boolean mGeoDetectionEnabled;
@@ -48,9 +51,13 @@
mUserId = builder.mUserId;
mUserConfigAllowed = builder.mUserConfigAllowed;
mAutoDetectionSupported = builder.mAutoDetectionSupported;
+ mGeoDetectionSupported = builder.mGeoDetectionSupported;
mAutoDetectionEnabled = builder.mAutoDetectionEnabled;
mLocationEnabled = builder.mLocationEnabled;
mGeoDetectionEnabled = builder.mGeoDetectionEnabled;
+ // if mGeoDetectionSupported then mAutoDetectionSupported, i.e. mGeoDetectionSupported
+ // cannot be true if mAutoDetectionSupported == false
+ Preconditions.checkState(mAutoDetectionSupported || !mGeoDetectionSupported);
}
/** Returns the ID of the user this configuration is associated with. */
@@ -69,11 +76,16 @@
return mUserConfigAllowed;
}
- /** Returns true if the device supports some form of auto time zone detection. */
+ /** Returns true if the device supports any form of auto time zone detection. */
public boolean isAutoDetectionSupported() {
return mAutoDetectionSupported;
}
+ /** Returns true if the device supports geolocation time zone detection. */
+ public boolean isGeoDetectionSupported() {
+ return mGeoDetectionSupported;
+ }
+
/** Returns the value of the auto time zone detection enabled setting. */
public boolean getAutoDetectionEnabledSetting() {
return mAutoDetectionEnabled;
@@ -101,10 +113,10 @@
* distinct from the raw setting value.
*/
public boolean getGeoDetectionEnabledBehavior() {
- if (getAutoDetectionEnabledBehavior()) {
- return mLocationEnabled && mGeoDetectionEnabled;
- }
- return false;
+ return getAutoDetectionEnabledBehavior()
+ && isGeoDetectionSupported()
+ && isLocationEnabled()
+ && getGeoDetectionEnabledSetting();
}
/** Creates a {@link TimeZoneCapabilitiesAndConfig} object using the configuration values. */
@@ -121,10 +133,10 @@
// Automatic time zone detection is only supported on devices if there is a telephony
// network available or geolocation time zone detection is possible.
- boolean deviceHasTimeZoneDetection = isAutoDetectionSupported();
+ boolean deviceHasAutoTimeZoneDetection = isAutoDetectionSupported();
final int configureAutoDetectionEnabledCapability;
- if (!deviceHasTimeZoneDetection) {
+ if (!deviceHasAutoTimeZoneDetection) {
configureAutoDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED;
} else if (!allowConfigDateTime) {
configureAutoDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED;
@@ -133,8 +145,9 @@
}
builder.setConfigureAutoDetectionEnabledCapability(configureAutoDetectionEnabledCapability);
+ boolean deviceHasLocationTimeZoneDetection = isGeoDetectionSupported();
final int configureGeolocationDetectionEnabledCapability;
- if (!deviceHasTimeZoneDetection) {
+ if (!deviceHasLocationTimeZoneDetection) {
configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED;
} else if (!allowConfigDateTime) {
configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED;
@@ -199,6 +212,7 @@
return mUserId == that.mUserId
&& mUserConfigAllowed == that.mUserConfigAllowed
&& mAutoDetectionSupported == that.mAutoDetectionSupported
+ && mGeoDetectionSupported == that.mGeoDetectionSupported
&& mAutoDetectionEnabled == that.mAutoDetectionEnabled
&& mLocationEnabled == that.mLocationEnabled
&& mGeoDetectionEnabled == that.mGeoDetectionEnabled;
@@ -207,7 +221,8 @@
@Override
public int hashCode() {
return Objects.hash(mUserId, mUserConfigAllowed, mAutoDetectionSupported,
- mAutoDetectionEnabled, mLocationEnabled, mGeoDetectionEnabled);
+ mGeoDetectionSupported, mAutoDetectionEnabled, mLocationEnabled,
+ mGeoDetectionEnabled);
}
@Override
@@ -216,6 +231,7 @@
+ "mUserId=" + mUserId
+ ", mUserConfigAllowed=" + mUserConfigAllowed
+ ", mAutoDetectionSupported=" + mAutoDetectionSupported
+ + ", mGeoDetectionSupported=" + mGeoDetectionSupported
+ ", mAutoDetectionEnabled=" + mAutoDetectionEnabled
+ ", mLocationEnabled=" + mLocationEnabled
+ ", mGeoDetectionEnabled=" + mGeoDetectionEnabled
@@ -228,8 +244,10 @@
public static class Builder {
private final @UserIdInt int mUserId;
+
private boolean mUserConfigAllowed;
private boolean mAutoDetectionSupported;
+ private boolean mGeoDetectionSupported;
private boolean mAutoDetectionEnabled;
private boolean mLocationEnabled;
private boolean mGeoDetectionEnabled;
@@ -248,6 +266,7 @@
this.mUserId = toCopy.mUserId;
this.mUserConfigAllowed = toCopy.mUserConfigAllowed;
this.mAutoDetectionSupported = toCopy.mAutoDetectionSupported;
+ this.mGeoDetectionSupported = toCopy.mGeoDetectionSupported;
this.mAutoDetectionEnabled = toCopy.mAutoDetectionEnabled;
this.mLocationEnabled = toCopy.mLocationEnabled;
this.mGeoDetectionEnabled = toCopy.mGeoDetectionEnabled;
@@ -262,7 +281,7 @@
}
/**
- * Sets whether automatic time zone detection is supported on this device.
+ * Sets whether any form of automatic time zone detection is supported on this device.
*/
public Builder setAutoDetectionSupported(boolean supported) {
mAutoDetectionSupported = supported;
@@ -270,6 +289,14 @@
}
/**
+ * Sets whether geolocation time zone detection is supported on this device.
+ */
+ public Builder setGeoDetectionSupported(boolean supported) {
+ mGeoDetectionSupported = supported;
+ return this;
+ }
+
+ /**
* Sets the value of the automatic time zone detection enabled setting for this device.
*/
public Builder setAutoDetectionEnabled(boolean enabled) {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
index 964dbec..941be0e 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -119,13 +119,13 @@
@Override
public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
- boolean geoDetectionEnabled = mGeoDetectionFeatureEnabled && isGeoDetectionEnabled(userId);
return new ConfigurationInternal.Builder(userId)
.setUserConfigAllowed(isUserConfigAllowed(userId))
.setAutoDetectionSupported(isAutoDetectionSupported())
+ .setGeoDetectionSupported(isGeoDetectionSupported())
.setAutoDetectionEnabled(isAutoDetectionEnabled())
.setLocationEnabled(isLocationEnabled(userId))
- .setGeoDetectionEnabled(geoDetectionEnabled)
+ .setGeoDetectionEnabled(isGeoDetectionEnabled(userId))
.build();
}
@@ -170,7 +170,11 @@
final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled();
setAutoDetectionEnabled(autoDetectionEnabled);
- if (mGeoDetectionFeatureEnabled) {
+ // Avoid writing the geo detection enabled setting for devices that do not support geo
+ // time zone detection: if we wrote it down then we'd set the value explicitly, which
+ // would prevent detecting "default" later. That might influence what happens on later
+ // releases that support geo detection on the same hardware.
+ if (isGeoDetectionSupported()) {
final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
setGeoDetectionEnabled(userId, geoTzDetectionEnabled);
}
@@ -183,7 +187,11 @@
}
private boolean isAutoDetectionSupported() {
- return deviceHasTelephonyNetwork() || mGeoDetectionFeatureEnabled;
+ return deviceHasTelephonyNetwork() || isGeoDetectionSupported();
+ }
+
+ private boolean isGeoDetectionSupported() {
+ return mGeoDetectionFeatureEnabled;
}
private boolean isAutoDetectionEnabled() {
@@ -199,10 +207,10 @@
}
private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
- final boolean locationEnabled = isLocationEnabled(userId);
+ final boolean geoDetectionEnabledByDefault = false;
return Settings.Secure.getIntForUser(mCr,
Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
- locationEnabled ? 1 : 0 /* defaultValue */, userId) != 0;
+ (geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0;
}
private void setGeoDetectionEnabled(@UserIdInt int userId, boolean enabled) {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index d09cd38..68a086d 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -30,6 +30,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -58,11 +59,14 @@
private static final String TAG = "TimeZoneDetectorService";
/**
- * A compile time constant "feature switch" for enabling / disabling location-based time zone
- * detection on Android. If this is {@code false}, there should be few / little changes in
- * behavior with previous releases and little overhead associated with geolocation components.
+ * A "feature switch" for enabling / disabling location-based time zone detection. If this is
+ * {@code false}, there should be few / little changes in behavior with previous releases and
+ * little overhead associated with geolocation components.
+ * TODO(b/151304765) Remove this when the feature is on for all.
*/
- public static final boolean GEOLOCATION_TIME_ZONE_DETECTION_ENABLED = false;
+ public static final boolean GEOLOCATION_TIME_ZONE_DETECTION_ENABLED =
+ SystemProperties.getBoolean(
+ "persist.sys.location_time_zone_detection_feature_enabled", false);
/**
* Handles the service lifecycle for {@link TimeZoneDetectorService} and
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 7b044ed..149dbd0 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -54,7 +54,7 @@
import android.media.tv.ITvInputServiceCallback;
import android.media.tv.ITvInputSession;
import android.media.tv.ITvInputSessionCallback;
-import android.media.tv.TvChannelInfo;
+import android.media.tv.TunedInfo;
import android.media.tv.TvContentRating;
import android.media.tv.TvContentRatingSystemInfo;
import android.media.tv.TvContract;
@@ -90,7 +90,6 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import java.io.File;
import java.io.FileDescriptor;
@@ -114,7 +113,7 @@
private static final boolean DEBUG = false;
private static final String TAG = "TvInputManagerService";
private static final String DVB_DIRECTORY = "/dev/dvb";
- private static final int APP_TAG_SELF = TvChannelInfo.APP_TAG_SELF;
+ private static final int APP_TAG_SELF = TunedInfo.APP_TAG_SELF;
private static final String PERMISSION_ACCESS_WATCHED_PROGRAMS =
"com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS";
@@ -860,9 +859,9 @@
try {
ITvInputManagerCallback callback = userState.mCallbacks.getBroadcastItem(i);
Pair<Integer, Integer> pidUid = userState.callbackPidUidMap.get(callback);
- List<TvChannelInfo> infos = getCurrentTvChannelInfosInternalLocked(
+ List<TunedInfo> infos = getCurrentTunedInfosInternalLocked(
userState, pidUid.first, pidUid.second);
- callback.onCurrentTvChannelInfosUpdated(infos);
+ callback.onCurrentTunedInfosUpdated(infos);
} catch (RemoteException e) {
Slog.e(TAG, "failed to report updated current channel infos to callback", e);
}
@@ -2097,14 +2096,14 @@
}
@Override
- public List<TvChannelInfo> getCurrentTvChannelInfos(@UserIdInt int userId) {
+ public List<TunedInfo> getCurrentTunedInfos(@UserIdInt int userId) {
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
"getTvCurrentChannelInfos");
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
- return getCurrentTvChannelInfosInternalLocked(userState, callingPid, callingUid);
+ return getCurrentTunedInfosInternalLocked(userState, callingPid, callingUid);
}
}
@@ -2278,9 +2277,9 @@
}
}
- private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked(
+ private List<TunedInfo> getCurrentTunedInfosInternalLocked(
UserState userState, int callingPid, int callingUid) {
- List<TvChannelInfo> channelInfos = new ArrayList<>();
+ List<TunedInfo> channelInfos = new ArrayList<>();
boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission(callingPid, callingUid);
for (SessionState state : userState.sessionStateMap.values()) {
if (state.isCurrent) {
@@ -2288,7 +2287,7 @@
int appType;
if (state.callingUid == callingUid) {
appTag = APP_TAG_SELF;
- appType = TvChannelInfo.APP_TYPE_SELF;
+ appType = TunedInfo.APP_TYPE_SELF;
} else {
appTag = userState.mAppTagMap.get(state.callingUid);
if (appTag == null) {
@@ -2296,10 +2295,10 @@
userState.mAppTagMap.put(state.callingUid, appTag);
}
appType = isSystemApp(state.componentName.getPackageName())
- ? TvChannelInfo.APP_TYPE_SYSTEM
- : TvChannelInfo.APP_TYPE_NON_SYSTEM;
+ ? TunedInfo.APP_TYPE_SYSTEM
+ : TunedInfo.APP_TYPE_NON_SYSTEM;
}
- channelInfos.add(new TvChannelInfo(
+ channelInfos.add(new TunedInfo(
state.inputId,
watchedProgramsAccess ? state.currentChannel : null,
state.isRecordingSession,
diff --git a/services/core/java/com/android/server/vcn/OWNERS b/services/core/java/com/android/server/vcn/OWNERS
new file mode 100644
index 0000000..33b9f0f
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+benedictwong@google.com
+ckesting@google.com
+evitayan@google.com
+nharold@google.com
+jchalard@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index fb06a9c..9d08b1b 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -397,6 +397,13 @@
return -1;
}
}
+
+ PackageOptimizationInfo getPackageOptimizationInfo(ArtManagerInternal artManagerInternal) {
+ return artManagerInternal == null || launchedActivityAppRecordRequiredAbi == null
+ ? PackageOptimizationInfo.createWithNoInfo()
+ : artManagerInternal.getPackageOptimizationInfo(applicationInfo,
+ launchedActivityAppRecordRequiredAbi, launchedActivityName);
+ }
}
ActivityMetricsLogger(ActivityStackSupervisor supervisor, Looper looper) {
@@ -857,14 +864,8 @@
info.bindApplicationDelayMs);
}
builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs);
- final ArtManagerInternal artManagerInternal = getArtManagerInternal();
final PackageOptimizationInfo packageOptimizationInfo =
- (artManagerInternal == null) || (info.launchedActivityAppRecordRequiredAbi == null)
- ? PackageOptimizationInfo.createWithNoInfo()
- : artManagerInternal.getPackageOptimizationInfo(
- info.applicationInfo,
- info.launchedActivityAppRecordRequiredAbi,
- info.launchedActivityName);
+ info.getPackageOptimizationInfo(getArtManagerInternal());
builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_REASON,
packageOptimizationInfo.getCompilationReason());
builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_FILTER,
@@ -985,6 +986,8 @@
builder.addTaggedData(APP_TRANSITION_PROCESS_RUNNING,
info.mProcessRunning ? 1 : 0);
mMetricsLogger.write(builder);
+ final PackageOptimizationInfo packageOptimizationInfo =
+ infoSnapshot.getPackageOptimizationInfo(getArtManagerInternal());
FrameworkStatsLog.write(
FrameworkStatsLog.APP_START_FULLY_DRAWN,
info.mLastLaunchedActivity.info.applicationInfo.uid,
@@ -995,6 +998,8 @@
info.mLastLaunchedActivity.info.name,
info.mProcessRunning,
startupTimeMs,
+ packageOptimizationInfo.getCompilationReason(),
+ packageOptimizationInfo.getCompilationFilter(),
info.mSourceType,
info.mSourceEventDelayMs);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9868fc1..aea944c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -701,6 +701,9 @@
// Token for targeting this activity for assist purposes.
final Binder assistToken = new Binder();
+ // Tracking cookie for the launch of this activity and it's task.
+ IBinder mLaunchCookie;
+
private final Runnable mPauseTimeoutRunnable = new Runnable() {
@Override
public void run() {
@@ -1643,6 +1646,7 @@
mHandoverTaskDisplayArea = daToken != null
? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
mHandoverLaunchDisplayId = options.getLaunchDisplayId();
+ mLaunchCookie = options.getLaunchCookie();
}
}
@@ -3222,6 +3226,7 @@
getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
mWmService.mTaskSnapshotController.onAppRemoved(this);
mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
+ mStackSupervisor.mStoppingActivities.remove(this);
waitingToShow = false;
// TODO(b/169035022): move to a more-appropriate place.
@@ -3985,10 +3990,6 @@
}
}
- ActivityOptions getOptionsForTargetActivityLocked() {
- return pendingOptions != null ? pendingOptions.forTargetActivity() : null;
- }
-
void clearOptionsLocked() {
clearOptionsLocked(true /* withAbort */);
}
@@ -4760,14 +4761,15 @@
supportsEnterPipOnTaskSwitch = false;
break;
case RESUMED:
- // If the app is capable of entering PIP, we should try pausing it now
- // so it can PIP correctly.
- if (deferHidingClient) {
- getRootTask().startPausingLocked(
- mStackSupervisor.mUserLeaving /* userLeaving */,
- false /* uiSleeping */, null /* resuming */, "makeInvisible");
+ // Do nothing if currently in the process of resuming the activity. Otherwise,
+ // starting to pause it since it is not visible.
+ if (task.mInResumeTopActivity
+ && task.topRunningActivity(true /* focusableOnly */) == this) {
break;
}
+ getRootTask().startPausingLocked(mStackSupervisor.mUserLeaving,
+ false /* uiSleeping */, null /* resuming */, "makeInvisible");
+ // fall through
case INITIALIZING:
case PAUSING:
case PAUSED:
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 28e71fa..c8a8f81 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1358,6 +1358,12 @@
}
return false;
}
+ // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
+ if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
+ Slog.w(TAG, "Background activity start for " + callingPackage
+ + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
+ return false;
+ }
// If we don't have callerApp at this point, no caller was provided to startActivity().
// That's the case for PendingIntent-based starts, since the creator's process might not be
// up and alive. If that's the case, we retrieve the WindowProcessController for the send()
@@ -1394,12 +1400,6 @@
}
}
}
- // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
- if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
- Slog.w(TAG, "Background activity start for " + callingPackage
- + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
- return false;
- }
// anything that has fallen through would currently be aborted
Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
+ "; callingUid: " + callingUid
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
index 4e742b9..af6c255 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
@@ -24,8 +24,8 @@
*/
public interface BackgroundActivityStartCallback {
/**
- * The token that allowed the activity start that triggered {@link
- * #onExclusiveTokenActivityStart()}.
+ * The token for which this callback is responsible for deciding whether the app can start
+ * background activities or not.
*
* Ideally this should just return a final variable, don't do anything costly here (don't hold
* any locks).
@@ -33,7 +33,10 @@
IBinder getToken();
/**
- * Called when the background activity start happens.
+ * Returns true if the background activity start due to originating token in {@link #getToken()}
+ * should be allowed or not.
+ *
+ * This will be called holding the WM lock, don't do anything costly here.
*/
- void onExclusiveTokenActivityStart(String packageName);
+ boolean isActivityStartAllowed(int uid, String packageName);
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 13033a6..60a62dc 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -157,6 +157,11 @@
mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration);
}
+ /** Returns {@code true} if requested override override configuration is not empty. */
+ boolean hasRequestedOverrideConfiguration() {
+ return mHasOverrideConfiguration;
+ }
+
/** Returns requested override configuration applied to this configuration container. */
public Configuration getRequestedOverrideConfiguration() {
return mRequestedOverrideConfiguration;
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 8bd42f0..38ad070 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -41,6 +41,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.policy.WindowManagerPolicy;
+import java.io.PrintWriter;
import java.util.Comparator;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@@ -71,6 +72,13 @@
IDisplayAreaOrganizer mOrganizer;
private final Configuration mTmpConfiguration = new Configuration();
+ /**
+ * Whether this {@link DisplayArea} should ignore fixed-orientation request. If {@code true}, it
+ * can never specify orientation, but shows the fixed-orientation apps below it in the
+ * letterbox; otherwise, it rotates based on the fixed-orientation request.
+ */
+ protected boolean mIgnoreOrientationRequest;
+
DisplayArea(WindowManagerService wms, Type type, String name) {
this(wms, type, name, FEATURE_UNDEFINED);
}
@@ -127,6 +135,56 @@
}
}
+ @Override
+ int getOrientation(int candidate) {
+ mLastOrientationSource = null;
+ if (mIgnoreOrientationRequest) {
+ return SCREEN_ORIENTATION_UNSET;
+ }
+
+ return super.getOrientation(candidate);
+ }
+
+ /**
+ * Sets whether this {@link DisplayArea} should ignore fixed-orientation request from apps and
+ * windows below it.
+ *
+ * @return Whether the display orientation changed after calling this method.
+ */
+ boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
+ if (mIgnoreOrientationRequest == ignoreOrientationRequest) {
+ return false;
+ }
+ mIgnoreOrientationRequest = ignoreOrientationRequest;
+
+ // Check whether we should notify Display to update orientation.
+ if (mDisplayContent == null) {
+ return false;
+ }
+
+ // The orientation request from this DA may now be respected.
+ if (!ignoreOrientationRequest) {
+ return mDisplayContent.updateOrientation();
+ }
+
+ final int lastOrientation = mDisplayContent.getLastOrientation();
+ final WindowContainer lastOrientationSource = mDisplayContent.getLastOrientationSource();
+ if (lastOrientation == SCREEN_ORIENTATION_UNSET
+ || lastOrientation == SCREEN_ORIENTATION_UNSPECIFIED) {
+ // Orientation won't be changed.
+ return false;
+ }
+ if (lastOrientationSource == null || lastOrientationSource.isDescendantOf(this)) {
+ // Try update if the orientation may be affected.
+ return mDisplayContent.updateOrientation();
+ }
+ return false;
+ }
+
+ boolean getIgnoreOrientationRequest() {
+ return mIgnoreOrientationRequest;
+ }
+
/**
* When a {@link DisplayArea} is repositioned, it should only be moved among its siblings of the
* same {@link Type}.
@@ -200,6 +258,34 @@
}
@Override
+ void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+ super.dump(pw, prefix, dumpAll);
+ if (mIgnoreOrientationRequest) {
+ pw.println(prefix + "mIgnoreOrientationRequest=true");
+ }
+ if (hasRequestedOverrideConfiguration()) {
+ pw.println(prefix + "overrideConfig=" + getRequestedOverrideConfiguration());
+ }
+ }
+
+ void dumpChildDisplayArea(PrintWriter pw, String prefix, boolean dumpAll) {
+ final String doublePrefix = prefix + " ";
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ final DisplayArea<?> childArea = getChildAt(i).asDisplayArea();
+ if (childArea == null) {
+ continue;
+ }
+ pw.println(prefix + "* " + childArea.getName());
+ if (childArea.isTaskDisplayArea()) {
+ // TaskDisplayArea can only contain task. And it is already printed by display.
+ continue;
+ }
+ childArea.dump(pw, doublePrefix, dumpAll);
+ childArea.dumpChildDisplayArea(pw, doublePrefix, dumpAll);
+ }
+ }
+
+ @Override
long getProtoFieldId() {
return DISPLAY_AREA;
}
@@ -370,6 +456,9 @@
Comparator.comparingInt(WindowToken::getWindowLayerFromType);
private final Predicate<WindowState> mGetOrientingWindow = w -> {
+ if (!w.isVisible() || !w.mLegacyPolicyVisibilityAfterAnim) {
+ return false;
+ }
final WindowManagerPolicy policy = mWmService.mPolicy;
if (policy.isKeyguardHostWindow(w.mAttrs)) {
if (mWmService.mKeyguardGoingAway) {
@@ -405,6 +494,11 @@
@Override
int getOrientation(int candidate) {
+ mLastOrientationSource = null;
+ if (mIgnoreOrientationRequest) {
+ return SCREEN_ORIENTATION_UNSET;
+ }
+
// Find a window requesting orientation.
final WindowState win = getWindow(mGetOrientingWindow);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 59f4232..b06d770 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -28,8 +28,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
@@ -2347,6 +2347,13 @@
@Override
int getOrientation() {
mLastOrientationSource = null;
+ if (mIgnoreOrientationRequest) {
+ // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "Display id=%d is ignoring all orientation requests, return %d",
+ mDisplayId, SCREEN_ORIENTATION_UNSPECIFIED);
+ return SCREEN_ORIENTATION_UNSPECIFIED;
+ }
if (mWmService.mDisplayFrozen) {
if (mWmService.mPolicy.isKeyguardLocked()) {
@@ -2363,19 +2370,15 @@
}
final int orientation = super.getOrientation();
- if (orientation != SCREEN_ORIENTATION_UNSET && orientation != SCREEN_ORIENTATION_BEHIND) {
+ if (orientation == SCREEN_ORIENTATION_UNSET) {
+ // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation
ProtoLog.v(WM_DEBUG_ORIENTATION,
- "App is requesting an orientation, return %d for display id=%d",
- orientation, mDisplayId);
- return orientation;
+ "No app or window is requesting an orientation, return %d for display id=%d",
+ SCREEN_ORIENTATION_UNSPECIFIED, mDisplayId);
+ return SCREEN_ORIENTATION_UNSPECIFIED;
}
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "No app is requesting an orientation, return %d for display id=%d",
- getLastOrientation(), mDisplayId);
- // The next app has not been requested to be visible, so we keep the current orientation
- // to prevent freezing/unfreezing the display too early.
- return getLastOrientation();
+ return orientation;
}
void updateDisplayInfo() {
@@ -2982,6 +2985,10 @@
}
pw.println();
+ pw.println(prefix + "Display areas in top down Z order:");
+ dumpChildDisplayArea(pw, subPrefix, dumpAll);
+
+ pw.println();
pw.println(prefix + "Task display areas in top down Z order:");
forAllTaskDisplayAreas(taskDisplayArea -> {
taskDisplayArea.dump(pw, prefix + " ", dumpAll);
@@ -4243,6 +4250,10 @@
@Override
int getOrientation(int candidate) {
+ if (mIgnoreOrientationRequest) {
+ return SCREEN_ORIENTATION_UNSET;
+ }
+
// IME does not participate in orientation.
return candidate;
}
@@ -5349,8 +5360,13 @@
return mDisplayPolicy.getSystemUiContext();
}
- Point getDisplayPosition() {
- return mWmService.mDisplayManagerInternal.getDisplayPosition(getDisplayId());
+ @Override
+ boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
+ if (mIgnoreOrientationRequest == ignoreOrientationRequest) return false;
+ final boolean rotationChanged = super.setIgnoreOrientationRequest(ignoreOrientationRequest);
+ mWmService.mDisplayWindowSettings.setIgnoreOrientationRequest(
+ this, mIgnoreOrientationRequest);
+ return rotationChanged;
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 3ff369a3..4e7e0ba 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1040,9 +1040,12 @@
}
if (attrs.providesInsetsTypes != null) {
- mContext.enforcePermission(
- android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
- "DisplayPolicy");
+ // Recents component is allowed to add inset types.
+ if (!mService.mAtmInternal.isCallerRecents(callingUid)) {
+ mContext.enforcePermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+ "DisplayPolicy");
+ }
enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providesInsetsTypes);
for (@InternalInsetsType int insetType : attrs.providesInsetsTypes) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 66c4b4e..c503431 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -853,6 +853,10 @@
}
}
+ int getFixedToUserRotationMode() {
+ return mFixedToUserRotation;
+ }
+
/**
* Returns {@code true} if this display rotation takes app requested orientation into
* consideration; {@code false} otherwise. For the time being the only case where this is {@code
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 04e37fa..f647bea 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -112,6 +112,7 @@
private boolean mShouldShowSystemDecors = false;
private boolean mShouldShowIme = false;
private int mFixedToUserRotation = IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
+ private boolean mIgnoreOrientationRequest = false;
private Entry(String name) {
mName = name;
@@ -131,6 +132,7 @@
mShouldShowSystemDecors = copyFrom.mShouldShowSystemDecors;
mShouldShowIme = copyFrom.mShouldShowIme;
mFixedToUserRotation = copyFrom.mFixedToUserRotation;
+ mIgnoreOrientationRequest = copyFrom.mIgnoreOrientationRequest;
}
/** @return {@code true} if all values are default. */
@@ -144,7 +146,8 @@
&& !mShouldShowWithInsecureKeyguard
&& !mShouldShowSystemDecors
&& !mShouldShowIme
- && mFixedToUserRotation == IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT;
+ && mFixedToUserRotation == IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT
+ && !mIgnoreOrientationRequest;
}
}
@@ -248,6 +251,15 @@
writeSettingsIfNeeded(entry, displayInfo);
}
+ void setIgnoreOrientationRequest(
+ DisplayContent displayContent, boolean ignoreOrientationRequest) {
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final Entry entry = getOrCreateEntry(displayInfo);
+ if (entry.mIgnoreOrientationRequest == ignoreOrientationRequest) return;
+ entry.mIgnoreOrientationRequest = ignoreOrientationRequest;
+ writeSettingsIfNeeded(entry, displayInfo);
+ }
+
private int getWindowingModeLocked(Entry entry, DisplayContent dc) {
int windowingMode = entry != null ? entry.mWindowingMode
: WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -389,6 +401,7 @@
final boolean hasSizeOverride = entry.mForcedWidth != 0 && entry.mForcedHeight != 0;
dc.mIsDensityForced = hasDensityOverride;
dc.mIsSizeForced = hasSizeOverride;
+ dc.setIgnoreOrientationRequest(entry.mIgnoreOrientationRequest);
final int width = hasSizeOverride ? entry.mForcedWidth : dc.mBaseDisplayWidth;
final int height = hasSizeOverride ? entry.mForcedHeight : dc.mBaseDisplayHeight;
@@ -529,6 +542,8 @@
entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors");
entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme");
entry.mFixedToUserRotation = getIntAttribute(parser, "fixedToUserRotation");
+ entry.mIgnoreOrientationRequest
+ = getBooleanAttribute(parser, "ignoreOrientationRequest");
mEntries.put(name, entry);
}
XmlUtils.skipCurrentTag(parser);
@@ -613,6 +628,10 @@
out.attribute(null, "fixedToUserRotation",
Integer.toString(entry.mFixedToUserRotation));
}
+ if (entry.mIgnoreOrientationRequest) {
+ out.attribute(null, "ignoreOrientationRequest",
+ Boolean.toString(entry.mIgnoreOrientationRequest));
+ }
out.endTag(null, "display");
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 143b657..470c2b1 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -531,7 +531,7 @@
// event. This is used to omit Surfaces from occlusion detection.
populateOverlayInputInfo(mInvalidInputWindow, w.getName(), type, isVisible);
mInputTransaction.setInputWindowInfo(
- w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
+ w.mWinAnimator.mSurfaceController.mSurfaceControl,
mInvalidInputWindow);
return;
}
@@ -600,8 +600,7 @@
if (w.mWinAnimator.hasSurface()) {
mInputTransaction.setInputWindowInfo(
- w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
- inputWindowHandle);
+ w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
}
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index dda1e2d..4ad2575 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -389,9 +389,7 @@
final int taskCount = visibleTasks.size();
for (int i = 0; i < taskCount; i++) {
final Task task = visibleTasks.get(i);
- final WindowConfiguration config = task.getWindowConfiguration();
- if (config.tasksAreFloating()
- || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ if (skipAnimation(task)) {
continue;
}
addAnimation(task, !recentTaskIds.get(task.mTaskId));
@@ -434,6 +432,19 @@
}
}
+
+ /**
+ * Whether a task should be filtered from the recents animation. This can be true for tasks
+ * being displayed outside of recents.
+ */
+ private boolean skipAnimation(Task task) {
+ final WindowConfiguration config = task.getWindowConfiguration();
+ return task.isAlwaysOnTop()
+ || config.tasksAreFloating()
+ || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+ }
+
+
@VisibleForTesting
AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
return addAnimation(task, isRecentTaskInvisible, false /* hidden */,
@@ -529,8 +540,9 @@
void addTaskToTargets(Task task, OnAnimationFinishedCallback finishedCallback) {
if (mRunner != null) {
- // No need to send task appeared when the task target already exists.
- if (isAnimatingTask(task)) {
+ // No need to send task appeared when the task target already exists, or when the
+ // task is being managed as a multi-window mode outside of recents (e.g. bubbles).
+ if (isAnimatingTask(task) || skipAnimation(task)) {
return;
}
final RemoteAnimationTarget target = createTaskRemoteAnimation(task, finishedCallback);
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 25732e7..7ed22a1 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -203,16 +203,14 @@
.setCallsite("ScreenRotationAnimation")
.build();
- // In case display bounds change, screenshot buffer and surface may mismatch so set a
- // scaling mode.
- SurfaceControl.Transaction t2 = mService.mTransactionFactory.get();
- t2.setOverrideScalingMode(mScreenshotLayer, Surface.SCALING_MODE_SCALE_TO_WINDOW);
- t2.apply(true /* sync */);
-
// Capture a screenshot into the surface we just created.
final int displayId = displayContent.getDisplayId();
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);
+
SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
mService.mDisplayManagerInternal.systemScreenshot(displayId);
if (screenshotBuffer != null) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1fdb49f..02230d6 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -204,15 +204,14 @@
int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
- SurfaceControl outBLASTSurfaceControl) {
+ InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
int res = mService.relayoutWindow(this, window, attrs,
requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
- outActiveControls, outSurfaceSize, outBLASTSurfaceControl);
+ outActiveControls, outSurfaceSize);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
+ Binder.getCallingPid());
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 249fe03..ecee46e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1474,14 +1474,6 @@
// Update task bounds if needed.
adjustBoundsForDisplayChangeIfNeeded(getDisplayContent());
- if (getWindowConfiguration().windowsAreScaleable()) {
- // We force windows out of SCALING_MODE_FREEZE so that we can continue to animate them
- // while a resize is pending.
- forceWindowsScaleable(true /* force */);
- } else {
- forceWindowsScaleable(false /* force */);
- }
-
mRootWindowContainer.updateUIDsPresentOnDisplay();
// Resume next focusable stack after reparenting to another display if we aren't removing
@@ -3780,17 +3772,6 @@
positionChildAt(position, child, false /* includeParents */);
}
- void forceWindowsScaleable(boolean force) {
- mWmService.openSurfaceTransaction();
- try {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- mChildren.get(i).forceWindowsScaleableInTransaction(force);
- }
- } finally {
- mWmService.closeSurfaceTransaction("forceWindowsScaleable");
- }
- }
-
void setTaskDescription(TaskDescription taskDescription) {
mTaskDescription = taskDescription;
}
@@ -4062,6 +4043,9 @@
info.topActivityInfo = mReuseActivitiesReport.top != null
? mReuseActivitiesReport.top.info
: null;
+ forAllActivities(r -> {
+ info.addLaunchCookie(r.mLaunchCookie);
+ });
}
@Nullable PictureInPictureParams getPictureInPictureParams() {
@@ -4792,9 +4776,11 @@
// If the task is not yet visible when it is added to the task organizer, then we should
// hide it to allow the task organizer to show it when it is properly reparented. We
// skip this for tasks created by the organizer because they can synchronously update
- // the leash before new children are added to the task.
+ // the leash before new children are added to the task. Also skip this if the task
+ // has already been sent to the organizer which can happen before the first draw if
+ // an existing task is reported to the organizer when it first registers.
if (!mAtmService.getTransitionController().isShellTransitionsEnabled()
- && !mCreatedByOrganizer
+ && !mCreatedByOrganizer && !mTaskAppearedSent
&& mTaskOrganizer != null && !prevHasBeenVisible) {
getSyncTransaction().hide(getSurfaceControl());
commitPendingTransaction();
@@ -4846,6 +4832,11 @@
@VisibleForTesting
boolean setTaskOrganizer(ITaskOrganizer organizer) {
+ return setTaskOrganizer(organizer, false /* skipTaskAppeared */);
+ }
+
+ @VisibleForTesting
+ boolean setTaskOrganizer(ITaskOrganizer organizer, boolean skipTaskAppeared) {
if (mTaskOrganizer == organizer) {
return false;
}
@@ -4858,7 +4849,9 @@
sendTaskVanished(prevOrganizer);
if (mTaskOrganizer != null) {
- sendTaskAppeared();
+ if (!skipTaskAppeared) {
+ sendTaskAppeared();
+ }
} else {
// No longer managed by any organizer.
mTaskAppearedSent = false;
@@ -4871,6 +4864,10 @@
return true;
}
+ boolean updateTaskOrganizerState(boolean forceUpdate) {
+ return updateTaskOrganizerState(forceUpdate, false /* skipTaskAppeared */);
+ }
+
/**
* Called when the task state changes (ie. from windowing mode change) an the task organizer
* state should also be updated.
@@ -4878,9 +4875,10 @@
* @param forceUpdate Updates the task organizer to the one currently specified in the task
* org controller for the task's windowing mode, ignoring the cached
* windowing mode checks.
+ * @param skipTaskAppeared Skips calling taskAppeared for the new organizer if it has changed
* @return {@code true} if task organizer changed.
*/
- boolean updateTaskOrganizerState(boolean forceUpdate) {
+ boolean updateTaskOrganizerState(boolean forceUpdate, boolean skipTaskAppeared) {
if (getSurfaceControl() == null) {
// Can't call onTaskAppeared without a surfacecontrol, so defer this until after one
// is created.
@@ -4896,7 +4894,7 @@
if (!forceUpdate && mTaskOrganizer == organizer) {
return false;
}
- return setTaskOrganizer(organizer);
+ return setTaskOrganizer(organizer, skipTaskAppeared);
}
@Override
@@ -5883,7 +5881,11 @@
final TaskDisplayArea taskDisplayArea = getDisplayArea();
// If the top activity is the resumed one, nothing to do.
+ // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible
+ // we still want to proceed if the visibility of other windows have changed (e.g. bringing
+ // a fullscreen window forward to cover another freeform activity.)
if (mResumedActivity == next && next.isState(RESUMED)
+ && taskDisplayArea.getWindowingMode() != WINDOWING_MODE_FREEFORM
&& taskDisplayArea.allResumedActivitiesComplete()) {
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index d69fb0b..830ad5d 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -29,10 +29,12 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
@@ -150,13 +152,6 @@
private boolean mRemoved;
/**
- * Whether the task display area should ignore fixed-orientation request. If {@code true}, it
- * can never specify orientation, but show the fixed-orientation apps in the letterbox;
- * otherwise, it rotates based on the fixed-orientation request when it has the focus.
- */
- private boolean mIgnoreOrientationRequest;
-
- /**
* The id of a leaf task that most recently being moved to front.
*/
private int mLastLeafTaskToFrontId;
@@ -654,28 +649,9 @@
}
}
- /**
- * Sets whether the task display area should ignore fixed-orientation request from apps.
- *
- * @return Whether the display orientation changed
- */
- boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
- if (mIgnoreOrientationRequest == ignoreOrientationRequest) {
- return false;
- }
-
- mIgnoreOrientationRequest = ignoreOrientationRequest;
- if (isLastFocused()) {
- // Update orientation if this TDA is the last focused, otherwise it shouldn't affect
- // the display.
- return mDisplayContent.updateOrientation();
- }
-
- return false;
- }
-
@Override
int getOrientation(int candidate) {
+ mLastOrientationSource = null;
// Only allow to specify orientation if this TDA is not set to ignore orientation request,
// and it has the focus.
if (mIgnoreOrientationRequest || !isLastFocused()) {
@@ -708,7 +684,21 @@
return SCREEN_ORIENTATION_UNSPECIFIED;
}
- return super.getOrientation(candidate);
+ final int orientation = super.getOrientation(candidate);
+ if (orientation != SCREEN_ORIENTATION_UNSET
+ && orientation != SCREEN_ORIENTATION_BEHIND) {
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "App is requesting an orientation, return %d for display id=%d",
+ orientation, mDisplayContent.mDisplayId);
+ return orientation;
+ }
+
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "No app is requesting an orientation, return %d for display id=%d",
+ mDisplayContent.getLastOrientation(), mDisplayContent.mDisplayId);
+ // The next app has not been requested to be visible, so we keep the current orientation
+ // to prevent freezing/unfreezing the display too early.
+ return mDisplayContent.getLastOrientation();
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 8201d10..48550ed0 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -32,6 +32,7 @@
import android.app.WindowConfiguration;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -39,6 +40,7 @@
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
+import android.window.TaskAppearedInfo;
import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
@@ -76,8 +78,6 @@
WINDOWING_MODE_FREEFORM
};
- private final WindowManagerGlobalLock mGlobalLock;
-
private class DeathRecipient implements IBinder.DeathRecipient {
ITaskOrganizer mTaskOrganizer;
@@ -103,39 +103,38 @@
* transaction before they are presented to the task org.
*/
private class TaskOrganizerCallbacks {
- final WindowManagerService mService;
final ITaskOrganizer mTaskOrganizer;
final Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
- private final SurfaceControl.Transaction mTransaction;
-
- TaskOrganizerCallbacks(WindowManagerService wm, ITaskOrganizer taskOrg,
+ TaskOrganizerCallbacks(ITaskOrganizer taskOrg,
Consumer<Runnable> deferTaskOrgCallbacksConsumer) {
- mService = wm;
mDeferTaskOrgCallbacksConsumer = deferTaskOrgCallbacksConsumer;
mTaskOrganizer = taskOrg;
- mTransaction = wm.mTransactionFactory.get();
}
IBinder getBinder() {
return mTaskOrganizer.asBinder();
}
+ SurfaceControl prepareLeash(Task task, boolean visible, String reason) {
+ SurfaceControl outSurfaceControl = new SurfaceControl(task.getSurfaceControl(), reason);
+ if (!task.mCreatedByOrganizer && !visible) {
+ // To prevent flashes, we hide the task prior to sending the leash to the
+ // task org if the task has previously hidden (ie. when entering PIP)
+ mTransaction.hide(outSurfaceControl);
+ mTransaction.apply();
+ }
+ return outSurfaceControl;
+ }
+
void onTaskAppeared(Task task) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId);
final boolean visible = task.isVisible();
final RunningTaskInfo taskInfo = task.getTaskInfo();
mDeferTaskOrgCallbacksConsumer.accept(() -> {
try {
- SurfaceControl outSurfaceControl = new SurfaceControl(task.getSurfaceControl(),
- "TaskOrganizerController.onTaskAppeared");
- if (!task.mCreatedByOrganizer && !visible) {
- // To prevent flashes, we hide the task prior to sending the leash to the
- // task org if the task has previously hidden (ie. when entering PIP)
- mTransaction.hide(outSurfaceControl);
- mTransaction.apply();
- }
- mTaskOrganizer.onTaskAppeared(taskInfo, outSurfaceControl);
+ mTaskOrganizer.onTaskAppeared(taskInfo, prepareLeash(task, visible,
+ "TaskOrganizerController.onTaskAppeared"));
} catch (RemoteException e) {
Slog.e(TAG, "Exception sending onTaskAppeared callback", e);
}
@@ -208,8 +207,7 @@
mDeferTaskOrgCallbacksConsumer != null
? mDeferTaskOrgCallbacksConsumer
: mService.mWindowManager.mAnimator::addAfterPrepareSurfacesRunnable;
- mOrganizer = new TaskOrganizerCallbacks(mService.mWindowManager, organizer,
- deferTaskOrgCallbacksConsumer);
+ mOrganizer = new TaskOrganizerCallbacks(organizer, deferTaskOrgCallbacksConsumer);
mDeathRecipient = new DeathRecipient(organizer);
try {
organizer.asBinder().linkToDeath(mDeathRecipient, 0);
@@ -219,6 +217,18 @@
mUid = uid;
}
+ /**
+ * Register this task with this state, but doesn't trigger the task appeared callback to
+ * the organizer.
+ */
+ SurfaceControl addTaskWithoutCallback(Task t, String reason) {
+ t.mTaskAppearedSent = true;
+ if (!mOrganizedTasks.contains(t)) {
+ mOrganizedTasks.add(t);
+ }
+ return mOrganizer.prepareLeash(t, t.isVisible(), reason);
+ }
+
void addTask(Task t) {
if (t.mTaskAppearedSent) return;
@@ -265,6 +275,9 @@
}
}
+ private final ActivityTaskManagerService mService;
+ private final WindowManagerGlobalLock mGlobalLock;
+
// List of task organizers by priority
private final LinkedList<ITaskOrganizer> mTaskOrganizers = new LinkedList<>();
private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();
@@ -273,8 +286,7 @@
// Set of organized tasks (by taskId) that dispatch back pressed to their organizers
private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet();
- private final ActivityTaskManagerService mService;
-
+ private SurfaceControl.Transaction mTransaction;
private RunningTaskInfo mTmpTaskInfo;
private Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
@@ -299,7 +311,7 @@
* Register a TaskOrganizer to manage tasks as they enter the a supported windowing mode.
*/
@Override
- public void registerTaskOrganizer(ITaskOrganizer organizer) {
+ public ParceledListSlice<TaskAppearedInfo> registerTaskOrganizer(ITaskOrganizer organizer) {
enforceStackPermission("registerTaskOrganizer()");
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
@@ -307,17 +319,36 @@
synchronized (mGlobalLock) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d",
organizer.asBinder(), uid);
+
+ // Defer initializing the transaction since the transaction factory can be set up
+ // by the tests after construction of the controller
+ if (mTransaction == null) {
+ mTransaction = mService.mWindowManager.mTransactionFactory.get();
+ }
+
if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
mTaskOrganizers.add(organizer);
mTaskOrganizerStates.put(organizer.asBinder(),
new TaskOrganizerState(organizer, uid));
}
+
+ final ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>();
+ final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
mService.mRootWindowContainer.forAllTasks((task) -> {
if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, task.getWindowingMode())) {
return;
}
- task.updateTaskOrganizerState(true /* forceUpdate */);
+
+ boolean returnTask = !task.mCreatedByOrganizer;
+ task.updateTaskOrganizerState(true /* forceUpdate */,
+ returnTask /* skipTaskAppeared */);
+ if (returnTask) {
+ SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task,
+ "TaskOrganizerController.registerTaskOrganizer");
+ taskInfos.add(new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl));
+ }
});
+ return new ParceledListSlice<>(taskInfos);
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 6904740..efa0525 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -122,7 +122,6 @@
//tmp vars for unused relayout params
private static final Point sTmpSurfaceSize = new Point();
- private static final SurfaceControl sTmpSurfaceControl = new SurfaceControl();
private final Window mWindow;
private final Surface mSurface;
@@ -260,7 +259,7 @@
try {
session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
- mTempControls, sTmpSurfaceSize, sTmpSurfaceControl);
+ mTempControls, sTmpSurfaceSize);
} catch (RemoteException e) {
// Local call.
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 38ec924..0edaa1d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -861,13 +861,6 @@
}
}
- void forceWindowsScaleableInTransaction(boolean force) {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowContainer wc = mChildren.get(i);
- wc.forceWindowsScaleableInTransaction(force);
- }
- }
-
/**
* @return {@code true} when an application can override an app transition animation on this
* container.
@@ -2744,8 +2737,9 @@
pw.print(prefix); pw.println("ContainerAnimator:");
mSurfaceAnimator.dump(pw, prefix + " ");
}
- if (mLastOrientationSource != null) {
+ if (mLastOrientationSource != null && this == mDisplayContent) {
pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource);
+ pw.println(prefix + "deepestLastOrientationSource=" + getLastOrientationSource());
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 03f88a4..26655f4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2102,8 +2102,7 @@
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
- SurfaceControl outBLASTSurfaceControl) {
+ InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
Arrays.fill(outActiveControls, null);
int result = 0;
boolean configChanged;
@@ -2278,8 +2277,7 @@
result = win.relayoutVisibleWindow(result, attrChanges);
try {
- result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl,
- result, win, winAnimator);
+ result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
} catch (Exception e) {
displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
@@ -2311,7 +2309,6 @@
// surface, let the client use that, but don't create new surface at this point.
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
- winAnimator.mSurfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} else {
if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
@@ -2499,8 +2496,7 @@
return focusMayChange;
}
- private int createSurfaceControl(SurfaceControl outSurfaceControl,
- SurfaceControl outBLASTSurfaceControl, int result,
+ private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
WindowState win, WindowStateAnimator winAnimator) {
if (!win.mHasSurface) {
result |= RELAYOUT_RES_SURFACE_CHANGED;
@@ -2515,7 +2511,6 @@
}
if (surfaceController != null) {
surfaceController.getSurfaceControl(outSurfaceControl);
- surfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl);
ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl);
} else {
@@ -3712,16 +3707,63 @@
@Override
public void setFixedToUserRotation(int displayId, int fixedToUserRotation) {
if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
- "freezeRotation()")) {
+ "setFixedToUserRotation()")) {
throw new SecurityException("Requires SET_ORIENTATION permission");
}
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final DisplayContent display = mRoot.getDisplayContent(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Trying to set fixed to user rotation for a missing display.");
+ return;
+ }
+ display.getDisplayRotation().setFixedToUserRotation(fixedToUserRotation);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ int getFixedToUserRotation(int displayId) {
synchronized (mGlobalLock) {
final DisplayContent display = mRoot.getDisplayContent(displayId);
if (display == null) {
- Slog.w(TAG, "Trying to set rotate for app for a missing display.");
- return;
+ Slog.w(TAG, "Trying to get fixed to user rotation for a missing display.");
+ return -1;
}
- display.getDisplayRotation().setFixedToUserRotation(fixedToUserRotation);
+ return display.getDisplayRotation().getFixedToUserRotationMode();
+ }
+ }
+
+ @Override
+ public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
+ mAtmInternal.enforceCallerIsRecentsOrHasPermission(
+ android.Manifest.permission.SET_ORIENTATION, "setIgnoreOrientationRequest()");
+
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final DisplayContent display = mRoot.getDisplayContent(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Trying to setIgnoreOrientationRequest() for a missing display.");
+ return;
+ }
+ display.setIgnoreOrientationRequest(ignoreOrientationRequest);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ boolean getIgnoreOrientationRequest(int displayId) {
+ synchronized (mGlobalLock) {
+ final DisplayContent display = mRoot.getDisplayContent(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Trying to getIgnoreOrientationRequest() for a missing display.");
+ return false;
+ }
+ return display.getIgnoreOrientationRequest();
}
}
@@ -3810,13 +3852,24 @@
synchronized (mGlobalLock) {
final DisplayContent display = mRoot.getDisplayContent(displayId);
if (display == null) {
- Slog.w(TAG, "Trying to thaw rotation for a missing display.");
+ Slog.w(TAG, "Trying to check if rotation is frozen on a missing display.");
return false;
}
return display.getDisplayRotation().isRotationFrozen();
}
}
+ int getDisplayUserRotation(int displayId) {
+ synchronized (mGlobalLock) {
+ final DisplayContent display = mRoot.getDisplayContent(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Trying to get user rotation of a missing display.");
+ return -1;
+ }
+ return display.getDisplayRotation().getUserRotation();
+ }
+ }
+
/**
* Recalculate the current rotation.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 506e0dd..3011f25 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -28,7 +28,6 @@
import android.util.Pair;
import android.view.Display;
import android.view.IWindowManager;
-import android.view.Surface;
import android.view.ViewDebug;
import com.android.internal.os.ByteTransferPipe;
@@ -102,10 +101,14 @@
}
}
return result;
- case "set-user-rotation":
- return runSetDisplayUserRotation(pw);
- case "set-fix-to-user-rotation":
- return runSetFixToUserRotation(pw);
+ case "user-rotation":
+ return runDisplayUserRotation(pw);
+ case "fixed-to-user-rotation":
+ return runFixedToUserRotation(pw);
+ case "set-ignore-orientation-request":
+ return runSetIgnoreOrientationRequest(pw);
+ case "get-ignore-orientation-request":
+ return runGetIgnoreOrientationRequest(pw);
case "dump-visible-window-views":
return runDumpVisibleWindowViews(pw);
default:
@@ -309,28 +312,37 @@
return Integer.parseInt(s);
}
- private int runSetDisplayUserRotation(PrintWriter pw) {
- final String lockMode = getNextArgRequired();
-
+ private int runDisplayUserRotation(PrintWriter pw) {
int displayId = Display.DEFAULT_DISPLAY;
String arg = getNextArg();
+ if (arg == null) {
+ return printDisplayUserRotation(pw, displayId);
+ }
+
if ("-d".equals(arg)) {
displayId = Integer.parseInt(getNextArgRequired());
arg = getNextArg();
}
+ final String lockMode = arg;
+ if (lockMode == null) {
+ return printDisplayUserRotation(pw, displayId);
+ }
+
if ("free".equals(lockMode)) {
mInternal.thawDisplayRotation(displayId);
return 0;
}
- if (!lockMode.equals("lock")) {
- getErrPrintWriter().println("Error: lock mode needs to be either free or lock.");
+ if (!"lock".equals(lockMode)) {
+ getErrPrintWriter().println("Error: argument needs to be either -d, free or lock.");
return -1;
}
+ arg = getNextArg();
try {
- final int rotation = arg != null ? Integer.parseInt(arg) : Surface.ROTATION_0;
+ final int rotation =
+ arg != null ? Integer.parseInt(arg) : -1 /* lock to current rotation */;
mInternal.freezeDisplayRotation(displayId, rotation);
return 0;
} catch (IllegalArgumentException e) {
@@ -339,12 +351,36 @@
}
}
- private int runSetFixToUserRotation(PrintWriter pw) throws RemoteException {
+ private int printDisplayUserRotation(PrintWriter pw, int displayId) {
+ final int displayUserRotation = mInternal.getDisplayUserRotation(displayId);
+ if (displayUserRotation < 0) {
+ getErrPrintWriter().println("Error: check logcat for more details.");
+ return -1;
+ }
+ if (!mInternal.isDisplayRotationFrozen(displayId)) {
+ pw.println("free");
+ return 0;
+ }
+ pw.print("lock ");
+ pw.println(displayUserRotation);
+ return 0;
+ }
+
+ private int runFixedToUserRotation(PrintWriter pw) throws RemoteException {
int displayId = Display.DEFAULT_DISPLAY;
- String arg = getNextArgRequired();
+ String arg = getNextArg();
+ if (arg == null) {
+ printFixedToUserRotation(pw, displayId);
+ return 0;
+ }
+
if ("-d".equals(arg)) {
displayId = Integer.parseInt(getNextArgRequired());
- arg = getNextArgRequired();
+ arg = getNextArg();
+ }
+
+ if (arg == null) {
+ return printFixedToUserRotation(pw, displayId);
}
final int fixedToUserRotation;
@@ -368,6 +404,65 @@
return 0;
}
+ private int printFixedToUserRotation(PrintWriter pw, int displayId) {
+ int fixedToUserRotationMode = mInternal.getFixedToUserRotation(displayId);
+ switch (fixedToUserRotationMode) {
+ case IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT:
+ pw.println("default");
+ return 0;
+ case IWindowManager.FIXED_TO_USER_ROTATION_DISABLED:
+ pw.println("disabled");
+ return 0;
+ case IWindowManager.FIXED_TO_USER_ROTATION_ENABLED:
+ pw.println("enabled");
+ return 0;
+ default:
+ getErrPrintWriter().println("Error: check logcat for more details.");
+ return -1;
+ }
+ }
+
+ private int runSetIgnoreOrientationRequest(PrintWriter pw) throws RemoteException {
+ int displayId = Display.DEFAULT_DISPLAY;
+ String arg = getNextArgRequired();
+ if ("-d".equals(arg)) {
+ displayId = Integer.parseInt(getNextArgRequired());
+ arg = getNextArgRequired();
+ }
+
+ final boolean ignoreOrientationRequest;
+ switch (arg) {
+ case "true":
+ case "1":
+ ignoreOrientationRequest = true;
+ break;
+ case "false":
+ case "0":
+ ignoreOrientationRequest = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we "
+ + "get " + arg);
+ return -1;
+ }
+
+ mInterface.setIgnoreOrientationRequest(displayId, ignoreOrientationRequest);
+ return 0;
+ }
+
+ private int runGetIgnoreOrientationRequest(PrintWriter pw) throws RemoteException {
+ int displayId = Display.DEFAULT_DISPLAY;
+ String arg = getNextArg();
+ if ("-d".equals(arg)) {
+ displayId = Integer.parseInt(getNextArgRequired());
+ }
+
+ final boolean ignoreOrientationRequest = mInternal.getIgnoreOrientationRequest(displayId);
+ pw.println("ignoreOrientationRequest " + ignoreOrientationRequest
+ + " for displayId=" + displayId);
+ return 0;
+ }
+
private int runDumpVisibleWindowViews(PrintWriter pw) {
if (!mInternal.checkCallingPermission(android.Manifest.permission.DUMP,
"runDumpVisibleWindowViews()")) {
@@ -429,12 +524,15 @@
pw.println(" Set display scaling mode.");
pw.println(" dismiss-keyguard");
pw.println(" Dismiss the keyguard, prompting user for auth if necessary.");
- pw.println(" set-user-rotation [free|lock] [-d DISPLAY_ID] [rotation]");
- pw.println(" Set user rotation mode and user rotation.");
+ pw.println(" user-rotation [-d DISPLAY_ID] [free|lock] [rotation]");
+ pw.println(" Print or set user rotation mode and user rotation.");
pw.println(" dump-visible-window-views");
pw.println(" Dumps the encoded view hierarchies of visible windows");
- pw.println(" set-fix-to-user-rotation [-d DISPLAY_ID] [enabled|disabled]");
- pw.println(" Enable or disable rotating display for app requested orientation.");
+ pw.println(" fixed-to-user-rotation [-d DISPLAY_ID] [enabled|disabled|default]");
+ pw.println(" Print or set rotating display for app requested orientation.");
+ 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.");
if (!IS_USER) {
pw.println(" tracing (start | stop)");
pw.println(" Start or stop window tracing.");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 0b200e2..5e07f51 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -207,7 +207,7 @@
final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
entries.next();
final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
- if (!wc.isAttached()) {
+ if (wc == null || !wc.isAttached()) {
Slog.e(TAG, "Attempt to operate on detached container: " + wc);
continue;
}
@@ -380,24 +380,18 @@
return effects;
}
- private int applyTaskDisplayAreaChanges(TaskDisplayArea taskDisplayArea,
- WindowContainerTransaction.Change c) {
- int effects = applyDisplayAreaChanges(taskDisplayArea, c);
- if ((c.getChangeMask()
- & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
- if (taskDisplayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) {
- effects |= TRANSACT_EFFECTS_LIFECYCLE;
- }
- }
-
- return effects;
- }
-
- private int applyDisplayAreaChanges(WindowContainer container,
+ private int applyDisplayAreaChanges(DisplayArea displayArea,
WindowContainerTransaction.Change c) {
final int[] effects = new int[1];
- container.forAllTasks(task -> {
+ if ((c.getChangeMask()
+ & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
+ if (displayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) {
+ effects[0] |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ }
+
+ displayArea.forAllTasks(task -> {
Task tr = (Task) task;
if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
@@ -475,10 +469,8 @@
int effects = applyChanges(wc, c);
- if (wc instanceof TaskDisplayArea) {
- effects |= applyTaskDisplayAreaChanges((TaskDisplayArea) wc, c);
- } else if (wc instanceof DisplayArea) {
- effects |= applyDisplayAreaChanges(wc, c);
+ if (wc instanceof DisplayArea) {
+ effects |= applyDisplayAreaChanges(wc.asDisplayArea(), c);
} else if (wc instanceof Task) {
effects |= applyTaskChanges(wc.asTask(), c);
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index fb9bdf5..e8a7a9c1 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -223,7 +223,7 @@
private boolean mRunningRemoteAnimation;
@Nullable
- private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
+ private final BackgroundActivityStartCallback mBackgroundActivityStartCallback;
/** The state for oom-adjustment calculation. */
private final OomScoreReferenceState mOomRefState;
@@ -555,8 +555,7 @@
return true;
}
// allow if the flag was explicitly set
- if (!mBackgroundActivityStartTokens.isEmpty()) {
- onBackgroundStartAllowedByToken();
+ if (isBackgroundStartAllowedByToken()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[WindowProcessController(" + mPid
+ ")] Activity start allowed: process allowed by token");
@@ -566,18 +565,29 @@
return false;
}
- private void onBackgroundStartAllowedByToken() {
+ /**
+ * If there are no tokens, we don't allow *by token*. If there are tokens, we need to check if
+ * the callback handles all the tokens, if so we ask the callback if the activity should be
+ * started, otherwise we allow.
+ */
+ private boolean isBackgroundStartAllowedByToken() {
+ if (mBackgroundActivityStartTokens.isEmpty()) {
+ return false;
+ }
if (mBackgroundActivityStartCallback == null) {
- return;
+ // We have tokens but no callback to decide => allow
+ return true;
}
IBinder callbackToken = mBackgroundActivityStartCallback.getToken();
for (IBinder token : mBackgroundActivityStartTokens.values()) {
if (token != callbackToken) {
- return;
+ // The callback doesn't handle all the tokens => allow
+ return true;
}
}
- mAtm.mH.post(() ->
- mBackgroundActivityStartCallback.onExclusiveTokenActivityStart(mInfo.packageName));
+ // The callback handles all the tokens => callback decides
+ return mBackgroundActivityStartCallback.isActivityStartAllowed(mInfo.uid,
+ mInfo.packageName);
}
private boolean isBoundByForegroundUid() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0b53bf6..25b4828 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2172,16 +2172,7 @@
}
}
- @Override
- void forceWindowsScaleableInTransaction(boolean force) {
- if (mWinAnimator != null && mWinAnimator.hasSurface()) {
- mWinAnimator.mSurfaceController.forceScaleableInTransaction(force);
- }
-
- super.forceWindowsScaleableInTransaction(force);
- }
-
- @Override
+ @Override
void removeImmediately() {
super.removeImmediately();
@@ -5779,7 +5770,7 @@
}
SurfaceControl getClientViewRootSurface() {
- return mWinAnimator.getClientViewRootSurface();
+ return mWinAnimator.getSurfaceControl();
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index f342976..972d0d4 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -359,8 +359,8 @@
// surface before destroying it.
if (mSurfaceController != null && mPendingDestroySurface != null) {
mPostDrawTransaction.reparentChildren(
- mSurfaceController.getClientViewRootSurface(),
- mPendingDestroySurface.getClientViewRootSurface()).apply();
+ mSurfaceController.mSurfaceControl,
+ mPendingDestroySurface.mSurfaceControl).apply();
}
destroySurfaceLocked();
mSurfaceDestroyDeferred = true;
@@ -371,7 +371,7 @@
// Our SurfaceControl is always at layer 0 within the parent Surface managed by
// window-state. We want this old Surface to stay on top of the new one
// until we do the swap, so we place it at a positive layer.
- t.setLayer(mSurfaceController.getClientViewRootSurface(), PRESERVED_SURFACE_LAYER);
+ t.setLayer(mSurfaceController.mSurfaceControl, PRESERVED_SURFACE_LAYER);
}
mDestroyPreservedSurfaceUponRedraw = true;
mSurfaceDestroyDeferred = true;
@@ -393,8 +393,8 @@
&& !mPendingDestroySurface.mChildrenDetached
&& (mWin.mActivityRecord == null || !mWin.mActivityRecord.isRelaunching())) {
mPostDrawTransaction.reparentChildren(
- mPendingDestroySurface.getClientViewRootSurface(),
- mSurfaceController.getClientViewRootSurface()).apply();
+ mPendingDestroySurface.mSurfaceControl,
+ mSurfaceController.mSurfaceControl).apply();
}
destroyDeferredSurfaceLocked();
@@ -971,10 +971,6 @@
* @return Returns true if the surface was successfully shown.
*/
private boolean showSurfaceRobustlyLocked() {
- if (mWin.getWindowConfiguration().windowsAreScaleable()) {
- mSurfaceController.forceScaleableInTransaction(true);
- }
-
boolean shown = mSurfaceController.showRobustlyInTransaction();
if (!shown)
return false;
@@ -988,8 +984,8 @@
// Instead let the children get removed when the old surface is deleted.
if (!mPendingDestroySurface.mChildrenDetached) {
mPostDrawTransaction.reparentChildren(
- mPendingDestroySurface.getClientViewRootSurface(),
- mSurfaceController.getClientViewRootSurface());
+ mPendingDestroySurface.mSurfaceControl,
+ mSurfaceController.mSurfaceControl);
}
}
@@ -1205,10 +1201,10 @@
mOffsetPositionForStackResize = offsetPositionForStackResize;
}
- SurfaceControl getClientViewRootSurface() {
+ SurfaceControl getSurfaceControl() {
if (!hasSurface()) {
return null;
}
- return mSurfaceController.getClientViewRootSurface();
+ return mSurfaceController.mSurfaceControl;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 160ad89..feecda7 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -30,7 +30,6 @@
import static com.android.server.wm.WindowSurfaceControllerProto.LAYER;
import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
-import android.graphics.Rect;
import android.graphics.Region;
import android.os.Debug;
import android.os.Trace;
@@ -51,18 +50,12 @@
SurfaceControl mSurfaceControl;
- /**
- * WM only uses for deferred transactions.
- */
- SurfaceControl mBLASTSurfaceControl;
-
// Should only be set from within setShown().
private boolean mSurfaceShown = false;
private float mSurfaceX = 0;
private float mSurfaceY = 0;
private int mSurfaceW = 0;
private int mSurfaceH = 0;
- private Rect mSurfaceCrop = new Rect(0, 0, -1, -1);
// Initialize to the identity matrix.
private float mLastDsdx = 1;
@@ -74,13 +67,6 @@
private int mSurfaceLayer = 0;
- // Surface flinger doesn't support crop rectangles where width or height is non-positive.
- // However, we need to somehow handle the situation where the cropping would completely hide
- // the window. We achieve this by explicitly hiding the surface and not letting it be shown.
- private boolean mHiddenForCrop = false;
-
- // Initially a surface is hidden after just being created.
- private boolean mHiddenForOtherReasons = true;
private final String title;
private final WindowManagerService mService;
@@ -121,32 +107,16 @@
final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags &
WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
+
if (useBLAST) {
- b.setContainerLayer();
+ b.setBLASTLayer();
}
mSurfaceControl = b.build();
- if (useBLAST) {
- mBLASTSurfaceControl = win.makeSurface()
- .setParent(mSurfaceControl)
- .setName(name + "(BLAST)")
- .setHidden(false)
- .setBLASTLayer()
- .setCallsite("WindowSurfaceController")
- .build();
- }
-
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- void reparentChildrenInTransaction(WindowSurfaceController other) {
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "REPARENT from: %s to: %s", this, other);
- if ((mSurfaceControl != null) && (other.mSurfaceControl != null)) {
- mSurfaceControl.reparentChildren(other.mSurfaceControl);
- }
- }
-
void detachChildren() {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SEVER CHILDREN");
mChildrenDetached = true;
@@ -157,7 +127,6 @@
void hide(SurfaceControl.Transaction transaction, String reason) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE HIDE ( %s ): %s", reason, title);
- mHiddenForOtherReasons = true;
mAnimator.destroyPreservedSurfaceLocked();
if (mSurfaceShown) {
@@ -189,50 +158,6 @@
} finally {
setShown(false);
mSurfaceControl = null;
- if (mBLASTSurfaceControl != null) {
- mBLASTSurfaceControl.release();
- }
- }
- }
-
- void setCropInTransaction(Rect clipRect, boolean recoveringMemory) {
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE CROP %s: %s", clipRect.toShortString(), title);
- try {
- if (clipRect.width() > 0 && clipRect.height() > 0) {
- if (!clipRect.equals(mSurfaceCrop)) {
- mSurfaceControl.setWindowCrop(clipRect);
- mSurfaceCrop.set(clipRect);
- }
- mHiddenForCrop = false;
- updateVisibility();
- } else {
- mHiddenForCrop = true;
- mAnimator.destroyPreservedSurfaceLocked();
- updateVisibility();
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error setting crop surface of " + this
- + " crop=" + clipRect.toShortString(), e);
- if (!recoveringMemory) {
- mAnimator.reclaimSomeSurfaceMemory("crop", true);
- }
- }
- }
-
- void clearCropInTransaction(boolean recoveringMemory) {
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE CLEAR CROP: %s", title);
- try {
- Rect clipRect = new Rect(0, 0, -1, -1);
- if (mSurfaceCrop.equals(clipRect)) {
- return;
- }
- mSurfaceControl.setWindowCrop(clipRect);
- mSurfaceCrop.set(clipRect);
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error setting clearing crop of " + this, e);
- if (!recoveringMemory) {
- mAnimator.reclaimSomeSurfaceMemory("crop", true);
- }
}
}
@@ -287,31 +212,6 @@
}
}
- boolean setBufferSizeInTransaction(int width, int height, boolean recoveringMemory) {
- final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
- if (surfaceResized) {
- mSurfaceW = width;
- mSurfaceH = height;
-
- try {
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SIZE %dx%d: %s", width, height, title);
- mSurfaceControl.setBufferSize(width, height);
- } catch (RuntimeException e) {
- // If something goes wrong with the surface (such
- // as running out of memory), don't take down the
- // entire system.
- Slog.e(TAG, "Error resizing surface of " + title
- + " size=(" + width + "x" + height + ")", e);
- if (!recoveringMemory) {
- mAnimator.reclaimSomeSurfaceMemory("size", true);
- }
- return false;
- }
- return true;
- }
- return false;
- }
-
boolean prepareToShowInTransaction(float alpha,
float dsdx, float dtdx, float dsdy,
float dtdy, boolean recoveringMemory) {
@@ -404,35 +304,15 @@
}
}
- void getContainerRect(Rect rect) {
- mAnimator.getContainerRect(rect);
- }
-
boolean showRobustlyInTransaction() {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SHOW (performLayout): %s", title);
if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
+ " during relayout");
- mHiddenForOtherReasons = false;
- return updateVisibility();
- }
- private boolean updateVisibility() {
- if (mHiddenForCrop || mHiddenForOtherReasons) {
- if (mSurfaceShown) {
- hideSurface(mTmpTransaction);
- SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
- }
- return false;
- } else {
- if (!mSurfaceShown) {
- return showSurface();
- } else {
- return true;
- }
+ if (mSurfaceShown) {
+ return true;
}
- }
- private boolean showSurface() {
try {
setShown(true);
mSurfaceControl.show();
@@ -442,7 +322,6 @@
}
mAnimator.reclaimSomeSurfaceMemory("show", true);
-
return false;
}
@@ -451,13 +330,6 @@
mSurfaceControl.deferTransactionUntil(barrier, frame);
}
- void forceScaleableInTransaction(boolean force) {
- // -1 means we don't override the default or client specified
- // scaling mode.
- int scalingMode = force ? SCALING_MODE_SCALE_TO_WINDOW : -1;
- mSurfaceControl.setOverrideScalingMode(scalingMode);
- }
-
boolean clearWindowContentFrameStats() {
if (mSurfaceControl == null) {
return false;
@@ -472,7 +344,6 @@
return mSurfaceControl.getContentFrameStats(outStats);
}
-
boolean hasSurface() {
return mSurfaceControl != null;
}
@@ -481,16 +352,6 @@
outSurfaceControl.copyFrom(mSurfaceControl, "WindowSurfaceController.getSurfaceControl");
}
- void getBLASTSurfaceControl(SurfaceControl outSurfaceControl) {
- if (mBLASTSurfaceControl != null) {
- outSurfaceControl.copyFrom(mBLASTSurfaceControl, "WindowSurfaceController.getBLASTSurfaceControl");
- }
- }
-
- int getLayer() {
- return mSurfaceLayer;
- }
-
boolean getShown() {
return mSurfaceShown;
}
@@ -507,14 +368,6 @@
}
}
- float getX() {
- return mSurfaceX;
- }
-
- float getY() {
- return mSurfaceY;
- }
-
int getWidth() {
return mSurfaceW;
}
@@ -523,21 +376,6 @@
return mSurfaceH;
}
- /**
- * Returns the Surface which the client-framework ViewRootImpl will be using.
- * This is either the WSA SurfaceControl or it's BLAST child surface.
- * This has too main uses:
- * 1. This is the Surface the client will add children to, we use this to make
- * sure we don't reparent the BLAST surface itself when calling reparentChildren
- * 2. We use this as the barrier Surface for some deferTransaction operations.
- */
- SurfaceControl getClientViewRootSurface() {
- if (mBLASTSurfaceControl != null) {
- return mBLASTSurfaceControl;
- }
- return mSurfaceControl;
- }
-
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(SHOWN, mSurfaceShown);
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 91645ba..31b9ad6 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -156,7 +156,6 @@
using android::hardware::hidl_string;
using android::hardware::hidl_death_recipient;
-using android::hardware::gnss::PsdsType;
using android::hardware::gnss::V1_0::GnssLocationFlags;
using android::hardware::gnss::V1_0::IAGnssRilCallback;
using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
@@ -228,9 +227,13 @@
using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl;
using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControlCallback;
+using android::hardware::gnss::BlocklistedSource;
+using android::hardware::gnss::GnssConstellationType;
+using android::hardware::gnss::PsdsType;
using IGnssAidl = android::hardware::gnss::IGnss;
using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
using IGnssPsdsCallbackAidl = android::hardware::gnss::IGnssPsdsCallback;
+using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration;
struct GnssDeathRecipient : virtual public hidl_death_recipient
{
@@ -266,6 +269,7 @@
sp<IGnssBatching_V2_0> gnssBatchingIface_V2_0 = nullptr;
sp<IGnssDebug_V1_0> gnssDebugIface = nullptr;
sp<IGnssDebug_V2_0> gnssDebugIface_V2_0 = nullptr;
+sp<IGnssConfigurationAidl> gnssConfigurationAidlIface = nullptr;
sp<IGnssConfiguration_V1_0> gnssConfigurationIface = nullptr;
sp<IGnssConfiguration_V1_1> gnssConfigurationIface_V1_1 = nullptr;
sp<IGnssConfiguration_V2_0> gnssConfigurationIface_V2_0 = nullptr;
@@ -455,16 +459,11 @@
}
}
-static jboolean checkAidlStatus(const Status& status, const char* errorMessage,
- const bool success) {
+static jboolean checkAidlStatus(const Status& status, const char* errorMessage) {
if (!status.isOk()) {
ALOGE("%s AIDL transport error: %s", errorMessage, status.toString8().c_str());
return JNI_FALSE;
}
- if (!success) {
- ALOGE("AIDL return failure: %s", errorMessage);
- return JNI_FALSE;
- }
return JNI_TRUE;
}
@@ -2371,7 +2370,15 @@
gnssNiIface = gnssNi;
}
- if (gnssHal_V2_1 != nullptr) {
+ if (gnssHalAidl != nullptr) {
+ sp<IGnssConfigurationAidl> gnssConfigurationAidl;
+ auto status = gnssHalAidl->getExtensionGnssConfiguration(&gnssConfigurationAidl);
+ if (status.isOk()) {
+ gnssConfigurationAidlIface = gnssConfigurationAidl;
+ } else {
+ ALOGD("Unable to get a handle to GnssConfiguration AIDL interface.");
+ }
+ } else if (gnssHal_V2_1 != nullptr) {
auto gnssConfiguration = gnssHal_V2_1->getExtensionGnssConfiguration_2_1();
if (!gnssConfiguration.isOk()) {
ALOGD("Unable to get a handle to GnssConfiguration_V2_1");
@@ -2519,9 +2526,8 @@
// Set IGnssPsds or IGnssXtra callback.
if (gnssPsdsAidlIface != nullptr) {
sp<IGnssPsdsCallbackAidl> gnssPsdsCallbackAidl = new GnssPsdsCallbackAidl();
- bool success;
- auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl, &success);
- if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.", success)) {
+ auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl);
+ if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.")) {
gnssPsdsAidlIface = nullptr;
}
} else {
@@ -2814,13 +2820,11 @@
jbyte* bytes = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(data, 0));
if (gnssPsdsAidlIface != nullptr) {
- bool success;
auto status = gnssPsdsAidlIface->injectPsdsData(static_cast<PsdsType>(psdsType),
std::vector<uint8_t>((const uint8_t*)bytes,
(const uint8_t*)bytes +
- length),
- &success);
- checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.", success);
+ length));
+ checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.");
} else if (gnssPsdsIface != nullptr) {
auto result = gnssPsdsIface->injectPsdsData_3_0(psdsType,
std::string((const char*)bytes, length));
@@ -3482,11 +3486,16 @@
static jboolean android_location_GnssConfiguration_set_emergency_supl_pdn(JNIEnv*,
jobject,
jint emergencySuplPdn) {
- if (gnssConfigurationIface == nullptr) {
+ if (gnssConfigurationIface == nullptr && gnssConfigurationAidlIface == nullptr) {
ALOGE("%s: IGnssConfiguration interface not available.", __func__);
return JNI_FALSE;
}
+ if (gnssConfigurationAidlIface != nullptr) {
+ auto status = gnssConfigurationAidlIface->setEmergencySuplPdn(emergencySuplPdn);
+ return checkAidlStatus(status, "gnssConfigurationAidlIface setEmergencySuplPdn() failed.");
+ }
+
auto result = gnssConfigurationIface->setEmergencySuplPdn(emergencySuplPdn);
return checkHidlReturn(result, "IGnssConfiguration setEmergencySuplPdn() failed.");
}
@@ -3494,10 +3503,16 @@
static jboolean android_location_GnssConfiguration_set_supl_version(JNIEnv*,
jobject,
jint version) {
- if (gnssConfigurationIface == nullptr) {
+ if (gnssConfigurationIface == nullptr && gnssConfigurationAidlIface == nullptr) {
ALOGE("%s: IGnssConfiguration interface not available.", __func__);
return JNI_FALSE;
}
+
+ if (gnssConfigurationAidlIface != nullptr) {
+ auto status = gnssConfigurationAidlIface->setSuplVersion(version);
+ return checkAidlStatus(status, "gnssConfigurationAidlIface setSuplVersion() failed.");
+ }
+
auto result = gnssConfigurationIface->setSuplVersion(version);
return checkHidlReturn(result, "IGnssConfiguration setSuplVersion() failed.");
}
@@ -3505,7 +3520,8 @@
static jboolean android_location_GnssConfiguration_set_supl_es(JNIEnv*,
jobject,
jint suplEs) {
- if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr) {
+ if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr ||
+ gnssConfigurationAidlIface != nullptr) {
ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0 and higher.");
return JNI_FALSE;
}
@@ -3522,11 +3538,16 @@
static jboolean android_location_GnssConfiguration_set_supl_mode(JNIEnv*,
jobject,
jint mode) {
- if (gnssConfigurationIface == nullptr) {
+ if (gnssConfigurationIface == nullptr && gnssConfigurationAidlIface == nullptr) {
ALOGE("%s: IGnssConfiguration interface not available.", __func__);
return JNI_FALSE;
}
+ if (gnssConfigurationAidlIface != nullptr) {
+ auto status = gnssConfigurationAidlIface->setSuplMode(mode);
+ return checkAidlStatus(status, "gnssConfigurationAidlIface setSuplMode() failed.");
+ }
+
auto result = gnssConfigurationIface->setSuplMode(mode);
return checkHidlReturn(result, "IGnssConfiguration setSuplMode() failed.");
}
@@ -3534,7 +3555,8 @@
static jboolean android_location_GnssConfiguration_set_gps_lock(JNIEnv*,
jobject,
jint gpsLock) {
- if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr) {
+ if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr ||
+ gnssConfigurationAidlIface != nullptr) {
ALOGI("Config parameter GPS_LOCK is deprecated in IGnssConfiguration.hal version 2.0.");
return JNI_FALSE;
}
@@ -3551,11 +3573,16 @@
static jboolean android_location_GnssConfiguration_set_lpp_profile(JNIEnv*,
jobject,
jint lppProfile) {
- if (gnssConfigurationIface == nullptr) {
+ if (gnssConfigurationIface == nullptr && gnssConfigurationAidlIface == nullptr) {
ALOGE("%s: IGnssConfiguration interface not available.", __func__);
return JNI_FALSE;
}
+ if (gnssConfigurationAidlIface != nullptr) {
+ auto status = gnssConfigurationAidlIface->setLppProfile(lppProfile);
+ return checkAidlStatus(status, "gnssConfigurationAidlIface setLppProfile() failed.");
+ }
+
auto result = gnssConfigurationIface->setLppProfile(lppProfile);
return checkHidlReturn(result, "IGnssConfiguration setLppProfile() failed.");
}
@@ -3563,18 +3590,26 @@
static jboolean android_location_GnssConfiguration_set_gnss_pos_protocol_select(JNIEnv*,
jobject,
jint gnssPosProtocol) {
- if (gnssConfigurationIface == nullptr) {
+ if (gnssConfigurationIface == nullptr && gnssConfigurationAidlIface == nullptr) {
ALOGE("%s: IGnssConfiguration interface not available.", __func__);
return JNI_FALSE;
}
+ if (gnssConfigurationAidlIface != nullptr) {
+ auto status = gnssConfigurationAidlIface->setGlonassPositioningProtocol(gnssPosProtocol);
+ return checkAidlStatus(status,
+ "gnssConfigurationAidlIface setGlonassPositioningProtocol() "
+ "failed.");
+ }
+
auto result = gnssConfigurationIface->setGlonassPositioningProtocol(gnssPosProtocol);
return checkHidlReturn(result, "IGnssConfiguration setGlonassPositioningProtocol() failed.");
}
static jboolean android_location_GnssConfiguration_set_satellite_blacklist(
JNIEnv* env, jobject, jintArray constellations, jintArray sv_ids) {
- if (gnssConfigurationIface_V1_1 == nullptr && gnssConfigurationIface_V2_1 == nullptr) {
+ if (gnssConfigurationIface_V1_1 == nullptr && gnssConfigurationIface_V2_1 == nullptr &&
+ gnssConfigurationAidlIface == nullptr) {
ALOGI("IGnssConfiguration interface does not support satellite blacklist.");
return JNI_FALSE;
}
@@ -3597,7 +3632,18 @@
return JNI_FALSE;
}
- if (gnssConfigurationIface_V2_1 != nullptr) {
+ if (gnssConfigurationAidlIface != nullptr) {
+ std::vector<BlocklistedSource> sources;
+ sources.resize(length);
+
+ for (int i = 0; i < length; i++) {
+ sources[i].constellation = static_cast<GnssConstellationType>(constellation_array[i]);
+ sources[i].svid = sv_id_array[i];
+ }
+
+ auto status = gnssConfigurationAidlIface->setBlocklist(sources);
+ return checkAidlStatus(status, "gnssConfigurationAidlIface setBlocklist() failed.");
+ } else if (gnssConfigurationIface_V2_1 != nullptr) {
hidl_vec<IGnssConfiguration_V2_1::BlacklistedSource> sources;
sources.resize(length);
@@ -3624,17 +3670,17 @@
static jboolean android_location_GnssConfiguration_set_es_extension_sec(
JNIEnv*, jobject, jint emergencyExtensionSeconds) {
- if (gnssConfigurationIface == nullptr) {
- ALOGE("%s: IGnssConfiguration interface not available.", __func__);
- return JNI_FALSE;
- }
-
- if (gnssConfigurationIface_V2_0 == nullptr) {
+ if (gnssConfigurationIface_V2_0 == nullptr && gnssConfigurationAidlIface == nullptr) {
ALOGI("Config parameter ES_EXTENSION_SEC is not supported in IGnssConfiguration.hal"
" versions earlier than 2.0.");
return JNI_FALSE;
}
+ if (gnssConfigurationAidlIface != nullptr) {
+ auto status = gnssConfigurationAidlIface->setEsExtensionSec(emergencyExtensionSeconds);
+ return checkAidlStatus(status, "gnssConfigurationAidlIface setEsExtensionSec() failed.");
+ }
+
auto result = gnssConfigurationIface_V2_0->setEsExtensionSec(emergencyExtensionSeconds);
return checkHidlReturn(result, "IGnssConfiguration setEsExtensionSec() failed.");
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 36ff974..177b7b4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1569,10 +1569,15 @@
}
/**
- * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no
- * component name is provided, look up the component name and fill it in for the caller.
+ * Creates a new {@link CallerIdentity} object to represent the caller's identity, which should
+ * be an admin of a profile on the device. If no component name is provided, look up the
+ * component name and fill it in for the caller.
+ *
+ * Note: this method should only be called when the expected caller is an admin.
+ *
+ * @throws SecurityException if the caller is not an active admin.
*/
- private CallerIdentity getCallerIdentityOptionalAdmin(@Nullable ComponentName adminComponent) {
+ private CallerIdentity getAdminCallerIdentity(@Nullable ComponentName adminComponent) {
if (adminComponent == null) {
ActiveAdmin admin = getActiveAdminOfCaller();
if (admin != null) {
@@ -1586,24 +1591,66 @@
/**
* Creates a new {@link CallerIdentity} object to represent the caller's identity. If no
- * package name is provided, look up the package name and fill it in for the caller.
+ * component name is provided, look up the component name and fill it in for the caller.
+ *
+ * Note: this method should only be called when the caller may not be an admin. If the caller
+ * is not an admin, the ComponentName in the returned identity will be null.
*/
- private CallerIdentity getCallerIdentityOptionalPackage(@Nullable String callerPackage) {
+ private CallerIdentity getNonPrivilegedOrAdminCallerIdentity(
+ @Nullable ComponentName adminComponent) {
+ if (adminComponent == null) {
+ ActiveAdmin admin = getActiveAdminOfCaller();
+ if (admin != null) {
+ adminComponent = admin.info.getComponent();
+ } else {
+ return getCallerIdentity();
+ }
+ }
+ return getCallerIdentity(adminComponent);
+
+ }
+
+ /**
+ * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no
+ * package name is provided, look up the package name and fill it in for the caller.
+ *
+ * Note: this method should only be called when the expected caller is an admin.
+ *
+ * @throws SecurityException if the caller is not an active admin.
+ */
+ private CallerIdentity getAdminCallerIdentityUsingPackage(@Nullable String callerPackage) {
if (callerPackage == null) {
ActiveAdmin admin = getActiveAdminOfCaller();
if (admin != null) {
return getCallerIdentity(admin.info.getPackageName());
}
throw new SecurityException("Caller is not an active admin");
- } else {
- return getCallerIdentity(callerPackage);
}
+ return getCallerIdentity(callerPackage);
+ }
+
+ /**
+ * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no
+ * package name is provided, look up the package name and fill it in for the caller.
+ */
+ private CallerIdentity getNonPrivilegedOrAdminCallerIdentityUsingPackage(
+ @Nullable String callerPackage) {
+ if (callerPackage == null) {
+ ActiveAdmin admin = getActiveAdminOfCaller();
+ if (admin != null) {
+ callerPackage = admin.info.getPackageName();
+ } else {
+ return getCallerIdentity();
+ }
+ }
+ return getCallerIdentity(callerPackage);
}
/**
* Retrieves the active admin of the caller. This method should not be called directly and
- * should only be called by {@link #getCallerIdentityOptionalAdmin} or
- * {@link #getCallerIdentityOptionalPackage}.
+ * should only be called by {@link #getAdminCallerIdentity},
+ * {@link #getNonPrivilegedOrAdminCallerIdentity}, {@link #getAdminCallerIdentityUsingPackage}
+ * or {@link #getNonPrivilegedOrAdminCallerIdentityUsingPackage}.
*/
private ActiveAdmin getActiveAdminOfCaller() {
final int callerUid = mInjector.binderGetCallingUid();
@@ -6023,7 +6070,7 @@
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = getCallerIdentityOptionalAdmin(comp);
+ final CallerIdentity caller = getAdminCallerIdentity(comp);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
@@ -6463,12 +6510,12 @@
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = getCallerIdentityOptionalAdmin(who);
+ final CallerIdentity caller = getAdminCallerIdentity(who);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
synchronized (getLockObject()) {
// Check for permissions if a particular caller is specified
- if (who != null) {
+ if (caller.hasAdminComponent()) {
// When checking for a single caller, status is based on caller's request
ActiveAdmin ap = getActiveAdminUncheckedLocked(who, userHandle);
return ap != null ? ap.encryptionRequested : false;
@@ -6497,7 +6544,7 @@
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = getCallerIdentityOptionalPackage(callerPackage);
+ final CallerIdentity caller = getAdminCallerIdentityUsingPackage(callerPackage);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
// It's not critical here, but let's make sure the package name is correct, in case
@@ -8614,12 +8661,12 @@
Objects.requireNonNull(agent, "agent null");
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = getCallerIdentityOptionalAdmin(admin);
+ final CallerIdentity caller = getAdminCallerIdentity(admin);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
synchronized (getLockObject()) {
final String componentName = agent.flattenToString();
- if (admin != null) {
+ if (caller.hasAdminComponent()) {
final ActiveAdmin ap = getActiveAdminUncheckedLocked(admin, userHandle, parent);
if (ap == null) return null;
TrustAgentInfo trustAgentInfo = ap.trustAgentInfos.get(componentName);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 275d8f1..975e226 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -47,7 +47,9 @@
import android.database.sqlite.SQLiteGlobal;
import android.graphics.GraphicsStatsService;
import android.hardware.display.DisplayManagerInternal;
+import android.net.ConnectivityManager;
import android.net.ConnectivityModuleConnector;
+import android.net.IConnectivityManager;
import android.net.NetworkStackClient;
import android.os.BaseBundle;
import android.os.Binder;
@@ -103,7 +105,6 @@
import com.android.server.clipboard.ClipboardService;
import com.android.server.compat.PlatformCompat;
import com.android.server.compat.PlatformCompatNative;
-import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.coverage.CoverageService;
import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -320,6 +321,10 @@
"com.android.server.media.MediaSessionService";
private static final String MEDIA_RESOURCE_MONITOR_SERVICE_CLASS =
"com.android.server.media.MediaResourceMonitorService";
+ private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS =
+ "com.android.server.ConnectivityServiceInitializer";
+ private static final String IP_CONNECTIVITY_METRICS_CLASS =
+ "com.android.server.connectivity.IpConnectivityMetrics";
private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
@@ -1038,7 +1043,7 @@
IpSecService ipSecService = null;
NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
- ConnectivityService connectivity = null;
+ IConnectivityManager connectivity = null;
NsdService serviceDiscovery = null;
WindowManagerService wm = null;
SerialService serial = null;
@@ -1228,7 +1233,7 @@
}
t.traceBegin("IpConnectivityMetrics");
- mSystemServiceManager.startService(IpConnectivityMetrics.class);
+ mSystemServiceManager.startService(IP_CONNECTIVITY_METRICS_CLASS);
t.traceEnd();
t.traceBegin("NetworkWatchlistService");
@@ -1572,16 +1577,15 @@
}
t.traceBegin("StartConnectivityService");
- try {
- connectivity = new ConnectivityService(
- context, networkManagement, networkStats, networkPolicy);
- ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity,
- /* allowIsolated= */ false,
- DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
- networkPolicy.bindConnectivityManager(connectivity);
- } catch (Throwable e) {
- reportWtf("starting Connectivity Service", e);
- }
+ // This has to be called after NetworkManagementService, NetworkStatsService
+ // and NetworkPolicyManager because ConnectivityService needs to take these
+ // services to initialize.
+ // TODO: Dynamically load service-connectivity.jar by using startServiceFromJar.
+ mSystemServiceManager.startService(CONNECTIVITY_SERVICE_INITIALIZER_CLASS);
+ connectivity = IConnectivityManager.Stub.asInterface(
+ ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+ // TODO: Use ConnectivityManager instead of ConnectivityService.
+ networkPolicy.bindConnectivityManager(connectivity);
t.traceEnd();
t.traceBegin("StartNsdService");
@@ -2270,7 +2274,6 @@
final NetworkManagementService networkManagementF = networkManagement;
final NetworkStatsService networkStatsF = networkStats;
final NetworkPolicyManagerService networkPolicyF = networkPolicy;
- final ConnectivityService connectivityF = connectivity;
final CountryDetectorService countryDetectorF = countryDetector;
final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
final InputManagerService inputManagerF = inputManager;
@@ -2279,6 +2282,8 @@
final MmsServiceBroker mmsServiceF = mmsService;
final IpSecService ipSecServiceF = ipSecService;
final WindowManagerService windowManagerF = wm;
+ final ConnectivityManager connectivityF = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
diff --git a/services/net/TEST_MAPPING b/services/net/TEST_MAPPING
new file mode 100644
index 0000000..7025dd1
--- /dev/null
+++ b/services/net/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "frameworks/base/core/java/android/net"
+ }
+ ]
+}
\ No newline at end of file
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 87f2c58..7803e78 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -1037,14 +1037,15 @@
* A {@link Runnable} that queries the Usage Stats Service for recent events for a specified
* user.
*/
- private class UsageStatsQueryRunnable implements Runnable {
+ private class UsageStatsQueryRunnable implements Runnable,
+ UsageStatsQueryHelper.EventListener {
private final UsageStatsQueryHelper mUsageStatsQueryHelper;
private long mLastEventTimestamp;
private UsageStatsQueryRunnable(int userId) {
mUsageStatsQueryHelper = mInjector.createUsageStatsQueryHelper(userId,
- (packageName) -> getPackage(packageName, userId));
+ (packageName) -> getPackage(packageName, userId), this);
mLastEventTimestamp = System.currentTimeMillis() - QUERY_EVENTS_MAX_AGE_MS;
}
@@ -1054,6 +1055,17 @@
mLastEventTimestamp = mUsageStatsQueryHelper.getLastEventTimestamp();
}
}
+
+ @Override
+ public void onEvent(PackageData packageData, ConversationInfo conversationInfo,
+ Event event) {
+ if (event.getType() == Event.TYPE_IN_APP_CONVERSATION) {
+ ConversationInfo updated = new ConversationInfo.Builder(conversationInfo)
+ .setLastEventTimestamp(event.getTimestamp())
+ .build();
+ packageData.getConversationStore().addOrUpdate(updated);
+ }
+ }
}
/** A {@link BroadcastReceiver} that receives the intents for a specified user. */
@@ -1135,8 +1147,9 @@
}
UsageStatsQueryHelper createUsageStatsQueryHelper(@UserIdInt int userId,
- Function<String, PackageData> packageDataGetter) {
- return new UsageStatsQueryHelper(userId, packageDataGetter);
+ Function<String, PackageData> packageDataGetter,
+ UsageStatsQueryHelper.EventListener eventListener) {
+ return new UsageStatsQueryHelper(userId, packageDataGetter, eventListener);
}
}
}
diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
index d008b72..aefb6e3 100644
--- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
@@ -43,17 +43,24 @@
private final Function<String, PackageData> mPackageDataGetter;
// Activity name -> Conversation start event (LOCUS_ID_SET)
private final Map<ComponentName, UsageEvents.Event> mConvoStartEvents = new ArrayMap<>();
+ private final EventListener mEventListener;
private long mLastEventTimestamp;
+ interface EventListener {
+ void onEvent(PackageData packageData, ConversationInfo conversationInfo, Event event);
+ }
+
/**
* @param userId The user whose events are to be queried.
* @param packageDataGetter The function to get {@link PackageData} with a package name.
+ * @param eventListener A listener that listens to the new event.
*/
UsageStatsQueryHelper(@UserIdInt int userId,
- Function<String, PackageData> packageDataGetter) {
+ Function<String, PackageData> packageDataGetter, EventListener eventListener) {
mUsageStatsManagerInternal = getUsageStatsManagerInternal();
mUserId = userId;
mPackageDataGetter = packageDataGetter;
+ mEventListener = eventListener;
}
/**
@@ -198,21 +205,27 @@
}
private void addEventByShortcutId(PackageData packageData, String shortcutId, Event event) {
- if (packageData.getConversationStore().getConversation(shortcutId) == null) {
+ ConversationInfo conversationInfo =
+ packageData.getConversationStore().getConversation(shortcutId);
+ if (conversationInfo == null) {
return;
}
EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
eventHistory.addEvent(event);
+ mEventListener.onEvent(packageData, conversationInfo, event);
}
private void addEventByLocusId(PackageData packageData, LocusId locusId, Event event) {
- if (packageData.getConversationStore().getConversationByLocusId(locusId) == null) {
+ ConversationInfo conversationInfo =
+ packageData.getConversationStore().getConversationByLocusId(locusId);
+ if (conversationInfo == null) {
return;
}
EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
EventStore.CATEGORY_LOCUS_ID_BASED, locusId.getId());
eventHistory.addEvent(event);
+ mEventListener.onEvent(packageData, conversationInfo, event);
}
private static UsageStatsManagerInternal getUsageStatsManagerInternal() {
diff --git a/services/profcollect/Android.bp b/services/profcollect/Android.bp
index 68fba55..b7be5d4 100644
--- a/services/profcollect/Android.bp
+++ b/services/profcollect/Android.bp
@@ -30,6 +30,7 @@
java_library_static {
name: "services.profcollect",
+ defaults: ["services_defaults"],
srcs: [":services.profcollect-sources"],
libs: ["services.core"],
}
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 09e3bfe..7d17109 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -41,6 +41,7 @@
import static com.android.server.backup.testing.Utils.transferStreamedData;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -2880,8 +2881,8 @@
}
private static IterableSubject assertDirectory(Path directory) throws IOException {
- return assertThat(oneTimeIterable(Files.newDirectoryStream(directory).iterator()))
- .named("directory " + directory);
+ return assertWithMessage("directory " + directory).that(
+ oneTimeIterable(Files.newDirectoryStream(directory).iterator()));
}
private static void assertJournalDoesNotContain(
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
index 3fe1f3f..3114a75 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
@@ -17,6 +17,7 @@
package com.android.server.backup.testing;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.robolectric.Shadows.shadowOf;
@@ -95,8 +96,8 @@
* logcat before that.
*/
public static void assertLogcatAtMost(String tag, int level) {
- assertThat(ShadowLog.getLogsForTag(tag).stream().allMatch(logItem -> logItem.type <= level))
- .named("All logs <= " + level)
+ assertWithMessage("All logs <= " + level).that(
+ ShadowLog.getLogsForTag(tag).stream().allMatch(logItem -> logItem.type <= level))
.isTrue();
}
@@ -105,8 +106,8 @@
* logcat before that.
*/
public static void assertLogcatAtLeast(String tag, int level) {
- assertThat(ShadowLog.getLogsForTag(tag).stream().anyMatch(logItem -> logItem.type >= level))
- .named("Any log >= " + level)
+ assertWithMessage("Any log >= " + level).that(
+ ShadowLog.getLogsForTag(tag).stream().anyMatch(logItem -> logItem.type >= level))
.isTrue();
}
@@ -121,11 +122,10 @@
* that uses logcat before that.
*/
public static void assertLogcat(String tag, int... logs) {
- assertThat(
+ assertWithMessage("Log items (specified per level)").that(
ShadowLog.getLogsForTag(tag).stream()
.map(logItem -> logItem.type)
.collect(toSet()))
- .named("Log items (specified per level)")
.containsExactly(IntStream.of(logs).boxed().toArray());
}
@@ -135,15 +135,13 @@
/** Declare shadow {@link ShadowEventLog} to use this. */
public static void assertEventLogged(int tag, Object... values) {
- assertThat(ShadowEventLog.getEntries())
- .named("Event logs")
+ assertWithMessage("Event logs").that(ShadowEventLog.getEntries())
.contains(new ShadowEventLog.Entry(tag, Arrays.asList(values)));
}
/** Declare shadow {@link ShadowEventLog} to use this. */
public static void assertEventNotLogged(int tag, Object... values) {
- assertThat(ShadowEventLog.getEntries())
- .named("Event logs")
+ assertWithMessage("Event logs").that(ShadowEventLog.getEntries())
.doesNotContain(new ShadowEventLog.Entry(tag, Arrays.asList(values)));
}
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 d61783e..a5f0834 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -48,10 +48,13 @@
import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
import static com.android.server.alarm.AlarmManagerService.TIME_CHANGED_MASK;
import static com.android.server.alarm.AlarmManagerService.WORKING_INDEX;
+import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
+import static com.android.server.alarm.Constants.TEST_CALLING_UID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -82,6 +85,7 @@
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.Settings;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.SparseArray;
@@ -108,6 +112,8 @@
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.concurrent.Executor;
@@ -116,9 +122,7 @@
@RunWith(AndroidJUnit4.class)
public class AlarmManagerServiceTest {
private static final String TAG = AlarmManagerServiceTest.class.getSimpleName();
- private static final String TEST_CALLING_PACKAGE = "com.android.framework.test-package";
private static final int SYSTEM_UI_UID = 12345;
- private static final int TEST_CALLING_UID = 67890;
private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID);
private long mAppStandbyWindow;
@@ -350,19 +354,31 @@
}
private void setTestAlarm(int type, long triggerTime, PendingIntent operation) {
- setTestAlarm(type, triggerTime, operation, 0, TEST_CALLING_UID);
+ setTestAlarm(type, triggerTime, operation, 0, AlarmManager.FLAG_STANDALONE,
+ TEST_CALLING_UID);
}
private void setRepeatingTestAlarm(int type, long firstTrigger, long interval,
PendingIntent pi) {
- setTestAlarm(type, firstTrigger, pi, interval, TEST_CALLING_UID);
+ setTestAlarm(type, firstTrigger, pi, interval, AlarmManager.FLAG_STANDALONE,
+ TEST_CALLING_UID);
+ }
+
+ private void setIdleUntilAlarm(int type, long triggerTime, PendingIntent pi) {
+ setTestAlarm(type, triggerTime, pi, 0, AlarmManager.FLAG_IDLE_UNTIL, TEST_CALLING_UID);
+ }
+
+ private void setWakeFromIdle(int type, long triggerTime, PendingIntent pi) {
+ // Note: Only alarm clock alarms are allowed to include this flag in the actual service.
+ // But this is a unit test so we'll only test the flag for granularity and convenience.
+ setTestAlarm(type, triggerTime, pi, 0,
+ AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE, TEST_CALLING_UID);
}
private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval,
- int callingUid) {
- mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, interval,
- operation, null, "test", AlarmManager.FLAG_STANDALONE, null, null,
- callingUid, TEST_CALLING_PACKAGE);
+ int flags, int callingUid) {
+ mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, interval, operation, null,
+ "test", flags, null, null, callingUid, TEST_CALLING_PACKAGE);
}
private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener) {
@@ -1002,7 +1018,7 @@
for (int i = 0; i < numAlarms; i++) {
int mockUid = UserHandle.getUid(mockUserId, 1234 + i);
setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10,
- getNewMockPendingIntent(mockUid), 0, mockUid);
+ getNewMockPendingIntent(mockUid), 0, AlarmManager.FLAG_STANDALONE, mockUid);
}
assertEquals(numAlarms, mService.mAlarmsPerUid.size());
mService.removeUserLocked(mockUserId);
@@ -1142,6 +1158,116 @@
}
}
+ @Test
+ public void singleIdleUntil() {
+ doReturn(0).when(mService).fuzzForDuration(anyLong());
+
+ final PendingIntent idleUntilPi6 = getNewMockPendingIntent();
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, idleUntilPi6);
+
+ assertTrue(mService.mPendingIdleUntil.matches(idleUntilPi6, null));
+ assertEquals(mNowElapsedTest + 6, mTestTimer.getElapsed());
+ assertEquals(mNowElapsedTest + 6, mService.mPendingIdleUntil.getWhenElapsed());
+
+ final PendingIntent idleUntilPi2 = getNewMockPendingIntent();
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 2, idleUntilPi2);
+
+ // The same mPendingIdleUntil should get updated, even with a different PendingIntent.
+ assertTrue(mService.mPendingIdleUntil.matches(idleUntilPi2, null));
+ assertEquals(mNowElapsedTest + 2, mTestTimer.getElapsed());
+ assertEquals(1, mService.mAlarmStore.size());
+
+ final PendingIntent idleUntilPi10 = getNewMockPendingIntent();
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10, idleUntilPi10);
+
+ // The same thing should happen even when the new alarm is in farther in the future.
+ assertTrue(mService.mPendingIdleUntil.matches(idleUntilPi10, null));
+ assertEquals(mNowElapsedTest + 10, mTestTimer.getElapsed());
+ assertEquals(1, mService.mAlarmStore.size());
+ }
+
+ @Test
+ public void nextWakeFromIdle() throws Exception {
+ assertNull(mService.mNextWakeFromIdle);
+
+ final PendingIntent wakeFromIdle6 = getNewMockPendingIntent();
+ final long trigger6 = mNowElapsedTest + 6;
+ setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, trigger6, wakeFromIdle6);
+
+ assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle6, null));
+ assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed());
+ assertEquals(trigger6, mTestTimer.getElapsed());
+
+ final PendingIntent wakeFromIdle10 = getNewMockPendingIntent();
+ final long trigger10 = mNowElapsedTest + 10;
+ setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, trigger10, wakeFromIdle10);
+
+ // mNextWakeFromIdle should not get updated.
+ assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle6, null));
+ assertEquals(trigger6, mTestTimer.getElapsed());
+ assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed());
+
+ final PendingIntent wakeFromIdle3 = getNewMockPendingIntent();
+ final long trigger3 = mNowElapsedTest + 3;
+ setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, trigger3, wakeFromIdle3);
+
+ // mNextWakeFromIdle should always reflect the next earliest wake_from_idle alarm.
+ assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle3, null));
+ assertEquals(trigger3, mTestTimer.getElapsed());
+ assertEquals(trigger3, mService.mNextWakeFromIdle.getWhenElapsed());
+
+ mNowElapsedTest = trigger3;
+ mTestTimer.expire();
+
+ assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle6, null));
+ assertEquals(trigger6, mTestTimer.getElapsed());
+ assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed());
+
+ mService.removeLocked(wakeFromIdle6, null);
+
+ assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle10, null));
+ assertEquals(trigger10, mTestTimer.getElapsed());
+ assertEquals(trigger10, mService.mNextWakeFromIdle.getWhenElapsed());
+
+ mService.removeLocked(wakeFromIdle10, null);
+ assertNull(mService.mNextWakeFromIdle);
+ }
+
+ @Test
+ public void idleUntilBeforeWakeFromIdle() {
+ doReturn(0).when(mService).fuzzForDuration(anyLong());
+
+ final PendingIntent idleUntilPi = getNewMockPendingIntent();
+ final long requestedIdleUntil = mNowElapsedTest + 10;
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, requestedIdleUntil, idleUntilPi);
+
+ assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed());
+
+ final PendingIntent wakeFromIdle5 = getNewMockPendingIntent();
+ setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, wakeFromIdle5);
+ assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed());
+
+ final PendingIntent wakeFromIdle8 = getNewMockPendingIntent();
+ setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 8, wakeFromIdle8);
+ assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed());
+
+ final PendingIntent wakeFromIdle12 = getNewMockPendingIntent();
+ setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 12, wakeFromIdle12);
+ assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed());
+
+ mService.removeLocked(wakeFromIdle5, null);
+ assertEquals(mNowElapsedTest + 8, mService.mPendingIdleUntil.getWhenElapsed());
+
+ mService.removeLocked(wakeFromIdle8, null);
+ assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed());
+
+ mService.removeLocked(idleUntilPi, null);
+ assertNull(mService.mPendingIdleUntil);
+
+ setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 15, idleUntilPi);
+ assertEquals(mNowElapsedTest + 12, mService.mPendingIdleUntil.getWhenElapsed());
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
@@ -1149,4 +1275,12 @@
}
LocalServices.removeServiceForTest(AlarmManagerInternal.class);
}
+
+ private void dumpAllAlarms(String tag, ArrayList<Alarm> alarms) {
+ System.out.println(tag + ": ");
+ IndentingPrintWriter ipw = new IndentingPrintWriter(new PrintWriter(System.out));
+ AlarmManagerService.dumpAlarmList(ipw, alarms, mNowElapsedTest,
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
+ ipw.close();
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
index 9e43b4a..f0490ce 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java
@@ -16,6 +16,9 @@
package com.android.server.alarm;
+import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
+import static com.android.server.alarm.Constants.TEST_CALLING_UID;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@@ -35,9 +38,6 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class AlarmStoreTest {
- private static final int TEST_CALLING_UID = 12345;
- private static final String TEST_CALLING_PACKAGE = "android.alarm.unit.test";
-
private AlarmStore mAlarmStore;
@Before
@@ -45,22 +45,22 @@
mAlarmStore = new BatchingAlarmStore(null);
}
- private static Alarm createAlarm(long whenElapsed, long windowLength, PendingIntent mockPi,
+ private static Alarm createAlarm(long whenElapsed, long windowLength,
AlarmManager.AlarmClockInfo alarmClock) {
- return createAlarm(AlarmManager.ELAPSED_REALTIME, whenElapsed, windowLength, mockPi,
+ return createAlarm(AlarmManager.ELAPSED_REALTIME, whenElapsed, windowLength,
alarmClock);
}
private static Alarm createWakeupAlarm(long whenElapsed, long windowLength,
- PendingIntent mockPi, AlarmManager.AlarmClockInfo alarmClock) {
- return createAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength, mockPi,
+ AlarmManager.AlarmClockInfo alarmClock) {
+ return createAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength,
alarmClock);
}
private static Alarm createAlarm(int type, long whenElapsed, long windowLength,
- PendingIntent mockPi, AlarmManager.AlarmClockInfo alarmClock) {
- return new Alarm(type, whenElapsed, whenElapsed, windowLength, whenElapsed + windowLength,
- 0, mockPi, null, null, null, 0, alarmClock, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+ AlarmManager.AlarmClockInfo alarmClock) {
+ return new Alarm(type, whenElapsed, whenElapsed, windowLength, 0, mock(PendingIntent.class),
+ null, null, null, 0, alarmClock, TEST_CALLING_UID, TEST_CALLING_PACKAGE);
}
private void addAlarmsToStore(Alarm... alarms) {
@@ -71,11 +71,11 @@
@Test
public void add() {
- final Alarm a1 = createAlarm(1, 0, mock(PendingIntent.class), null);
+ final Alarm a1 = createAlarm(1, 0, null);
mAlarmStore.add(a1);
assertEquals(1, mAlarmStore.size());
- final Alarm a2 = createAlarm(2, 0, mock(PendingIntent.class), null);
+ final Alarm a2 = createAlarm(2, 0, null);
mAlarmStore.add(a2);
assertEquals(2, mAlarmStore.size());
@@ -86,17 +86,17 @@
@Test
public void remove() {
- final Alarm a1 = createAlarm(1, 0, mock(PendingIntent.class), null);
- final Alarm a2 = createAlarm(2, 0, mock(PendingIntent.class), null);
- final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null);
+ final Alarm a1 = createAlarm(1, 0, null);
+ final Alarm a2 = createAlarm(2, 0, null);
+ final Alarm a5 = createAlarm(5, 0, null);
addAlarmsToStore(a1, a2, a5);
- ArrayList<Alarm> removed = mAlarmStore.remove(a -> (a.whenElapsed < 4));
+ ArrayList<Alarm> removed = mAlarmStore.remove(a -> (a.getWhenElapsed() < 4));
assertEquals(2, removed.size());
assertEquals(1, mAlarmStore.size());
assertTrue(removed.contains(a1) && removed.contains(a2));
- final Alarm a8 = createAlarm(8, 0, mock(PendingIntent.class), null);
+ final Alarm a8 = createAlarm(8, 0, null);
addAlarmsToStore(a8, a2, a1);
removed = mAlarmStore.remove(unused -> false);
@@ -110,10 +110,10 @@
@Test
public void removePendingAlarms() {
- final Alarm a1_11 = createAlarm(1, 10, mock(PendingIntent.class), null);
- final Alarm a2_5 = createAlarm(2, 3, mock(PendingIntent.class), null);
- final Alarm a6_9 = createAlarm(6, 3, mock(PendingIntent.class), null);
- addAlarmsToStore(a2_5, a6_9, a1_11);
+ final Alarm a1to11 = createAlarm(1, 10, null);
+ final Alarm a2to5 = createAlarm(2, 3, null);
+ final Alarm a6to9 = createAlarm(6, 3, null);
+ addAlarmsToStore(a2to5, a6to9, a1to11);
final ArrayList<Alarm> pendingAt0 = mAlarmStore.removePendingAlarms(0);
assertEquals(0, pendingAt0.size());
@@ -121,24 +121,24 @@
final ArrayList<Alarm> pendingAt3 = mAlarmStore.removePendingAlarms(3);
assertEquals(2, pendingAt3.size());
- assertTrue(pendingAt3.contains(a1_11) && pendingAt3.contains(a2_5));
+ assertTrue(pendingAt3.contains(a1to11) && pendingAt3.contains(a2to5));
assertEquals(1, mAlarmStore.size());
- addAlarmsToStore(a2_5, a1_11);
+ addAlarmsToStore(a2to5, a1to11);
final ArrayList<Alarm> pendingAt7 = mAlarmStore.removePendingAlarms(7);
assertEquals(3, pendingAt7.size());
- assertTrue(pendingAt7.contains(a1_11) && pendingAt7.contains(a2_5) && pendingAt7.contains(
- a6_9));
+ assertTrue(pendingAt7.contains(a1to11) && pendingAt7.contains(a2to5) && pendingAt7.contains(
+ a6to9));
assertEquals(0, mAlarmStore.size());
}
@Test
public void getNextWakeupDeliveryTime() {
- final Alarm a1_10 = createAlarm(1, 9, mock(PendingIntent.class), null);
- final Alarm a3_8_wakeup = createWakeupAlarm(3, 5, mock(PendingIntent.class), null);
- final Alarm a6_wakeup = createWakeupAlarm(6, 0, mock(PendingIntent.class), null);
- final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null);
- addAlarmsToStore(a5, a6_wakeup, a3_8_wakeup, a1_10);
+ final Alarm a1to10 = createAlarm(1, 9, null);
+ final Alarm a3to8wakeup = createWakeupAlarm(3, 5, null);
+ final Alarm a6wakeup = createWakeupAlarm(6, 0, null);
+ final Alarm a5 = createAlarm(5, 0, null);
+ addAlarmsToStore(a5, a6wakeup, a3to8wakeup, a1to10);
// The wakeup alarms are [6] and [3, 8], hence 6 is the latest time till when we can
// defer delivering any wakeup alarm.
@@ -155,11 +155,11 @@
@Test
public void getNextDeliveryTime() {
- final Alarm a1_10 = createAlarm(1, 9, mock(PendingIntent.class), null);
- final Alarm a3_8_wakeup = createWakeupAlarm(3, 5, mock(PendingIntent.class), null);
- final Alarm a6_wakeup = createWakeupAlarm(6, 0, mock(PendingIntent.class), null);
- final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null);
- addAlarmsToStore(a5, a6_wakeup, a3_8_wakeup, a1_10);
+ final Alarm a1to10 = createAlarm(1, 9, null);
+ final Alarm a3to8wakeup = createWakeupAlarm(3, 5, null);
+ final Alarm a6wakeup = createWakeupAlarm(6, 0, null);
+ final Alarm a5 = createAlarm(5, 0, null);
+ addAlarmsToStore(a5, a6wakeup, a3to8wakeup, a1to10);
assertTrue(mAlarmStore.getNextDeliveryTime() <= 5);
@@ -168,24 +168,22 @@
}
@Test
- public void recalculateAlarmDeliveries() {
- final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null);
- final Alarm a8 = createAlarm(8, 0, mock(PendingIntent.class), null);
- final Alarm a10 = createAlarm(10, 0, mock(PendingIntent.class), null);
+ public void updateAlarmDeliveries() {
+ final Alarm a5 = createAlarm(5, 0, null);
+ final Alarm a8 = createAlarm(8, 0, null);
+ final Alarm a10 = createAlarm(10, 0, null);
addAlarmsToStore(a8, a10, a5);
assertEquals(5, mAlarmStore.getNextDeliveryTime());
- mAlarmStore.recalculateAlarmDeliveries(a -> {
- a.whenElapsed += 3;
- a.maxWhenElapsed = a.whenElapsed;
+ mAlarmStore.updateAlarmDeliveries(a -> {
+ a.setPolicyElapsed(Alarm.REQUESTER_POLICY_INDEX, a.getWhenElapsed() + 3);
return true;
});
assertEquals(8, mAlarmStore.getNextDeliveryTime());
- mAlarmStore.recalculateAlarmDeliveries(a -> {
- a.whenElapsed = 20 - a.whenElapsed;
- a.maxWhenElapsed = a.whenElapsed;
+ mAlarmStore.updateAlarmDeliveries(a -> {
+ a.setPolicyElapsed(Alarm.REQUESTER_POLICY_INDEX, 20 - a.getWhenElapsed());
return true;
});
assertEquals(7, mAlarmStore.getNextDeliveryTime());
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java
new file mode 100644
index 0000000..efcfae3
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.alarm;
+
+import static android.app.AlarmManager.ELAPSED_REALTIME;
+
+import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
+import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
+import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
+import static com.android.server.alarm.Constants.TEST_CALLING_UID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import android.app.PendingIntent;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AlarmTest {
+
+ private Alarm createDefaultAlarm(long requestedElapsed, long windowLength) {
+ return new Alarm(ELAPSED_REALTIME, 0, requestedElapsed, windowLength, 0,
+ mock(PendingIntent.class), null, null, null, 0, null, TEST_CALLING_UID,
+ TEST_CALLING_PACKAGE);
+ }
+
+ @Test
+ public void initSetsOnlyRequesterPolicy() {
+ final Alarm a = createDefaultAlarm(4567, 2);
+ assertEquals(4567, a.getPolicyElapsed(REQUESTER_POLICY_INDEX));
+ assertEquals(0, a.getPolicyElapsed(APP_STANDBY_POLICY_INDEX));
+ }
+
+ @Test
+ public void whenElapsed() {
+ final Alarm a = createDefaultAlarm(0, 0);
+
+ a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4);
+ a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10);
+ assertEquals(10, a.getWhenElapsed());
+
+ a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 12);
+ assertEquals(12, a.getWhenElapsed());
+
+ a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 7);
+ assertEquals(10, a.getWhenElapsed());
+
+ a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 2);
+ assertEquals(7, a.getWhenElapsed());
+
+ a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 7);
+ assertEquals(7, a.getWhenElapsed());
+ }
+
+ @Test
+ public void maxWhenElapsed() {
+ final Alarm a = createDefaultAlarm(10, 12);
+ assertEquals(22, a.getMaxWhenElapsed());
+
+ a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 15);
+ assertEquals(27, a.getMaxWhenElapsed());
+
+ a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 2);
+ assertEquals(14, a.getMaxWhenElapsed());
+
+ a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 5);
+ assertEquals(14, a.getMaxWhenElapsed());
+
+ a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 16);
+ assertEquals(16, a.getMaxWhenElapsed());
+
+ a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 12);
+ assertEquals(14, a.getMaxWhenElapsed());
+ }
+
+ @Test
+ public void setPolicyElapsed() {
+ final Alarm exactAlarm = createDefaultAlarm(10, 0);
+
+ assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4));
+ assertTrue(exactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
+
+ assertFalse(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 8));
+ assertFalse(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 10));
+ assertFalse(exactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 8));
+
+ assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 7));
+
+ final Alarm inexactAlarm = createDefaultAlarm(10, 5);
+
+ assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4));
+ assertTrue(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10));
+
+ // whenElapsed won't change, but maxWhenElapsed will.
+ assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 8));
+ assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 10));
+
+ assertFalse(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 8));
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
index 6465739..5bb6a42 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java
@@ -45,7 +45,7 @@
}
uidAlarms.add(new Alarm(
removeIt ? RTC : RTC_WAKEUP,
- 0, 0, 0, 0, 0, null, null, null, null, 0, null, uid, name));
+ 0, 0, 0, 0, null, null, null, null, 0, null, uid, name));
return all;
}
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/services/tests/mockingservicestests/src/com/android/server/alarm/Constants.java
similarity index 70%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to services/tests/mockingservicestests/src/com/android/server/alarm/Constants.java
index 71cd0a7..2552db8 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/Constants.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 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,6 +14,9 @@
* limitations under the License.
*/
-package android.media.tv;
+package com.android.server.alarm;
-parcelable TvChannelInfo;
+public interface Constants {
+ String TEST_CALLING_PACKAGE = "com.android.framework.test-package";
+ int TEST_CALLING_UID = 67890;
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
index 04e8b63..b85da94 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
@@ -34,12 +34,12 @@
import android.os.HandlerThread;
import android.os.Process;
import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
-import com.android.server.am.ActivityManagerService.Injector;
import com.android.server.appop.AppOpsService;
import com.android.server.wm.ActivityTaskManagerService;
@@ -55,7 +55,11 @@
import org.mockito.MockitoAnnotations;
import org.mockito.quality.Strictness;
+import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
@Presubmit
public class AppChildProcessTest {
@@ -68,6 +72,7 @@
private Context mContext = getInstrumentation().getTargetContext();
private TestInjector mInjector;
+ private PhantomTestInjector mPhantomInjector;
private ActivityManagerService mAms;
private ProcessList mProcessList;
private PhantomProcessList mPhantomProcessList;
@@ -94,6 +99,7 @@
mProcessList = spy(pList);
mInjector = new TestInjector(mContext);
+ mPhantomInjector = new PhantomTestInjector();
mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread());
mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
@@ -101,6 +107,7 @@
mAms.mPackageManagerInt = mPackageManagerInt;
pList.mService = mAms;
mPhantomProcessList = mAms.mPhantomProcessList;
+ mPhantomProcessList.mInjector = mPhantomInjector;
doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
doReturn(false).when(() -> Process.supportsPidFd());
// Remove stale instance of PackageManagerInternal if there is any
@@ -136,47 +143,60 @@
final String child2ProcessName = "test1_child1_child2";
final String nativeProcessName = "test_native";
- makeParent(zygote64Pid, initPid);
- makeParent(zygote32Pid, initPid);
+ makeProcess(rootUid, zygote64Pid, zygote64ProcessName);
+ makeParent(rootUid, zygote64Pid, initPid);
+ makeProcess(rootUid, zygote32Pid, zygote32ProcessName);
+ makeParent(rootUid, zygote32Pid, initPid);
makeAppProcess(app1Pid, app1Uid, app1ProcessName, app1ProcessName);
- makeParent(app1Pid, zygote64Pid);
makeAppProcess(app2Pid, app2Uid, app2ProcessName, app2ProcessName);
- makeParent(app2Pid, zygote64Pid);
+
+ mPhantomProcessList.lookForPhantomProcessesLocked();
assertEquals(0, mPhantomProcessList.mPhantomProcesses.size());
// Verify zygote itself isn't a phantom process
assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
- zygote64ProcessName, rootUid, zygote64Pid));
+ zygote64ProcessName, rootUid, zygote64Pid, false));
assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
- zygote32ProcessName, rootUid, zygote32Pid));
+ zygote32ProcessName, rootUid, zygote32Pid, false));
// Verify none of the app isn't a phantom process
assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
- app1ProcessName, app1Uid, app1Pid));
+ app1ProcessName, app1Uid, app1Pid, false));
assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
- app2ProcessName, app2Uid, app2Pid));
+ app2ProcessName, app2Uid, app2Pid, false));
// "Fork" an app child process
- makeParent(child1Pid, app1Pid);
+ makeProcess(app1Uid, child1Pid, child1ProcessName);
+ makeParent(app1Uid, child1Pid, app1Pid);
+ mPhantomProcessList.lookForPhantomProcessesLocked();
+
PhantomProcessRecord pr = mPhantomProcessList
- .getOrCreatePhantomProcessIfNeededLocked(child1ProcessName, app1Uid, child1Pid);
+ .getOrCreatePhantomProcessIfNeededLocked(
+ child1ProcessName, app1Uid, child1Pid, true);
assertTrue(pr != null);
assertEquals(1, mPhantomProcessList.mPhantomProcesses.size());
assertEquals(pr, mPhantomProcessList.mPhantomProcesses.valueAt(0));
verifyPhantomProcessRecord(pr, child1ProcessName, app1Uid, child1Pid);
// Create another native process from init
- makeParent(nativePid, initPid);
+ makeProcess(rootUid, nativePid, nativeProcessName);
+ makeParent(rootUid, nativePid, initPid);
+ mPhantomProcessList.lookForPhantomProcessesLocked();
+
assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
- nativeProcessName, rootUid, nativePid));
+ nativeProcessName, rootUid, nativePid, false));
assertEquals(1, mPhantomProcessList.mPhantomProcesses.size());
assertEquals(pr, mPhantomProcessList.mPhantomProcesses.valueAt(0));
// "Fork" another app child process
- makeParent(child2Pid, child1Pid);
+ makeProcess(app1Uid, child2Pid, child2ProcessName);
+ makeParent(app1Uid, child2Pid, app1Pid);
+ mPhantomProcessList.lookForPhantomProcessesLocked();
+
PhantomProcessRecord pr2 = mPhantomProcessList
- .getOrCreatePhantomProcessIfNeededLocked(child2ProcessName, app1Uid, child2Pid);
+ .getOrCreatePhantomProcessIfNeededLocked(
+ child2ProcessName, app1Uid, child2Pid, false);
assertTrue(pr2 != null);
assertEquals(2, mPhantomProcessList.mPhantomProcesses.size());
verifyPhantomProcessRecord(pr2, child2ProcessName, app1Uid, child2Pid);
@@ -197,19 +217,27 @@
assertEquals(pid, pr.mPid);
}
+ private void makeProcess(int uid, int pid, String processName) {
+ doReturn(uid).when(() -> Process.getUidForPid(eq(pid)));
+ mPhantomInjector.mPidToName.put(pid, processName);
+ }
+
private void makeAppProcess(int pid, int uid, String packageName, String processName) {
+ makeProcess(uid, pid, processName);
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = packageName;
+ ai.uid = uid;
ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid);
app.pid = pid;
mAms.mPidsSelfLocked.doAddInternal(app);
+ mPhantomInjector.addToProcess(uid, pid, pid);
}
- private void makeParent(int pid, int ppid) {
- doReturn(ppid).when(() -> Process.getParentPid(eq(pid)));
+ private void makeParent(int uid, int pid, int ppid) {
+ mPhantomInjector.addToProcess(uid, ppid, pid);
}
- private class TestInjector extends Injector {
+ private class TestInjector extends ActivityManagerService.Injector {
TestInjector(Context context) {
super(context);
}
@@ -230,6 +258,56 @@
}
}
+ private class PhantomTestInjector extends PhantomProcessList.Injector {
+ ArrayMap<String, InputStream> mPathToInput = new ArrayMap<>();
+ ArrayMap<String, StringBuffer> mPathToData = new ArrayMap<>();
+ ArrayMap<InputStream, StringBuffer> mInputToData = new ArrayMap<>();
+ ArrayMap<Integer, String> mPidToName = new ArrayMap<>();
+
+ @Override
+ InputStream openCgroupProcs(String path) throws FileNotFoundException, SecurityException {
+ InputStream input = mPathToInput.get(path);
+ if (input != null) {
+ return input;
+ }
+ input = new ByteArrayInputStream(new byte[8]); // buf size doesn't matter here
+ mPathToInput.put(path, input);
+ StringBuffer sb = mPathToData.get(path);
+ if (sb == null) {
+ sb = new StringBuffer();
+ mPathToData.put(path, sb);
+ }
+ mInputToData.put(input, sb);
+ return input;
+ }
+
+ @Override
+ int readCgroupProcs(InputStream input, byte[] buf, int offset, int len) throws IOException {
+ StringBuffer sb = mInputToData.get(input);
+ if (sb == null) {
+ return -1;
+ }
+ byte[] avail = sb.toString().getBytes();
+ System.arraycopy(avail, 0, buf, offset, Math.min(len, avail.length));
+ return Math.min(len, avail.length);
+ }
+
+ @Override
+ String getProcessName(final int pid) {
+ return mPidToName.get(pid);
+ }
+
+ void addToProcess(int uid, int pid, int newPid) {
+ final String path = PhantomProcessList.getCgroupFilePath(uid, pid);
+ StringBuffer sb = mPathToData.get(path);
+ if (sb == null) {
+ sb = new StringBuffer();
+ mPathToData.put(path, sb);
+ }
+ sb.append(newPid).append('\n');
+ }
+ }
+
static class ServiceThreadRule implements TestRule {
private ServiceThread mThread;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
index a0f48c6..d67eddd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
@@ -79,7 +79,7 @@
Location coarse = mFudger.createCoarse(fine);
assertThat(coarse).isNotNull();
- assertThat(coarse).isNotSameAs(fine);
+ assertThat(coarse).isNotSameInstanceAs(fine);
assertThat(coarse.hasBearing()).isFalse();
assertThat(coarse.hasSpeed()).isFalse();
assertThat(coarse.hasAltitude()).isFalse();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
index 31ec4a5..3aedd3c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
@@ -710,7 +710,6 @@
@Test
public void testProviderRequest() {
assertThat(mProvider.getRequest().isActive()).isFalse();
- assertThat(mProvider.getRequest().getLocationRequests()).isEmpty();
ILocationListener listener1 = createMockLocationListener();
LocationRequest request1 = new LocationRequest.Builder(5).setWorkSource(
@@ -718,7 +717,6 @@
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
assertThat(mProvider.getRequest().isActive()).isTrue();
- assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request1);
assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
assertThat(mProvider.getRequest().isLowPower()).isFalse();
@@ -732,8 +730,6 @@
mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
assertThat(mProvider.getRequest().isActive()).isTrue();
- assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request1,
- request2);
assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
assertThat(mProvider.getRequest().isLowPower()).isFalse();
@@ -742,7 +738,6 @@
mManager.unregisterLocationRequest(listener1);
assertThat(mProvider.getRequest().isActive()).isTrue();
- assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request2);
assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
assertThat(mProvider.getRequest().isLowPower()).isTrue();
@@ -751,7 +746,6 @@
mManager.unregisterLocationRequest(listener2);
assertThat(mProvider.getRequest().isActive()).isFalse();
- assertThat(mProvider.getRequest().getLocationRequests()).isEmpty();
}
@Test
@@ -855,7 +849,6 @@
mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
assertThat(mProvider.getRequest().isActive()).isTrue();
- assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request2);
assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isTrue();
}
diff --git a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
index 968a402..3ace3f4 100644
--- a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
@@ -27,8 +27,6 @@
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.BluetoothAirplaneModeListener.AirplaneModeHelper;
-
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -41,7 +39,7 @@
private Context mContext;
private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
private BluetoothAdapter mBluetoothAdapter;
- private AirplaneModeHelper mHelper;
+ private BluetoothModeChangeHelper mHelper;
@Mock BluetoothManagerService mBluetoothManagerService;
@@ -49,7 +47,7 @@
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
- mHelper = mock(AirplaneModeHelper.class);
+ mHelper = mock(BluetoothModeChangeHelper.class);
when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT))
.thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT);
doNothing().when(mHelper).setSettingsInt(anyString(), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index c91bb93..d2d85c8 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -223,6 +223,25 @@
}
@Test
+ public void testInterceptPowerKeyDown_firstPowerDown_panicGestureNotLaunched() {
+ withPanicGestureEnabledSettingValue(true);
+ mGestureLauncherService.updatePanicButtonGestureEnabled();
+
+ long eventTime = INITIAL_EVENT_TIME_MILLIS
+ + GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS - 1;
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+ verify(mMetricsLogger).histogram("power_double_tap_interval", (int) eventTime);
+ }
+
+ @Test
public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffInteractive() {
withCameraDoubleTapPowerEnableConfigValue(false);
withCameraDoubleTapPowerDisableSettingValue(1);
@@ -405,6 +424,200 @@
}
@Test
+ public void
+ testInterceptPowerKeyDown_fiveInboundPresses_cameraAndPanicEnabled_bothLaunch() {
+ withCameraDoubleTapPowerEnableConfigValue(true);
+ withCameraDoubleTapPowerDisableSettingValue(0);
+ withPanicGestureEnabledSettingValue(true);
+ mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+ mGestureLauncherService.updatePanicButtonGestureEnabled();
+ withUserSetupCompleteValue(true);
+
+ // First button press does nothing
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+
+ final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+
+ // 2nd button triggers camera
+ eventTime += interval;
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ outLaunched.value = false;
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(intercepted);
+ assertTrue(outLaunched.value);
+
+ // Camera checks
+ verify(mStatusBarManagerInternal).onCameraLaunchGestureDetected(
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
+ verify(mMetricsLogger)
+ .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
+
+ final ArgumentCaptor<Integer> cameraIntervalCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMetricsLogger, times(2)).histogram(
+ eq("power_double_tap_interval"), cameraIntervalCaptor.capture());
+ List<Integer> cameraIntervals = cameraIntervalCaptor.getAllValues();
+ assertEquals((int) INITIAL_EVENT_TIME_MILLIS, cameraIntervals.get(0).intValue());
+ assertEquals((int) interval, cameraIntervals.get(1).intValue());
+
+ final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMetricsLogger, times(2)).histogram(
+ eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
+ List<Integer> tapCounts = tapCountCaptor.getAllValues();
+ assertEquals(1, tapCounts.get(0).intValue());
+ assertEquals(2, tapCounts.get(1).intValue());
+
+ // Continue the button presses for the panic gesture.
+
+ // Presses 3 and 4 should not trigger any gesture
+ for (int i = 0; i < 2; i++) {
+ eventTime += interval;
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ outLaunched.value = false;
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+ }
+
+ // Fifth button press should trigger the panic flow
+ eventTime += interval;
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ outLaunched.value = false;
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(intercepted);
+ assertTrue(outLaunched.value);
+
+ // TODO (b/169960245) Verify metric event equiv. to ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE
+ verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
+
+ final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMetricsLogger, times(5)).histogram(
+ eq("power_double_tap_interval"), intervalCaptor.capture());
+ List<Integer> intervals = intervalCaptor.getAllValues();
+ assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
+ assertEquals((int) interval, intervals.get(1).intValue());
+ }
+
+ @Test
+ public void
+ testInterceptPowerKeyDown_fiveInboundPresses_panicGestureEnabled_launchesPanicFlow() {
+ withPanicGestureEnabledSettingValue(true);
+ mGestureLauncherService.updatePanicButtonGestureEnabled();
+ withUserSetupCompleteValue(true);
+
+ // First button press does nothing
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+
+ final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ // 3 more button presses which should not trigger any gesture (camera gesture disabled)
+ for (int i = 0; i < 3; i++) {
+ eventTime += interval;
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ outLaunched.value = false;
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+ }
+
+ // Fifth button press should trigger the panic flow
+ eventTime += interval;
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ outLaunched.value = false;
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(outLaunched.value);
+ assertTrue(intercepted);
+
+ // TODO (b/169960245) Verify metric event equiv. to ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE
+ verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
+
+ final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mMetricsLogger, times(5)).histogram(
+ eq("power_double_tap_interval"), intervalCaptor.capture());
+ List<Integer> intervals = intervalCaptor.getAllValues();
+ assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
+ assertEquals((int) interval, intervals.get(1).intValue());
+ }
+
+ @Test
+ public void
+ testInterceptPowerKeyDown_tenInboundPresses_panicGestureEnabled_pressesIntercepted() {
+ withPanicGestureEnabledSettingValue(true);
+ mGestureLauncherService.updatePanicButtonGestureEnabled();
+ withUserSetupCompleteValue(true);
+
+ // First button press does nothing
+ long eventTime = INITIAL_EVENT_TIME_MILLIS;
+ KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ boolean interactive = true;
+ MutableBoolean outLaunched = new MutableBoolean(true);
+ boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertFalse(intercepted);
+ assertFalse(outLaunched.value);
+
+ final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+ // 3 more button presses which should not trigger any gesture, but intercepts action.
+ for (int i = 0; i < 3; i++) {
+ eventTime += interval;
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ outLaunched.value = false;
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+ }
+
+ // Fifth button press should trigger the panic flow
+ eventTime += interval;
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ outLaunched.value = false;
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(outLaunched.value);
+ assertTrue(intercepted);
+
+ // 5 more button presses which should not trigger any gesture, but intercepts action.
+ for (int i = 0; i < 5; i++) {
+ eventTime += interval;
+ keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+ IGNORED_REPEAT);
+ outLaunched.value = false;
+ intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+ outLaunched);
+ assertTrue(intercepted);
+ assertFalse(outLaunched.value);
+ }
+ }
+
+ @Test
public void testInterceptPowerKeyDown_longpress() {
withCameraDoubleTapPowerEnableConfigValue(true);
withCameraDoubleTapPowerDisableSettingValue(0);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 03dce4c..faf7169a 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -68,6 +68,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.util.SparseArray;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
@@ -76,6 +77,7 @@
import com.android.server.LocalServices;
import com.android.server.am.ProcessList.IsolatedUidRange;
import com.android.server.am.ProcessList.IsolatedUidRangeAllocator;
+import com.android.server.am.UidObserverController.ChangeRecord;
import com.android.server.appop.AppOpsService;
import com.android.server.wm.ActivityTaskManagerService;
@@ -539,15 +541,15 @@
ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
ActivityManager.PROCESS_STATE_TOP
};
- final Map<Integer, UidRecord.ChangeItem> changeItems = new HashMap<>();
+ final Map<Integer, ChangeRecord> changeItems = new HashMap<>();
for (int i = 0; i < changesForPendingUidRecords.length; ++i) {
- final UidRecord.ChangeItem pendingChange = new UidRecord.ChangeItem();
+ final ChangeRecord pendingChange = new ChangeRecord();
pendingChange.change = changesForPendingUidRecords[i];
pendingChange.uid = i;
- pendingChange.processState = procStatesForPendingUidRecords[i];
+ pendingChange.procState = procStatesForPendingUidRecords[i];
pendingChange.procStateSeq = i;
changeItems.put(changesForPendingUidRecords[i], pendingChange);
- mAms.mUidObserverController.mPendingUidChanges.add(pendingChange);
+ addPendingUidChange(pendingChange);
}
mAms.mUidObserverController.dispatchUidsChanged();
@@ -602,7 +604,7 @@
verifyObserverReceivedChanges(observerToTest, changesToVerify, changeItems,
(observer, changeItem) -> {
verify(observer).onUidStateChanged(changeItem.uid,
- changeItem.processState, changeItem.procStateSeq,
+ changeItem.procState, changeItem.procStateSeq,
ActivityManager.PROCESS_CAPABILITY_NONE);
});
}
@@ -612,14 +614,14 @@
}
private interface ObserverChangesVerifier {
- void verify(IUidObserver observer, UidRecord.ChangeItem changeItem) throws RemoteException;
+ void verify(IUidObserver observer, ChangeRecord changeItem) throws RemoteException;
}
private void verifyObserverReceivedChanges(IUidObserver observer, int[] changesToVerify,
- Map<Integer, UidRecord.ChangeItem> changeItems, ObserverChangesVerifier verifier)
+ Map<Integer, ChangeRecord> changeItems, ObserverChangesVerifier verifier)
throws RemoteException {
for (int change : changesToVerify) {
- final UidRecord.ChangeItem changeItem = changeItems.get(change);
+ final ChangeRecord changeItem = changeItems.get(change);
verifier.verify(observer, changeItem);
}
}
@@ -641,59 +643,59 @@
// So, resetting the mock here.
Mockito.reset(observer);
- final UidRecord.ChangeItem changeItem = new UidRecord.ChangeItem();
+ final ChangeRecord changeItem = new ChangeRecord();
changeItem.uid = TEST_UID;
changeItem.change = UidRecord.CHANGE_PROCSTATE;
- changeItem.processState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+ changeItem.procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
changeItem.procStateSeq = 111;
- mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+ addPendingUidChange(changeItem);
mAms.mUidObserverController.dispatchUidsChanged();
// First process state message is always delivered regardless of whether the process state
// change is above or below the cutpoint (PROCESS_STATE_SERVICE).
verify(observer).onUidStateChanged(TEST_UID,
- changeItem.processState, changeItem.procStateSeq,
+ changeItem.procState, changeItem.procStateSeq,
ActivityManager.PROCESS_CAPABILITY_NONE);
verifyNoMoreInteractions(observer);
- changeItem.processState = ActivityManager.PROCESS_STATE_RECEIVER;
- mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+ changeItem.procState = ActivityManager.PROCESS_STATE_RECEIVER;
+ addPendingUidChange(changeItem);
mAms.mUidObserverController.dispatchUidsChanged();
// Previous process state change is below cutpoint (PROCESS_STATE_SERVICE) and
// the current process state change is also below cutpoint, so no callback will be invoked.
verifyNoMoreInteractions(observer);
- changeItem.processState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
- mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+ changeItem.procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+ addPendingUidChange(changeItem);
mAms.mUidObserverController.dispatchUidsChanged();
// Previous process state change is below cutpoint (PROCESS_STATE_SERVICE) and
// the current process state change is above cutpoint, so callback will be invoked with the
// current process state change.
verify(observer).onUidStateChanged(TEST_UID,
- changeItem.processState, changeItem.procStateSeq,
+ changeItem.procState, changeItem.procStateSeq,
ActivityManager.PROCESS_CAPABILITY_NONE);
verifyNoMoreInteractions(observer);
- changeItem.processState = ActivityManager.PROCESS_STATE_TOP;
- mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+ changeItem.procState = ActivityManager.PROCESS_STATE_TOP;
+ addPendingUidChange(changeItem);
mAms.mUidObserverController.dispatchUidsChanged();
// Previous process state change is above cutpoint (PROCESS_STATE_SERVICE) and
// the current process state change is also above cutpoint, so no callback will be invoked.
verifyNoMoreInteractions(observer);
- changeItem.processState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
- mAms.mUidObserverController.mPendingUidChanges.add(changeItem);
+ changeItem.procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+ addPendingUidChange(changeItem);
mAms.mUidObserverController.dispatchUidsChanged();
// Previous process state change is above cutpoint (PROCESS_STATE_SERVICE) and
// the current process state change is below cutpoint, so callback will be invoked with the
// current process state change.
verify(observer).onUidStateChanged(TEST_UID,
- changeItem.processState, changeItem.procStateSeq,
+ changeItem.procState, changeItem.procStateSeq,
ActivityManager.PROCESS_CAPABILITY_NONE);
verifyNoMoreInteractions(observer);
}
/**
- * This test verifies that {@link ActivityManagerService#mValidateUids} which is a
+ * This test verifies that {@link UidObserverController#getValidateUidsForTest()} which is a
* part of dumpsys is correctly updated.
*/
@Test
@@ -707,45 +709,45 @@
ActivityManager.PROCESS_STATE_SERVICE,
ActivityManager.PROCESS_STATE_RECEIVER
};
- final ArrayList<UidRecord.ChangeItem> pendingItemsForUids =
+ final ArrayList<ChangeRecord> pendingItemsForUids =
new ArrayList<>(changesForPendingItems.length);
for (int i = 0; i < changesForPendingItems.length; ++i) {
- final UidRecord.ChangeItem item = new UidRecord.ChangeItem();
+ final ChangeRecord item = new ChangeRecord();
item.uid = i;
item.change = changesForPendingItems[i];
- item.processState = procStatesForPendingItems[i];
+ item.procState = procStatesForPendingItems[i];
pendingItemsForUids.add(i, item);
}
// Verify that when there no observers listening to uid state changes, then there will
// be no changes to validateUids.
- mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids);
+ addPendingUidChanges(pendingItemsForUids);
mAms.mUidObserverController.dispatchUidsChanged();
assertEquals("No observers registered, so validateUids should be empty",
- 0, mAms.mUidObserverController.mValidateUids.size());
+ 0, mAms.mUidObserverController.getValidateUidsForTest().size());
final IUidObserver observer = mock(IUidObserver.Stub.class);
when(observer.asBinder()).thenReturn((IBinder) observer);
mAms.registerUidObserver(observer, 0, 0, null);
// Verify that when observers are registered, then validateUids is correctly updated.
- mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids);
+ addPendingUidChanges(pendingItemsForUids);
mAms.mUidObserverController.dispatchUidsChanged();
for (int i = 0; i < pendingItemsForUids.size(); ++i) {
- final UidRecord.ChangeItem item = pendingItemsForUids.get(i);
+ final ChangeRecord item = pendingItemsForUids.get(i);
final UidRecord validateUidRecord =
- mAms.mUidObserverController.mValidateUids.get(item.uid);
+ mAms.mUidObserverController.getValidateUidsForTest().get(item.uid);
if ((item.change & UidRecord.CHANGE_GONE) != 0) {
assertNull("validateUidRecord should be null since the change is either "
+ "CHANGE_GONE or CHANGE_GONE_IDLE", validateUidRecord);
} else {
assertNotNull("validateUidRecord should not be null since the change is neither "
+ "CHANGE_GONE nor CHANGE_GONE_IDLE", validateUidRecord);
- assertEquals("processState: " + item.processState + " curProcState: "
+ assertEquals("processState: " + item.procState + " curProcState: "
+ validateUidRecord.getCurProcState() + " should have been equal",
- item.processState, validateUidRecord.getCurProcState());
- assertEquals("processState: " + item.processState + " setProcState: "
+ item.procState, validateUidRecord.getCurProcState());
+ assertEquals("processState: " + item.procState + " setProcState: "
+ validateUidRecord.getCurProcState() + " should have been equal",
- item.processState, validateUidRecord.setProcState);
+ item.procState, validateUidRecord.setProcState);
if (item.change == UidRecord.CHANGE_IDLE) {
assertTrue("UidRecord.idle should be updated to true for CHANGE_IDLE",
validateUidRecord.idle);
@@ -759,19 +761,19 @@
// Verify that when uid state changes to CHANGE_GONE or CHANGE_GONE_IDLE, then it
// will be removed from validateUids.
assertNotEquals("validateUids should not be empty", 0,
- mAms.mUidObserverController.mValidateUids.size());
+ mAms.mUidObserverController.getValidateUidsForTest().size());
for (int i = 0; i < pendingItemsForUids.size(); ++i) {
- final UidRecord.ChangeItem item = pendingItemsForUids.get(i);
+ final ChangeRecord item = pendingItemsForUids.get(i);
// Assign CHANGE_GONE_IDLE to some items and CHANGE_GONE to the others, using even/odd
// distribution for this assignment.
item.change = (i % 2) == 0 ? (UidRecord.CHANGE_GONE | UidRecord.CHANGE_IDLE)
: UidRecord.CHANGE_GONE;
}
- mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids);
+ addPendingUidChanges(pendingItemsForUids);
mAms.mUidObserverController.dispatchUidsChanged();
assertEquals("validateUids should be empty, size="
- + mAms.mUidObserverController.mValidateUids.size(),
- 0, mAms.mUidObserverController.mValidateUids.size());
+ + mAms.mUidObserverController.getValidateUidsForTest().size(),
+ 0, mAms.mUidObserverController.getValidateUidsForTest().size());
}
@Test
@@ -784,8 +786,6 @@
// Add a pending change for TEST_UID and verify enqueueUidChangeLocked still works as
// expected.
- final UidRecord.ChangeItem changeItem = new UidRecord.ChangeItem();
- uidRecord.pendingChange = changeItem;
uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ2;
verifyLastProcStateSeqUpdated(uidRecord, -1, TEST_PROC_STATE_SEQ2);
}
@@ -793,7 +793,7 @@
@Test
public void testEnqueueUidChangeLocked_nullUidRecord() {
// Use "null" uidRecord to make sure there is no crash.
- mAms.mUidObserverController.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE);
+ mAms.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE);
}
private void verifyLastProcStateSeqUpdated(UidRecord uidRecord, int uid, long curProcstateSeq) {
@@ -802,7 +802,7 @@
final int changeToDispatch = UID_RECORD_CHANGES[i];
// Reset lastProcStateSeqDispatchToObservers after every test.
uidRecord.lastDispatchedProcStateSeq = 0;
- mAms.mUidObserverController.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch);
+ mAms.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch);
// Verify there is no effect on curProcStateSeq.
assertEquals(curProcstateSeq, uidRecord.curProcStateSeq);
if ((changeToDispatch & UidRecord.CHANGE_GONE) != 0) {
@@ -833,16 +833,16 @@
// Reset the current state
mHandler.reset();
- uidRecord.pendingChange = null;
- mAms.mUidObserverController.mPendingUidChanges.clear();
+ clearPendingUidChanges();
- mAms.mUidObserverController.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch);
+ mAms.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch);
- // Verify that UidRecord.pendingChange is updated correctly.
- assertNotNull(uidRecord.pendingChange);
- assertEquals(TEST_UID, uidRecord.pendingChange.uid);
- assertEquals(expectedProcState, uidRecord.pendingChange.processState);
- assertEquals(TEST_PROC_STATE_SEQ1, uidRecord.pendingChange.procStateSeq);
+ // Verify that pendingChange is updated correctly.
+ final ChangeRecord pendingChange = getPendingChange(uidRecord.uid);
+ assertNotNull(pendingChange);
+ assertEquals(TEST_UID, pendingChange.uid);
+ assertEquals(expectedProcState, pendingChange.procState);
+ assertEquals(TEST_PROC_STATE_SEQ1, pendingChange.procStateSeq);
// TODO: Verify that DISPATCH_UIDS_CHANGED_UI_MSG is posted to handler.
}
@@ -923,6 +923,29 @@
mAms.mProcessList.mActiveUids.clear();
}
+ private ChangeRecord getPendingChange(int uid) {
+ final SparseArray<ChangeRecord> pendingChanges =
+ mAms.mUidObserverController.getPendingUidChangesForTest();
+ return pendingChanges.get(uid);
+ }
+
+ private void addPendingUidChange(ChangeRecord record) {
+ mAms.mUidObserverController.getPendingUidChangesForTest().put(record.uid, record);
+ }
+
+ private void addPendingUidChanges(ArrayList<ChangeRecord> changes) {
+ final SparseArray<ChangeRecord> pendingChanges =
+ mAms.mUidObserverController.getPendingUidChangesForTest();
+ for (int i = 0; i < changes.size(); ++i) {
+ final ChangeRecord record = changes.get(i);
+ pendingChanges.put(record.uid, record);
+ }
+ }
+
+ private void clearPendingUidChanges() {
+ mAms.mUidObserverController.getPendingUidChangesForTest().clear();
+ }
+
private static class TestHandler extends Handler {
private static final long WAIT_FOR_MSG_TIMEOUT_MS = 4000; // 4 sec
private static final long WAIT_FOR_MSG_INTERVAL_MS = 400; // 0.4 sec
diff --git a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
new file mode 100644
index 0000000..57c581e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_RECENT;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
+import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.IUidObserver;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.DebugUtils;
+import android.util.Pair;
+import android.util.SparseArray;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.am.UidObserverController.ChangeRecord;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+@SmallTest
+public class UidObserverControllerTest {
+ private static final int TEST_UID1 = 1111;
+ private static final int TEST_UID2 = 2222;
+ private static final int TEST_UID3 = 3333;
+
+ private static final String TEST_PKG1 = "com.example1";
+ private static final String TEST_PKG2 = "com.example2";
+ private static final String TEST_PKG3 = "com.example3";
+
+ private UidObserverController mUidObserverController;
+
+ @Before
+ public void setUp() {
+ mUidObserverController = new UidObserverController(Mockito.mock(Handler.class));
+ }
+
+ @Test
+ public void testEnqueueUidChange() {
+ int change = mUidObserverController.enqueueUidChange(TEST_UID1, UidRecord.CHANGE_ACTIVE,
+ PROCESS_STATE_FOREGROUND_SERVICE, PROCESS_CAPABILITY_ALL, 0, false);
+ assertEquals("expected=ACTIVE,actual=" + changeToStr(change),
+ UidRecord.CHANGE_ACTIVE, change);
+ assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE,
+ PROCESS_CAPABILITY_ALL, 0, false);
+ assertNull(getPendingChange(TEST_UID2));
+
+ change = mUidObserverController.enqueueUidChange(TEST_UID2, UidRecord.CHANGE_CACHED,
+ PROCESS_STATE_CACHED_RECENT, PROCESS_CAPABILITY_NONE, 99, true);
+ assertEquals("expected=ACTIVE,actual=" + changeToStr(change),
+ UidRecord.CHANGE_CACHED, change);
+ assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE,
+ PROCESS_CAPABILITY_ALL, 0, false);
+ assertPendingChange(TEST_UID2, UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT,
+ PROCESS_CAPABILITY_NONE, 99, true);
+
+ change = mUidObserverController.enqueueUidChange(TEST_UID1, UidRecord.CHANGE_UNCACHED,
+ PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false);
+ assertEquals("expected=ACTIVE|UNCACHED,actual=" + changeToStr(change),
+ UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, change);
+ assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED,
+ PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false);
+ assertPendingChange(TEST_UID2, UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT,
+ PROCESS_CAPABILITY_NONE, 99, true);
+ }
+
+ @Test
+ public void testMergeWithPendingChange() {
+ final SparseArray<Pair<Integer, Integer>> changesToVerify = new SparseArray<>();
+
+ changesToVerify.put(UidRecord.CHANGE_ACTIVE,
+ Pair.create(UidRecord.CHANGE_ACTIVE, UidRecord.CHANGE_IDLE));
+ changesToVerify.put(UidRecord.CHANGE_IDLE,
+ Pair.create(UidRecord.CHANGE_IDLE, UidRecord.CHANGE_ACTIVE));
+ changesToVerify.put(UidRecord.CHANGE_CACHED,
+ Pair.create(UidRecord.CHANGE_CACHED, UidRecord.CHANGE_UNCACHED));
+ changesToVerify.put(UidRecord.CHANGE_UNCACHED,
+ Pair.create(UidRecord.CHANGE_UNCACHED, UidRecord.CHANGE_CACHED));
+ changesToVerify.put(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED,
+ Pair.create(UidRecord.CHANGE_ACTIVE, UidRecord.CHANGE_UNCACHED));
+ changesToVerify.put(UidRecord.CHANGE_IDLE | UidRecord.CHANGE_CACHED,
+ Pair.create(UidRecord.CHANGE_IDLE, UidRecord.CHANGE_CACHED));
+ changesToVerify.put(UidRecord.CHANGE_GONE,
+ Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_ACTIVE));
+ changesToVerify.put(UidRecord.CHANGE_GONE,
+ Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_CACHED));
+
+ for (int i = 0; i < changesToVerify.size(); ++i) {
+ final int expectedChange = changesToVerify.keyAt(i);
+ final int currentChange = changesToVerify.valueAt(i).first;
+ final int pendingChange = changesToVerify.valueAt(i).second;
+ assertEquals("current=" + changeToStr(currentChange) + ", pending="
+ + changeToStr(pendingChange) + "exp=" + changeToStr(expectedChange),
+ expectedChange, UidObserverController.mergeWithPendingChange(
+ currentChange, pendingChange));
+ }
+ }
+
+ @Test
+ public void testDispatchUidsChanged() throws RemoteException {
+ addPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_PROCSTATE,
+ PROCESS_STATE_TOP, 0, PROCESS_CAPABILITY_ALL, false);
+
+ final IUidObserver observer1 = Mockito.mock(IUidObserver.Stub.class);
+ registerObserver(observer1,
+ ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_ACTIVE,
+ PROCESS_STATE_IMPORTANT_FOREGROUND, TEST_PKG2, TEST_UID2);
+ final IUidObserver observer2 = Mockito.mock(IUidObserver.Stub.class);
+ registerObserver(observer2, ActivityManager.UID_OBSERVER_PROCSTATE,
+ PROCESS_STATE_SERVICE, TEST_PKG3, TEST_UID3);
+
+ mUidObserverController.dispatchUidsChanged();
+ verify(observer1).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP,
+ 0, PROCESS_CAPABILITY_ALL);
+ verify(observer1).onUidActive(TEST_UID1);
+ verifyNoMoreInteractions(observer1);
+ verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP,
+ 0, PROCESS_CAPABILITY_ALL);
+ verifyNoMoreInteractions(observer2);
+
+ addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_IMPORTANT_BACKGROUND,
+ 99, PROCESS_CAPABILITY_FOREGROUND_LOCATION, false);
+ mUidObserverController.dispatchUidsChanged();
+ verify(observer1).onUidStateChanged(TEST_UID1, PROCESS_STATE_IMPORTANT_BACKGROUND,
+ 99, PROCESS_CAPABILITY_FOREGROUND_LOCATION);
+ verifyNoMoreInteractions(observer1);
+ verifyNoMoreInteractions(observer2);
+
+ addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_RECEIVER,
+ 111, PROCESS_CAPABILITY_NONE, false);
+ mUidObserverController.dispatchUidsChanged();
+ verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_RECEIVER,
+ 111, PROCESS_CAPABILITY_NONE);
+ verifyNoMoreInteractions(observer1);
+ verifyNoMoreInteractions(observer2);
+
+ unregisterObserver(observer1);
+
+ addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_TOP,
+ 112, PROCESS_CAPABILITY_ALL, false);
+ mUidObserverController.dispatchUidsChanged();
+ verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP,
+ 112, PROCESS_CAPABILITY_ALL);
+ verifyNoMoreInteractions(observer1);
+ verifyNoMoreInteractions(observer2);
+
+ unregisterObserver(observer2);
+
+ addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_CACHED_RECENT,
+ 112, PROCESS_CAPABILITY_NONE, false);
+ mUidObserverController.dispatchUidsChanged();
+ verifyNoMoreInteractions(observer1);
+ verifyNoMoreInteractions(observer2);
+ }
+
+ private void registerObserver(IUidObserver observer, int which, int cutpoint,
+ String callingPackage, int callingUid) {
+ when(observer.asBinder()).thenReturn((IBinder) observer);
+ mUidObserverController.register(observer, which, cutpoint, callingPackage, callingUid);
+ Mockito.reset(observer);
+ }
+
+ private void unregisterObserver(IUidObserver observer) {
+ when(observer.asBinder()).thenReturn((IBinder) observer);
+ mUidObserverController.unregister(observer);
+ Mockito.reset(observer);
+ }
+
+ private void addPendingChange(int uid, int change, int procState, long procStateSeq,
+ int capability, boolean ephemeral) {
+ final ChangeRecord record = new ChangeRecord();
+ record.uid = uid;
+ record.change = change;
+ record.procState = procState;
+ record.procStateSeq = procStateSeq;
+ record.capability = capability;
+ record.ephemeral = ephemeral;
+ mUidObserverController.getPendingUidChangesForTest().put(uid, record);
+ }
+
+ private void assertPendingChange(int uid, int change, int procState, long procStateSeq,
+ int capability, boolean ephemeral) {
+ final ChangeRecord record = getPendingChange(uid);
+ assertNotNull(record);
+ assertEquals(change, record.change);
+ assertEquals(procState, record.procState);
+ assertEquals(procStateSeq, record.procStateSeq);
+ assertEquals(capability, record.capability);
+ assertEquals(ephemeral, record.ephemeral);
+ }
+
+ private ChangeRecord getPendingChange(int uid) {
+ return mUidObserverController.getPendingUidChangesForTest().get(uid);
+ }
+
+ private static String changeToStr(int change) {
+ return DebugUtils.flagsToString(UidRecord.class, "CHANGE_", change);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java
index 24f7830..081bfb5 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java
@@ -50,8 +50,7 @@
@Before
public void setUp() throws Exception {
- mAppSearchImpl = new AppSearchImpl(mTemporaryFolder.newFolder());
- mAppSearchImpl.initialize();
+ mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
}
/**
@@ -288,7 +287,8 @@
SchemaProto finalSchemaProto = schemaProto;
AppSearchException e = expectThrows(AppSearchException.class, () ->
mAppSearchImpl.setSchema("database1", finalSchemaProto, /*forceOverride=*/false));
- assertThat(e).hasMessageThat().isEqualTo("Schema is incompatible.");
+ assertThat(e).hasMessageThat().contains("Schema is incompatible");
+ assertThat(e).hasMessageThat().contains("Deleted types: [database1/Document]");
// ForceOverride to delete.
mAppSearchImpl.setSchema("database1", finalSchemaProto, /*forceOverride=*/true);
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index dbaa482..9ee1205 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -20,7 +20,6 @@
import static com.android.server.attention.AttentionManagerService.ATTENTION_CACHE_BUFFER_SIZE;
import static com.android.server.attention.AttentionManagerService.DEFAULT_STALE_AFTER_MILLIS;
-import static com.android.server.attention.AttentionManagerService.DEVICE_CONFIG_MAX_STALENESS_MILLIS;
import static com.android.server.attention.AttentionManagerService.KEY_STALE_AFTER_MILLIS;
import static com.google.common.truth.Truth.assertThat;
@@ -40,7 +39,6 @@
import android.os.IThermalService;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.service.attention.IAttentionCallback;
import android.service.attention.IAttentionService;
@@ -118,7 +116,7 @@
@Test
public void testCheckAttention_returnFalseWhenPowerManagerNotInteract() throws RemoteException {
- doReturn(true).when(mSpyAttentionManager).isAttentionServiceSupported();
+ mSpyAttentionManager.mIsServiceEnabled = true;
doReturn(false).when(mMockIPowerManager).isInteractive();
AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class);
assertThat(mSpyAttentionManager.checkAttention(mTimeout, callback)).isFalse();
@@ -126,7 +124,7 @@
@Test
public void testCheckAttention_callOnSuccess() throws RemoteException {
- doReturn(true).when(mSpyAttentionManager).isAttentionServiceSupported();
+ mSpyAttentionManager.mIsServiceEnabled = true;
doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
doReturn(true).when(mMockIPowerManager).isInteractive();
doNothing().when(mSpyAttentionManager).freeIfInactiveLocked();
@@ -207,38 +205,6 @@
DEFAULT_STALE_AFTER_MILLIS);
}
- @Test
- public void testEnsureDeviceConfigCachedValuesFreshness_doesNotCallDeviceConfigTooFrequently() {
- DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
- KEY_STALE_AFTER_MILLIS, String.valueOf(DEFAULT_STALE_AFTER_MILLIS), false);
- assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
- DEFAULT_STALE_AFTER_MILLIS);
-
- DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
- KEY_STALE_AFTER_MILLIS, "123", false);
-
- // New value is ignored
- assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
- DEFAULT_STALE_AFTER_MILLIS);
- }
-
-
- @Test
- public void testEnsureDeviceConfigCachedValuesFreshness_refreshesWhenStale() {
- DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
- KEY_STALE_AFTER_MILLIS, String.valueOf(DEFAULT_STALE_AFTER_MILLIS), false);
- assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
- DEFAULT_STALE_AFTER_MILLIS);
-
- DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
- KEY_STALE_AFTER_MILLIS, "123", false);
- mSpyAttentionManager.mLastReadDeviceConfigMillis =
- SystemClock.elapsedRealtime() - (DEVICE_CONFIG_MAX_STALENESS_MILLIS + 1);
-
- // Values are refreshed
- assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(123);
- }
-
private class MockIAttentionService implements IAttentionService {
public void checkAttention(IAttentionCallback callback) throws RemoteException {
callback.onSuccess(0, 0);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/HardwareAuthTokenUtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/HardwareAuthTokenUtilsTest.java
new file mode 100644
index 0000000..84987e6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/HardwareAuthTokenUtilsTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.hardware.keymaster.HardwareAuthToken;
+import android.hardware.keymaster.Timestamp;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@Presubmit
+@SmallTest
+public class HardwareAuthTokenUtilsTest {
+
+ @Test
+ public void testByteArrayLoopBack() {
+ final byte[] hat = new byte[69];
+ for (int i = 0; i < 69; i++) {
+ hat[i] = (byte) i;
+ }
+
+ final HardwareAuthToken hardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hat);
+ final byte[] hat2 = HardwareAuthTokenUtils.toByteArray(hardwareAuthToken);
+
+ for (int i = 0; i < hat.length; i++) {
+ assertEquals(hat[i], hat2[i]);
+ }
+ }
+
+ @Test
+ public void testHardwareAuthTokenLoopBack() {
+ final long testChallenge = 1000L;
+ final long testUserId = 2000L;
+ final long testAuthenticatorId = 3000L;
+ final int testAuthenticatorType = 4000;
+ final long testTimestamp = 5000L;
+
+ final HardwareAuthToken hardwareAuthToken = new HardwareAuthToken();
+ hardwareAuthToken.challenge = testChallenge;
+ hardwareAuthToken.userId = testUserId;
+ hardwareAuthToken.authenticatorId = testAuthenticatorId;
+ hardwareAuthToken.authenticatorType = testAuthenticatorType;
+ hardwareAuthToken.timestamp = new Timestamp();
+ hardwareAuthToken.timestamp.milliSeconds = testTimestamp;
+ hardwareAuthToken.mac = new byte[32];
+
+ for (int i = 0; i < hardwareAuthToken.mac.length; i++) {
+ hardwareAuthToken.mac[i] = (byte) i;
+ }
+
+ final byte[] hat = HardwareAuthTokenUtils.toByteArray(hardwareAuthToken);
+ final HardwareAuthToken hardwareAuthToken2 =
+ HardwareAuthTokenUtils.toHardwareAuthToken(hat);
+
+ assertEquals(testChallenge, hardwareAuthToken2.challenge);
+ assertEquals(testUserId, hardwareAuthToken2.userId);
+ assertEquals(testAuthenticatorId, hardwareAuthToken2.authenticatorId);
+ assertEquals(testAuthenticatorType, hardwareAuthToken2.authenticatorType);
+ assertEquals(testTimestamp, hardwareAuthToken2.timestamp.milliSeconds);
+
+ for (int i = 0; i < hardwareAuthToken.mac.length; i++) {
+ assertEquals(hardwareAuthToken.mac[i], hardwareAuthToken2.mac[i]);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 59d4e2a..058794a 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -16,7 +16,7 @@
package com.android.server.devicestate;
-import static com.android.server.devicestate.DeviceStateManagerService.INVALID_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertThrows;
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index b69cc47..ec747ac 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -83,8 +83,8 @@
BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
- mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mContext,
- mDisplayDeviceConfig);
+ mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mContext
+ );
controller.setLoggingEnabled(true);
// Configure the brightness controller and grab an instance of the sensor listener,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 7cbf571..ef98b98 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -814,7 +814,7 @@
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
- assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+ assertThat(mNativeWrapper.getResultMessages()).containsAtLeast(pressed, released);
}
@Test
@@ -834,7 +834,7 @@
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
- assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+ assertThat(mNativeWrapper.getResultMessages()).containsAtLeast(pressed, released);
}
@Test
@@ -853,7 +853,7 @@
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
- assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+ assertThat(mNativeWrapper.getResultMessages()).containsAtLeast(pressed, released);
}
@Test
@@ -872,7 +872,7 @@
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
- assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+ assertThat(mNativeWrapper.getResultMessages()).containsAtLeast(pressed, released);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
index cdff97b..9ab762a 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
@@ -76,7 +76,7 @@
// Act and assert
assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
- asList(imeService))).isSameAs(imeService);
+ asList(imeService))).isSameInstanceAs(imeService);
}
private ResolveInfo buildResolveInfo(String permission, int flags) {
diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
index 41be54a..f26e094 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
@@ -194,7 +194,7 @@
assertThat(rulesFetched.size())
.isEqualTo(INDEXING_BLOCK_SIZE * 2 + unindexedRuleCount);
assertThat(rulesFetched)
- .containsAllOf(
+ .containsAtLeast(
getPackageNameIndexedRule(installedPackageName),
getAppCertificateIndexedRule(installedAppCertificate));
}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java b/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
index 192ade7..4810563 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
@@ -45,6 +45,7 @@
return new ConfigurationInternal.Builder(userId)
.setUserConfigAllowed(true)
.setAutoDetectionSupported(true)
+ .setGeoDetectionSupported(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
.setGeoDetectionEnabled(geoDetectionEnabled)
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
index 6921bb2..8d5687c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
@@ -71,7 +71,7 @@
Map<String, Pair<SecretKey, byte[]>> filteredKeys =
mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
- assertThat(filteredKeys.entrySet()).containsAllIn(rawKeys.entrySet());
+ assertThat(filteredKeys.entrySet()).containsAtLeastElementsIn(rawKeys.entrySet());
}
@Test
@@ -85,7 +85,7 @@
Map<String, Pair<SecretKey, byte[]>> filteredKeys =
mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
- assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+ assertThat(rawKeys.entrySet()).containsAtLeastElementsIn(filteredKeys.entrySet());
}
@Test
@@ -100,7 +100,7 @@
Map<String, Pair<SecretKey, byte[]>> filteredKeys =
mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
- assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+ assertThat(rawKeys.entrySet()).containsAtLeastElementsIn(filteredKeys.entrySet());
}
@Test
@@ -122,7 +122,7 @@
Map<String, Pair<SecretKey, byte[]>> filteredKeys =
mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
- assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+ assertThat(rawKeys.entrySet()).containsAtLeastElementsIn(filteredKeys.entrySet());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java
index 9836c64..b0cb2ea 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java
@@ -70,7 +70,7 @@
CertXml certXml = CertXml.parse(certXmlBytes);
List<X509Certificate> endpointCerts = certXml.getAllEndpointCerts();
assertThat(endpointCerts).hasSize(3);
- assertThat(endpointCerts).containsAllOf(TestData.LEAF_CERT_1, TestData.LEAF_CERT_2);
+ assertThat(endpointCerts).containsAtLeast(TestData.LEAF_CERT_1, TestData.LEAF_CERT_2);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
index 154d42c..3a292de 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
@@ -72,7 +72,8 @@
@JvmStatic
fun checkAllCasesHandled() {
// Assert that all states have been tested at least once.
- assertThat(CASES.map { it.state }.distinct()).containsAllIn(ActorState.values())
+ assertThat(CASES.map { it.state }.distinct())
+ .containsAtLeastElementsIn(ActorState.values())
}
@BeforeClass
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 f37054d..9ce4ee0 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
@@ -41,6 +41,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -100,6 +101,7 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.function.Function;
@RunWith(JUnit4.class)
public final class DataManagerTest {
@@ -660,6 +662,26 @@
}
@Test
+ public void testConversationLastEventTimestampUpdate() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ PackageData packageData = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY);
+ ConversationInfo conversationInfo =
+ packageData.getConversationStore().getConversation(TEST_SHORTCUT_ID);
+ Event event = new Event(123L, Event.TYPE_IN_APP_CONVERSATION);
+
+ mInjector.mUsageStatsQueryHelper.mEventListener.onEvent(packageData, conversationInfo,
+ event);
+ ConversationInfo newConversationInfo =
+ packageData.getConversationStore().getConversation(TEST_SHORTCUT_ID);
+ assertEquals(123L, newConversationInfo.getLastEventTimestamp());
+ }
+
+ @Test
public void testDeleteUninstalledPackageDataOnPackageRemoved() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
@@ -1122,6 +1144,18 @@
}
}
+ private class TestUsageStatsQueryHelper extends UsageStatsQueryHelper {
+
+ private final EventListener mEventListener;
+
+ TestUsageStatsQueryHelper(int userId,
+ Function<String, PackageData> packageDataGetter,
+ EventListener eventListener) {
+ super(userId, packageDataGetter, eventListener);
+ mEventListener = eventListener;
+ }
+ }
+
private class TestInjector extends DataManager.Injector {
private final TestContactsQueryHelper mContactsQueryHelper =
@@ -1129,6 +1163,7 @@
private TestCallLogQueryHelper mCallLogQueryHelper;
private TestMmsQueryHelper mMmsQueryHelper;
private TestSmsQueryHelper mSmsQueryHelper;
+ private TestUsageStatsQueryHelper mUsageStatsQueryHelper;
@Override
ScheduledExecutorService createScheduledExecutor() {
@@ -1165,5 +1200,14 @@
mSmsQueryHelper = new TestSmsQueryHelper(context, eventConsumer);
return mSmsQueryHelper;
}
+
+ @Override
+ UsageStatsQueryHelper createUsageStatsQueryHelper(@UserIdInt int userId,
+ Function<String, PackageData> packageDataGetter,
+ UsageStatsQueryHelper.EventListener eventListener) {
+ mUsageStatsQueryHelper =
+ new TestUsageStatsQueryHelper(userId, packageDataGetter, eventListener);
+ return mUsageStatsQueryHelper;
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
index e968607..baa74b7 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
@@ -26,6 +26,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
@@ -72,6 +73,8 @@
@Mock
private UsageStatsManagerInternal mUsageStatsManagerInternal;
+ @Mock
+ private UsageStatsQueryHelper.EventListener mEventListener;
private TestPackageData mPackageData;
private UsageStatsQueryHelper mHelper;
@@ -93,7 +96,7 @@
.setLocusId(LOCUS_ID_1)
.build();
- mHelper = new UsageStatsQueryHelper(USER_ID_PRIMARY, pkg -> mPackageData);
+ mHelper = new UsageStatsQueryHelper(USER_ID_PRIMARY, pkg -> mPackageData, mEventListener);
}
@After
@@ -131,6 +134,8 @@
Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
assertEquals(1, events.size());
assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+ verify(mEventListener).onEvent(
+ mPackageData, mPackageData.mConversationStore.mConversationInfo, events.get(0));
}
@Test
@@ -145,6 +150,8 @@
Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
assertEquals(1, events.size());
assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+ verify(mEventListener).onEvent(
+ mPackageData, mPackageData.mConversationStore.mConversationInfo, events.get(0));
}
@Test
@@ -159,6 +166,8 @@
Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
assertEquals(1, events.size());
assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+ verify(mEventListener).onEvent(
+ mPackageData, mPackageData.mConversationStore.mConversationInfo, events.get(0));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 44bb58f..7579956 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -41,6 +41,7 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Slog;
+import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -58,6 +59,8 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import javax.annotation.concurrent.GuardedBy;
+
/** Test {@link UserManager} functionality. */
@RunWith(AndroidJUnit4.class)
public final class UserManagerTest {
@@ -134,7 +137,7 @@
@SmallTest
@Test
public void testHasSystemUser() throws Exception {
- assertThat(findUser(UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue();
}
@MediumTest
@@ -164,9 +167,9 @@
assertThat(user1).isNotNull();
assertThat(user2).isNotNull();
- assertThat(findUser(UserHandle.USER_SYSTEM)).isTrue();
- assertThat(findUser(user1.id)).isTrue();
- assertThat(findUser(user2.id)).isTrue();
+ assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue();
+ assertThat(hasUser(user1.id)).isTrue();
+ assertThat(hasUser(user2.id)).isTrue();
}
@MediumTest
@@ -175,7 +178,7 @@
UserInfo userInfo = createUser("Guest 1", UserInfo.FLAG_GUEST);
removeUser(userInfo.id);
- assertThat(findUser(userInfo.id)).isFalse();
+ assertThat(hasUser(userInfo.id)).isFalse();
}
@MediumTest
@@ -199,7 +202,7 @@
}
}
- assertThat(findUser(userInfo.id)).isFalse();
+ assertThat(hasUser(userInfo.id)).isFalse();
}
@MediumTest
@@ -208,6 +211,79 @@
assertThrows(IllegalArgumentException.class, () -> mUserManager.removeUser(null));
}
+ @MediumTest
+ @Test
+ public void testRemoveUserOrSetEphemeral_restrictedReturnsError() throws Exception {
+ final int currentUser = ActivityManager.getCurrentUser();
+ final UserInfo user1 = createUser("User 1", /* flags= */ 0);
+ mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ true,
+ asHandle(currentUser));
+ try {
+ assertThat(mUserManager.removeUserOrSetEphemeral(user1.id)).isEqualTo(
+ UserManagerService.REMOVE_RESULT_ERROR);
+ } finally {
+ mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ false,
+ asHandle(currentUser));
+ }
+
+ assertThat(hasUser(user1.id)).isTrue();
+ assertThat(getUser(user1.id).isEphemeral()).isFalse();
+ }
+
+ @MediumTest
+ @Test
+ public void testRemoveUserOrSetEphemeral_systemUserReturnsError() throws Exception {
+ assertThat(mUserManager.removeUserOrSetEphemeral(UserHandle.USER_SYSTEM)).isEqualTo(
+ UserManagerService.REMOVE_RESULT_ERROR);
+
+ assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue();
+ }
+
+ @MediumTest
+ @Test
+ public void testRemoveUserOrSetEphemeral_invalidUserReturnsError() throws Exception {
+ assertThat(hasUser(Integer.MAX_VALUE)).isFalse();
+ assertThat(mUserManager.removeUserOrSetEphemeral(Integer.MAX_VALUE)).isEqualTo(
+ UserManagerService.REMOVE_RESULT_ERROR);
+ }
+
+ @MediumTest
+ @Test
+ public void testRemoveUserOrSetEphemeral_currentUserSetEphemeral() throws Exception {
+ final int startUser = ActivityManager.getCurrentUser();
+ final UserInfo user1 = createUser("User 1", /* flags= */ 0);
+ // Switch to the user just created.
+ switchUser(user1.id, null, /* ignoreHandle= */ true);
+
+ assertThat(mUserManager.removeUserOrSetEphemeral(user1.id)).isEqualTo(
+ UserManagerService.REMOVE_RESULT_SET_EPHEMERAL);
+
+ assertThat(hasUser(user1.id)).isTrue();
+ assertThat(getUser(user1.id).isEphemeral()).isTrue();
+
+ // Switch back to the starting user.
+ switchUser(startUser, null, /* ignoreHandle= */ true);
+
+ // User is removed once switch is complete
+ synchronized (mUserRemoveLock) {
+ waitForUserRemovalLocked(user1.id);
+ }
+ assertThat(hasUser(user1.id)).isFalse();
+ }
+
+ @MediumTest
+ @Test
+ public void testRemoveUserOrSetEphemeral_nonCurrentUserRemoved() throws Exception {
+ final UserInfo user1 = createUser("User 1", /* flags= */ 0);
+ synchronized (mUserRemoveLock) {
+ assertThat(mUserManager.removeUserOrSetEphemeral(user1.id)).isEqualTo(
+ UserManagerService.REMOVE_RESULT_REMOVED);
+ waitForUserRemovalLocked(user1.id);
+ }
+
+ assertThat(hasUser(user1.id)).isFalse();
+ }
+
/** Tests creating a FULL user via specifying userType. */
@MediumTest
@Test
@@ -608,15 +684,20 @@
() -> mUserManager.getUserCreationTime(asHandle(user.id)));
}
- private boolean findUser(int id) {
+ @Nullable
+ private UserInfo getUser(int id) {
List<UserInfo> list = mUserManager.getUsers();
for (UserInfo user : list) {
if (user.id == id) {
- return true;
+ return user;
}
}
- return false;
+ return null;
+ }
+
+ private boolean hasUser(int id) {
+ return getUser(id) != null;
}
@MediumTest
@@ -639,7 +720,7 @@
UserInfo user1 = createUser("User 1", 0);
UserInfo user2 = createUser("User 2", 0);
long[] serialNumbersOfUsers = mUserManager.getSerialNumbersOfUsers(false);
- assertThat(serialNumbersOfUsers).asList().containsAllOf(
+ assertThat(serialNumbersOfUsers).asList().containsAtLeast(
(long) user1.serialNumber, (long) user2.serialNumber);
}
@@ -918,17 +999,22 @@
private void removeUser(int userId) {
synchronized (mUserRemoveLock) {
mUserManager.removeUser(userId);
- long time = System.currentTimeMillis();
- while (mUserManager.getUserInfo(userId) != null) {
- try {
- mUserRemoveLock.wait(REMOVE_CHECK_INTERVAL_MILLIS);
- } catch (InterruptedException ie) {
- Thread.currentThread().interrupt();
- return;
- }
- if (System.currentTimeMillis() - time > REMOVE_TIMEOUT_MILLIS) {
- fail("Timeout waiting for removeUser. userId = " + userId);
- }
+ waitForUserRemovalLocked(userId);
+ }
+ }
+
+ @GuardedBy("mUserRemoveLock")
+ private void waitForUserRemovalLocked(int userId) {
+ long time = System.currentTimeMillis();
+ while (mUserManager.getUserInfo(userId) != null) {
+ try {
+ mUserRemoveLock.wait(REMOVE_CHECK_INTERVAL_MILLIS);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ if (System.currentTimeMillis() - time > REMOVE_TIMEOUT_MILLIS) {
+ fail("Timeout waiting for removeUser. userId = " + userId);
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
index b6a0979..eedc978 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -46,41 +46,25 @@
private static final String INSTALLER = "some.installer";
private static final Correspondence<VersionedPackage, VersionedPackage> VER_PKG_CORR =
- new Correspondence<VersionedPackage, VersionedPackage>() {
- @Override
- public boolean compare(VersionedPackage a, VersionedPackage b) {
- if (a == null || b == null) {
- return a == b;
- }
- return a.equals(b);
+ Correspondence.from((VersionedPackage a, VersionedPackage b) -> {
+ if (a == null || b == null) {
+ return a == b;
}
-
- @Override
- public String toString() {
- return "is the same as";
- }
- };
+ return a.equals(b);
+ }, "is the same as");
private static final Correspondence<PackageRollbackInfo.RestoreInfo,
PackageRollbackInfo.RestoreInfo>
RESTORE_INFO_CORR =
- new Correspondence<PackageRollbackInfo.RestoreInfo, PackageRollbackInfo.RestoreInfo>() {
- @Override
- public boolean compare(PackageRollbackInfo.RestoreInfo a,
- PackageRollbackInfo.RestoreInfo b) {
- if (a == null || b == null) {
- return a == b;
- }
- return a.userId == b.userId
- && a.appId == b.appId
- && Objects.equals(a.seInfo, b.seInfo);
+ Correspondence.from((PackageRollbackInfo.RestoreInfo a,
+ PackageRollbackInfo.RestoreInfo b) -> {
+ if (a == null || b == null) {
+ return a == b;
}
-
- @Override
- public String toString() {
- return "is the same as";
- }
- };
+ return a.userId == b.userId
+ && a.appId == b.appId
+ && Objects.equals(a.seInfo, b.seInfo);
+ }, "is the same as");
private static final String JSON_ROLLBACK_NO_EXT = "{'info':{'rollbackId':123,'packages':"
+ "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55},"
diff --git a/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java b/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
index 46224cb..8fb2e68 100644
--- a/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
@@ -132,7 +132,7 @@
appSizes.getLong(i), cacheSizes.getLong(i));
apps.add(app);
}
- assertThat(apps).containsAllOf(new AppSizeGrouping("com.test.app", 1100, 20),
+ assertThat(apps).containsAtLeast(new AppSizeGrouping("com.test.app", 1100, 20),
new AppSizeGrouping("com.test.app2", 11, 2));
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 3fc294d..682a80c 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -47,6 +47,7 @@
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(true)
.setAutoDetectionSupported(true)
+ .setGeoDetectionSupported(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
.setGeoDetectionEnabled(true)
@@ -108,6 +109,7 @@
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(false)
.setAutoDetectionSupported(true)
+ .setGeoDetectionSupported(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
.setGeoDetectionEnabled(true)
@@ -169,6 +171,7 @@
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(true)
.setAutoDetectionSupported(false)
+ .setGeoDetectionSupported(false)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
.setGeoDetectionEnabled(true)
@@ -220,4 +223,67 @@
assertTrue(configuration.isGeoDetectionEnabled());
}
}
+
+ /**
+ * Tests when {@link ConfigurationInternal#isAutoDetectionSupported()} is true, but
+ * {@link ConfigurationInternal#isGeoDetectionSupported()} is false.
+ */
+ @Test
+ public void test_geoDetectNotSupported() {
+ ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+ .setUserConfigAllowed(true)
+ .setAutoDetectionSupported(true)
+ .setGeoDetectionSupported(false)
+ .setAutoDetectionEnabled(true)
+ .setLocationEnabled(true)
+ .setGeoDetectionEnabled(true)
+ .build();
+ {
+ ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
+ .setAutoDetectionEnabled(true)
+ .build();
+ assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
+ assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
+ assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior());
+ assertFalse(autoOnConfig.getGeoDetectionEnabledBehavior());
+
+ TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
+ autoOnConfig.createCapabilitiesAndConfig();
+
+ TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
+ assertEquals(CAPABILITY_POSSESSED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_NOT_SUPPORTED,
+ capabilities.getConfigureGeoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_NOT_APPLICABLE,
+ capabilities.getSuggestManualTimeZoneCapability());
+
+ TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration();
+ assertTrue(configuration.isAutoDetectionEnabled());
+ assertTrue(configuration.isGeoDetectionEnabled());
+ }
+ {
+ ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
+ .setAutoDetectionEnabled(false)
+ .build();
+ assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
+ assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
+ assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior());
+ assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior());
+
+ TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
+ autoOffConfig.createCapabilitiesAndConfig();
+
+ TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
+ assertEquals(CAPABILITY_POSSESSED,
+ capabilities.getConfigureAutoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_NOT_SUPPORTED,
+ capabilities.getConfigureGeoDetectionEnabledCapability());
+ assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZoneCapability());
+
+ TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration();
+ assertFalse(configuration.isAutoDetectionEnabled());
+ assertTrue(configuration.isGeoDetectionEnabled());
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index cb27657..d2452ea 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -365,6 +365,7 @@
final boolean geoDetectionEnabled = autoDetectionEnabled;
return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setAutoDetectionSupported(true)
+ .setGeoDetectionSupported(true)
.setUserConfigAllowed(true)
.setAutoDetectionEnabled(autoDetectionEnabled)
.setLocationEnabled(geoDetectionEnabled)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 296aa73..a6ffd20 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -91,6 +91,7 @@
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(false)
.setAutoDetectionSupported(true)
+ .setGeoDetectionSupported(true)
.setAutoDetectionEnabled(false)
.setLocationEnabled(true)
.setGeoDetectionEnabled(false)
@@ -100,6 +101,7 @@
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(false)
.setAutoDetectionSupported(true)
+ .setGeoDetectionSupported(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
.setGeoDetectionEnabled(true)
@@ -109,32 +111,36 @@
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(true)
.setAutoDetectionSupported(false)
+ .setGeoDetectionSupported(false)
.setAutoDetectionEnabled(false)
.setLocationEnabled(true)
.setGeoDetectionEnabled(false)
.build();
+ private static final ConfigurationInternal CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED =
+ new ConfigurationInternal.Builder(USER_ID)
+ .setUserConfigAllowed(true)
+ .setAutoDetectionSupported(true)
+ .setGeoDetectionSupported(false)
+ .setAutoDetectionEnabled(true)
+ .setLocationEnabled(true)
+ .setGeoDetectionEnabled(true)
+ .build();
+
private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_DISABLED =
new ConfigurationInternal.Builder(USER_ID)
.setUserConfigAllowed(true)
.setAutoDetectionSupported(true)
+ .setGeoDetectionSupported(true)
.setAutoDetectionEnabled(false)
.setLocationEnabled(true)
.setGeoDetectionEnabled(false)
.build();
- private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_ENABLED =
- new ConfigurationInternal.Builder(USER_ID)
- .setUserConfigAllowed(true)
- .setAutoDetectionSupported(true)
- .setAutoDetectionEnabled(false)
- .setLocationEnabled(true)
- .setGeoDetectionEnabled(true)
- .build();
-
private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_DISABLED =
new ConfigurationInternal.Builder(USER_ID)
.setAutoDetectionSupported(true)
+ .setGeoDetectionSupported(true)
.setUserConfigAllowed(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
@@ -144,6 +150,7 @@
private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_ENABLED =
new ConfigurationInternal.Builder(USER_ID)
.setAutoDetectionSupported(true)
+ .setGeoDetectionSupported(true)
.setUserConfigAllowed(true)
.setAutoDetectionEnabled(true)
.setLocationEnabled(true)
@@ -223,14 +230,14 @@
// The settings should not have been changed: user shouldn't have the capabilities.
script.verifyConfigurationNotChanged();
- // Update the configuration with auto detection enabled.
+ // Try to update the configuration with auto detection enabled.
script.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */);
// The settings should not have been changed: user shouldn't have the capabilities.
script.verifyConfigurationNotChanged();
- // Try to update the configuration to enable geolocation time zone detection.
+ // Try to update the configuration to enable geolocation time zone detection.
script.simulateUpdateConfiguration(
USER_ID, CONFIG_GEO_DETECTION_ENABLED, false /* expectedResult */);
@@ -249,7 +256,7 @@
// The settings should not have been changed: user shouldn't have the capabilities.
script.verifyConfigurationNotChanged();
- // Update the configuration with auto detection enabled.
+ // Try to update the configuration with auto detection enabled.
script.simulateUpdateConfiguration(
USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */);
@@ -258,6 +265,38 @@
}
@Test
+ public void testUpdateConfiguration_autoDetectSupportedGeoNotSupported() {
+ Script script = new Script().initializeConfig(CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED);
+
+ // Update the configuration with auto detection disabled.
+ script.simulateUpdateConfiguration(
+ USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */);
+
+ // The settings should have been changed and the StrategyListener onChange() called.
+ ConfigurationInternal expectedConfig =
+ new ConfigurationInternal.Builder(CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED)
+ .setAutoDetectionEnabled(false)
+ .build();
+ script.verifyConfigurationChangedAndReset(expectedConfig);
+
+ // Try to update the configuration with geo detection disabled.
+ script.simulateUpdateConfiguration(
+ USER_ID, CONFIG_GEO_DETECTION_DISABLED, false /* expectedResult */);
+
+ // The settings should not have been changed: user shouldn't have the capability to modify
+ // the setting when the feature is disabled.
+ script.verifyConfigurationNotChanged();
+
+ // Try to update the configuration with geo detection enabled.
+ script.simulateUpdateConfiguration(
+ USER_ID, CONFIG_GEO_DETECTION_ENABLED, false /* expectedResult */);
+
+ // The settings should not have been changed: user shouldn't have the capability to modify
+ // the setting when the feature is disabled.
+ script.verifyConfigurationNotChanged();
+ }
+
+ @Test
public void testEmptyTelephonySuggestions() {
TelephonyTimeZoneSuggestion slotIndex1TimeZoneSuggestion =
createEmptySlotIndex1Suggestion();
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 7b07102..1055069 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -81,9 +81,7 @@
// A correspondence to compare a FrontendResource and a TunerFrontendInfo.
private static final Correspondence<FrontendResource, TunerFrontendInfo> FR_TFI_COMPARE =
- new Correspondence<FrontendResource, TunerFrontendInfo>() {
- @Override
- public boolean compare(FrontendResource actual, TunerFrontendInfo expected) {
+ Correspondence.from((FrontendResource actual, TunerFrontendInfo expected) -> {
if (actual == null || expected == null) {
return (actual == null) && (expected == null);
}
@@ -91,13 +89,7 @@
return actual.getId() == expected.getId()
&& actual.getType() == expected.getFrontendType()
&& actual.getExclusiveGroupId() == expected.getExclusiveGroupId();
- }
-
- @Override
- public String toString() {
- return "is correctly configured from ";
- }
- };
+ }, "is correctly configured from ");
@Before
public void setUp() throws Exception {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index d7e431f..e304083 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -690,6 +690,39 @@
}
@Test
+ public void unbindOtherUserServices() throws PackageManager.NameNotFoundException {
+ Context context = mock(Context.class);
+ PackageManager pm = mock(PackageManager.class);
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+ when(context.getPackageName()).thenReturn(mContext.getPackageName());
+ when(context.getUserId()).thenReturn(mContext.getUserId());
+ when(context.getPackageManager()).thenReturn(pm);
+ when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+ ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+ when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ ServiceConnection sc = (ServiceConnection) args[1];
+ sc.onServiceConnected(cn, mock(IBinder.class));
+ return true;
+ });
+
+ service.registerService(cn, 0);
+ service.registerService(cn, 10);
+ service.registerService(cn, 11);
+ service.unbindOtherUserServices(11);
+
+ assertFalse(service.isBound(cn, 0));
+ assertFalse(service.isBound(cn, 10));
+ assertTrue(service.isBound(cn, 11));
+ }
+
+ @Test
public void testPackageUninstall_packageNoLongerInApprovedList() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
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 ed6a20b..1100496 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -43,6 +43,9 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_MUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -110,6 +113,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.IIntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
@@ -248,6 +252,11 @@
Resources mResources;
@Mock
RankingHandler mRankingHandler;
+ @Mock
+ ActivityManagerInternal mAmi;
+
+ @Mock
+ IIntentSender pi1;
private static final int MAX_POST_DELAY = 1000;
@@ -392,7 +401,6 @@
DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class);
when(deviceIdleInternal.getNotificationAllowlistDuration()).thenReturn(3000L);
- ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
@@ -403,7 +411,7 @@
LocalServices.removeServiceForTest(DeviceIdleInternal.class);
LocalServices.addService(DeviceIdleInternal.class, deviceIdleInternal);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
- LocalServices.addService(ActivityManagerInternal.class, activityManagerInternal);
+ LocalServices.addService(ActivityManagerInternal.class, mAmi);
doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
@@ -477,7 +485,7 @@
mGroupHelper, mAm, mAtm, mAppUsageStats,
mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
mAppOpsManager, mUm, mHistoryManager, mStatsManager,
- mock(TelephonyManager.class));
+ mock(TelephonyManager.class), mAmi);
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
mService.setAudioManager(mAudioManager);
@@ -674,7 +682,8 @@
}
Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
.setContentTitle("foo")
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .addAction(new Notification.Action.Builder(null, "test", null).build());
if (extender != null) {
nb.extend(extender);
}
@@ -810,6 +819,7 @@
PendingIntent pendingIntent = mock(PendingIntent.class);
Intent intent = mock(Intent.class);
when(pendingIntent.getIntent()).thenReturn(intent);
+ when(pendingIntent.getTarget()).thenReturn(pi1);
ActivityInfo info = new ActivityInfo();
info.resizeMode = RESIZE_MODE_RESIZEABLE;
@@ -1873,7 +1883,7 @@
public void testGroupInstanceIds() throws Exception {
final NotificationRecord group1 = generateNotificationRecord(
mTestNotificationChannel, 1, "group1", true);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "testGroupInstanceIds",
group1.getSbn().getId(), group1.getSbn().getNotification(),
group1.getSbn().getUserId());
waitForIdle();
@@ -1881,7 +1891,7 @@
// same group, child, should be returned
final NotificationRecord group1Child = generateNotificationRecord(
mTestNotificationChannel, 2, "group1", false);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "testGroupInstanceIds",
group1Child.getSbn().getId(),
group1Child.getSbn().getNotification(), group1Child.getSbn().getUserId());
waitForIdle();
@@ -7134,4 +7144,159 @@
inOrder.verify(parent).recordDismissalSentiment(anyInt());
inOrder.verify(child).recordDismissalSentiment(anyInt());
}
+
+ @Test
+ public void testImmutableBubbleIntent() throws Exception {
+ when(mAmi.getPendingIntentFlags(pi1))
+ .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+ NotificationRecord r = generateMessageBubbleNotifRecord(true,
+ mTestNotificationChannel, 7, "testImmutableBubbleIntent", null, false);
+ try {
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+ r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+ waitForIdle();
+ fail("Allowed a bubble with an immutable intent to be posted");
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+ }
+
+ @Test
+ public void testMutableBubbleIntent() throws Exception {
+ when(mAmi.getPendingIntentFlags(pi1))
+ .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+ NotificationRecord r = generateMessageBubbleNotifRecord(true,
+ mTestNotificationChannel, 7, "testMutableBubbleIntent", null, false);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+ r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(r.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testImmutableDirectReplyActionIntent() throws Exception {
+ when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+ .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+ NotificationRecord r = generateMessageBubbleNotifRecord(false,
+ mTestNotificationChannel, 7, "testImmutableDirectReplyActionIntent", null, false);
+ try {
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+ r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+ waitForIdle();
+ fail("Allowed a direct reply with an immutable intent to be posted");
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+ }
+
+ @Test
+ public void testMutableDirectReplyActionIntent() throws Exception {
+ when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+ .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+ NotificationRecord r = generateMessageBubbleNotifRecord(false,
+ mTestNotificationChannel, 7, "testMutableDirectReplyActionIntent", null, false);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+ r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(r.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testImmutableDirectReplyContextualActionIntent() throws Exception {
+ when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+ .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ ArrayList<Notification.Action> extraAction = new ArrayList<>();
+ RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+ PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+ Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+ inputIntent).addRemoteInput(remoteInput)
+ .build();
+ extraAction.add(replyAction);
+ Bundle signals = new Bundle();
+ signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
+ Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+ r.getUser());
+ r.addAdjustment(adjustment);
+ r.applyAdjustments();
+
+ try {
+ mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
+ r.getSbn().getTag(), r,false);
+ fail("Allowed a contextual direct reply with an immutable intent to be posted");
+ } catch (IllegalArgumentException e) {
+ // good
+ }
+ }
+
+ @Test
+ public void testMutableDirectReplyContextualActionIntent() throws Exception {
+ when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+ .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ ArrayList<Notification.Action> extraAction = new ArrayList<>();
+ RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+ PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+ Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+ Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+ inputIntent).addRemoteInput(remoteInput)
+ .build();
+ extraAction.add(replyAction);
+ Bundle signals = new Bundle();
+ signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
+ Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+ r.getUser());
+ r.addAdjustment(adjustment);
+ r.applyAdjustments();
+
+ mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
+ r.getSbn().getTag(), r,false);
+ }
+
+ @Test
+ public void testImmutableActionIntent() throws Exception {
+ when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+ .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+ r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+ waitForIdle();
+ StatusBarNotification[] notifs =
+ mBinderService.getActiveNotifications(r.getSbn().getPackageName());
+ assertEquals(1, notifs.length);
+ }
+
+ @Test
+ public void testImmutableContextualActionIntent() throws Exception {
+ when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+ .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ ArrayList<Notification.Action> extraAction = new ArrayList<>();
+ extraAction.add(new Notification.Action(0, "hello", null));
+ Bundle signals = new Bundle();
+ signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
+ Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+ r.getUser());
+ r.addAdjustment(adjustment);
+ r.applyAdjustments();
+
+ mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
+ r.getSbn().getTag(), r,false);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 3281c3f..a80f62a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -32,6 +32,7 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IUriGrantsManager;
@@ -154,7 +155,8 @@
mock(DevicePolicyManagerInternal.class), mock(IUriGrantsManager.class),
mock(UriGrantsManagerInternal.class),
mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class),
- mock(StatsManager.class), mock(TelephonyManager.class));
+ mock(StatsManager.class), mock(TelephonyManager.class),
+ mock(ActivityManagerInternal.class));
} catch (SecurityException e) {
if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
throw e;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
index eca71b6..e5ae2d3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
@@ -305,6 +305,7 @@
//when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
// anyString(), anyInt(), any())).thenReturn(true);
- assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isSameAs(si);
+ assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM))
+ .isSameInstanceAs(si);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index e17601e..01c1f1f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -23,6 +23,8 @@
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.DisplayArea.Type.ANY;
@@ -37,14 +39,18 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
+import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
+import android.view.View;
+import android.view.WindowManager;
import com.google.android.collect.Lists;
@@ -405,6 +411,55 @@
childBounds1, windowToken.getMaxBounds());
}
+ @Test
+ public void testGetOrientation() {
+ final DisplayArea.Tokens area = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "test");
+ final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY);
+ spyOn(token);
+ doReturn(mock(DisplayContent.class)).when(token).getDisplayContent();
+ doNothing().when(token).setParent(any());
+ final WindowState win = createWindowState(token);
+ spyOn(win);
+ doNothing().when(win).setParent(any());
+ win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+ token.addChild(win, 0);
+ area.addChild(token);
+
+ doReturn(true).when(win).isVisible();
+
+ assertEquals("Visible window can request orientation",
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
+ area.getOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR));
+
+ doReturn(false).when(win).isVisible();
+
+ assertEquals("Invisible window cannot request orientation",
+ ActivityInfo.SCREEN_ORIENTATION_NOSENSOR,
+ area.getOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR));
+ }
+
+ @Test
+ public void testSetIgnoreOrientationRequest() {
+ final DisplayArea.Tokens area = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "test");
+ final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY);
+ spyOn(token);
+ doReturn(mock(DisplayContent.class)).when(token).getDisplayContent();
+ doNothing().when(token).setParent(any());
+ final WindowState win = createWindowState(token);
+ spyOn(win);
+ doNothing().when(win).setParent(any());
+ win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+ token.addChild(win, 0);
+ area.addChild(token);
+ doReturn(true).when(win).isVisible();
+
+ assertEquals(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, area.getOrientation());
+
+ area.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSET, area.getOrientation());
+ }
+
private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
private TestDisplayArea(WindowManagerService wms, Rect bounds) {
super(wms, ANY, "half display area");
@@ -417,6 +472,13 @@
}
}
+ private WindowState createWindowState(WindowToken token) {
+ return new WindowState(mWms, mock(Session.class), new TestIWindow(), token,
+ null /* parentWindow */, 0 /* appOp */, new WindowManager.LayoutParams(),
+ View.VISIBLE, 0 /* ownerId */, 0 /* showUserId */,
+ false /* ownerCanAddInternalSystemWindow */);
+ }
+
private WindowToken createWindowToken(int type) {
return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(),
type, false /* persist */, null /* displayContent */,
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 d0a5644..ecbfac8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -163,12 +163,6 @@
}
@Override
- public SurfaceControl.Transaction setOverrideScalingMode(SurfaceControl sc,
- int overrideScalingMode) {
- return this;
- }
-
- @Override
public SurfaceControl.Transaction setColor(SurfaceControl sc, float[] color) {
return this;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index d2b7ac4..7975899 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -1002,14 +1002,18 @@
public void testNotSpecifyOrientationByFloatingTask() {
final Task task = getTestTask();
final ActivityRecord activity = task.getTopMostActivity();
+ final WindowContainer<?> parentContainer = task.getParent();
final TaskDisplayArea taskDisplayArea = task.getDisplayArea();
activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parentContainer.getOrientation());
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation());
task.setWindowingMode(WINDOWING_MODE_PINNED);
- assertEquals(SCREEN_ORIENTATION_UNSET, taskDisplayArea.getOrientation());
+ // TDA returns the last orientation when child returns UNSET
+ assertEquals(SCREEN_ORIENTATION_UNSET, parentContainer.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index dc85904..db5c796 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -197,19 +197,19 @@
}
@Override
- public void screenTurningOn(ScreenOnListener screenOnListener) {
+ public void screenTurningOn(int displayId, ScreenOnListener screenOnListener) {
}
@Override
- public void screenTurnedOn() {
+ public void screenTurnedOn(int displayId) {
}
@Override
- public void screenTurningOff(ScreenOffListener screenOffListener) {
+ public void screenTurningOff(int displayId, ScreenOffListener screenOffListener) {
}
@Override
- public void screenTurnedOff() {
+ public void screenTurnedOff(int displayId) {
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 0152fc6..aac8397 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -54,12 +54,14 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.PictureInPictureParams;
import android.content.pm.ActivityInfo;
+import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
@@ -72,6 +74,7 @@
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.IWindowContainerTransactionCallback;
+import android.window.TaskAppearedInfo;
import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
@@ -79,8 +82,10 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
/**
@@ -93,14 +98,27 @@
@Presubmit
@RunWith(WindowTestRunner.class)
public class WindowOrganizerTests extends WindowTestsBase {
- private ITaskOrganizer registerMockOrganizer() {
+
+ private ITaskOrganizer createMockOrganizer() {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
when(organizer.asBinder()).thenReturn(new Binder());
-
- mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer);
return organizer;
}
+ private ITaskOrganizer registerMockOrganizer(ArrayList<TaskAppearedInfo> existingTasks) {
+ final ITaskOrganizer organizer = createMockOrganizer();
+ ParceledListSlice<TaskAppearedInfo> tasks =
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer);
+ if (existingTasks != null) {
+ existingTasks.addAll(tasks.getList());
+ }
+ return organizer;
+ }
+
+ private ITaskOrganizer registerMockOrganizer() {
+ return registerMockOrganizer(null);
+ }
+
Task createTask(Task stack, boolean fakeDraw) {
final Task task = createTaskInStack(stack, 0);
@@ -128,27 +146,21 @@
@Test
public void testAppearVanish() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack);
- final ITaskOrganizer organizer = registerMockOrganizer();
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- stack.setTaskOrganizer(organizer);
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-
stack.removeImmediately();
verify(organizer).onTaskVanished(any());
}
@Test
public void testAppearWaitsForVisibility() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack, false);
- final ITaskOrganizer organizer = registerMockOrganizer();
-
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- stack.setTaskOrganizer(organizer);
verify(organizer, never())
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
@@ -163,9 +175,9 @@
@Test
public void testNoVanishedIfNoAppear() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack, false /* hasBeenVisible */);
- final ITaskOrganizer organizer = registerMockOrganizer();
// In this test we skip making the Task visible, and verify
// that even though a TaskOrganizer is set remove doesn't emit
@@ -179,28 +191,25 @@
@Test
public void testTaskNoDraw() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack, false /* fakeDraw */);
- final ITaskOrganizer organizer = registerMockOrganizer();
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
verify(organizer, never())
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
assertTrue(stack.isOrganized());
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
- verify(organizer, never()).onTaskVanished(any());
+ assertTaskVanished(organizer, false /* expectVanished */, stack);
assertFalse(stack.isOrganized());
}
@Test
public void testClearOrganizer() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack);
- final ITaskOrganizer organizer = registerMockOrganizer();
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- stack.setTaskOrganizer(organizer);
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
assertTrue(stack.isOrganized());
@@ -211,16 +220,15 @@
@Test
public void testUnregisterOrganizer() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack);
- final ITaskOrganizer organizer = registerMockOrganizer();
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
assertTrue(stack.isOrganized());
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
- verify(organizer).onTaskVanished(any());
+ assertTaskVanished(organizer, true /* expectVanished */, stack);
assertFalse(stack.isOrganized());
}
@@ -232,37 +240,47 @@
final Task task2 = createTask(stack2);
final Task stack3 = createStack();
final Task task3 = createTask(stack3);
- final ITaskOrganizer organizer = registerMockOrganizer();
+ final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
+ final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
- // verify that tasks are appeared on registration
- verify(organizer, times(3))
- .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
+ // verify that tasks are returned and taskAppeared is not called
+ assertContainsTasks(existingTasks, stack, stack2, stack3);
+ verify(organizer, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
+ any(SurfaceControl.class));
+ verify(organizer, times(0)).onTaskVanished(any());
assertTrue(stack.isOrganized());
- // Now we replace the registration and1 verify the new organizer receives tasks
- final ITaskOrganizer organizer2 = registerMockOrganizer();
- verify(organizer2, times(3))
- .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
+ // Now we replace the registration and verify the new organizer receives existing tasks
+ final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>();
+ final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2);
+ assertContainsTasks(existingTasks2, stack, stack2, stack3);
+ verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class),
+ any(SurfaceControl.class));
verify(organizer2, times(0)).onTaskVanished(any());
- // One for task
- verify(organizer, times(3)).onTaskVanished(any());
+ // Removed tasks from the original organizer
+ assertTaskVanished(organizer, true /* expectVanished */, stack, stack2, stack3);
assertTrue(stack2.isOrganized());
// Now we unregister the second one, the first one should automatically be reregistered
// so we verify that it's now seeing changes.
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2);
- verify(organizer, times(6))
+ verify(organizer, times(3))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- verify(organizer2, times(3)).onTaskVanished(any());
+ assertTaskVanished(organizer2, true /* expectVanished */, stack, stack2, stack3);
}
@Test
public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
final Task stack = createStack();
final Task task = createTask(stack);
+ final Task stack2 = createStack();
+ final Task task2 = createTask(stack2);
+ ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
+ final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
+ assertContainsTasks(existingTasks, stack, stack2);
- final ITaskOrganizer organizer = registerMockOrganizer();
- verify(organizer, times(1))
+ // Verify we don't get onTaskAppeared if we are returned the tasks
+ verify(organizer, never())
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
}
@@ -366,7 +384,7 @@
}
@Test
- public void testSetIgnoreOrientationRequest() {
+ public void testSetIgnoreOrientationRequest_taskDisplayArea() {
removeGlobalMinSizeRestriction();
final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
final Task stack = taskDisplayArea.createStack(
@@ -378,7 +396,7 @@
activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
// TDA returns UNSET when ignoreOrientationRequest == true
- // DC is UNSPECIFIED because it is using the previous (default) when TDA returns UNSET.
+ // DC is UNSPECIFIED when child returns UNSET
assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
@@ -399,8 +417,40 @@
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
// TDA returns UNSET when ignoreOrientationRequest == true
- // DC is LANDSCAPE because it is using the previous when TDA returns UNSET.
+ // DC is UNSPECIFIED when child returns UNSET
assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
+ assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
+ }
+
+ @Test
+ public void testSetIgnoreOrientationRequest_displayContent() {
+ removeGlobalMinSizeRestriction();
+ final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+ final Task stack = taskDisplayArea.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setStack(stack).build();
+ mDisplayContent.setFocusedApp(activity);
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+ // DC uses the orientation request from app
+ assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
+
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ t.setIgnoreOrientationRequest(
+ mDisplayContent.mRemoteToken.toWindowContainerToken(),
+ true /* ignoreOrientationRequest */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+
+ // DC returns UNSPECIFIED when ignoreOrientationRequest == true
+ assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
+
+ t.setIgnoreOrientationRequest(
+ mDisplayContent.mRemoteToken.toWindowContainerToken(),
+ false /* ignoreOrientationRequest */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+
+ // DC uses the orientation request from app after mIgnoreOrientationRequest is set to false
assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
}
@@ -922,9 +972,9 @@
@Test
public void testPreventDuplicateAppear() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack, false /* fakeDraw */);
- final ITaskOrganizer organizer = registerMockOrganizer();
stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
stack.setTaskOrganizer(organizer);
@@ -945,17 +995,14 @@
@Test
public void testInterceptBackPressedOnTaskRoot() throws RemoteException {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stack = createStack();
final Task task = createTask(stack);
final ActivityRecord activity = createActivityRecordInTask(stack.mDisplayContent, task);
final Task stack2 = createStack();
final Task task2 = createTask(stack2);
final ActivityRecord activity2 = createActivityRecordInTask(stack.mDisplayContent, task2);
- final ITaskOrganizer organizer = registerMockOrganizer();
- // Setup the task to be controlled by the MW mode organizer
- stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
assertTrue(stack.isOrganized());
assertTrue(stack2.isOrganized());
@@ -982,9 +1029,9 @@
@Test
public void testBLASTCallbackWithMultipleWindows() throws Exception {
+ final ITaskOrganizer organizer = registerMockOrganizer();
final Task stackController = createStack();
final Task task = createTask(stackController);
- final ITaskOrganizer organizer = registerMockOrganizer();
final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1");
final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2");
makeWindowVisible(w1);
@@ -1035,4 +1082,37 @@
assertFalse(daTask.isForceHidden());
});
}
+
+ /**
+ * Verifies that task vanished is called for a specific task.
+ */
+ private void assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks)
+ throws RemoteException {
+ ArgumentCaptor<RunningTaskInfo> arg = ArgumentCaptor.forClass(RunningTaskInfo.class);
+ verify(organizer, atLeastOnce()).onTaskVanished(arg.capture());
+ List<RunningTaskInfo> taskInfos = arg.getAllValues();
+
+ HashSet<Integer> vanishedTaskIds = new HashSet<>();
+ for (int i = 0; i < taskInfos.size(); i++) {
+ vanishedTaskIds.add(taskInfos.get(i).taskId);
+ }
+ HashSet<Integer> taskIds = new HashSet<>();
+ for (int i = 0; i < tasks.length; i++) {
+ taskIds.add(tasks[i].mTaskId);
+ }
+
+ assertTrue(expectVanished
+ ? vanishedTaskIds.containsAll(taskIds)
+ : !vanishedTaskIds.removeAll(taskIds));
+ }
+
+ private void assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks) {
+ HashSet<Integer> taskIds = new HashSet<>();
+ for (int i = 0; i < taskInfos.size(); i++) {
+ taskIds.add(taskInfos.get(i).getTaskInfo().taskId);
+ }
+ for (int i = 0; i < expectedTasks.length; i++) {
+ assertTrue(taskIds.contains(expectedTasks[i].mTaskId));
+ }
+ }
}
diff --git a/services/usb/java/com/android/server/usb/MtpNotificationManager.java b/services/usb/java/com/android/server/usb/MtpNotificationManager.java
index 462ee19..39f2f29 100644
--- a/services/usb/java/com/android/server/usb/MtpNotificationManager.java
+++ b/services/usb/java/com/android/server/usb/MtpNotificationManager.java
@@ -64,12 +64,13 @@
private final Context mContext;
private final OnOpenInAppListener mListener;
+ private final Receiver mReceiver;
MtpNotificationManager(Context context, OnOpenInAppListener listener) {
mContext = context;
mListener = listener;
- final Receiver receiver = new Receiver();
- context.registerReceiver(receiver, new IntentFilter(ACTION_OPEN_IN_APPS));
+ mReceiver = new Receiver();
+ context.registerReceiver(mReceiver, new IntentFilter(ACTION_OPEN_IN_APPS));
}
void showNotification(UsbDevice device) {
@@ -154,4 +155,8 @@
static interface OnOpenInAppListener {
void onOpenInApp(UsbDevice device);
}
+
+ public void unregister() {
+ mContext.unregisterReceiver(mReceiver);
+ }
}
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index d7b6b5d..26ee03c 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -261,6 +261,15 @@
}
/**
+ * Unregister all broadcast receivers. Must be called explicitly before
+ * object deletion.
+ */
+ public void unregisterReceivers() {
+ mPackageMonitor.unregister();
+ mMtpNotificationManager.unregister();
+ }
+
+ /**
* Remove all defaults and denied packages for a user.
*
* @param userToRemove The user
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 7b677ee..8e53ff4 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -124,6 +124,7 @@
if (mSettingsByProfileGroup.indexOfKey(userToRemove.getIdentifier()) >= 0) {
// The user to remove is the parent user of the group. The parent is the last user
// that gets removed. All state will be removed with the user
+ mSettingsByProfileGroup.get(userToRemove.getIdentifier()).unregisterReceivers();
mSettingsByProfileGroup.remove(userToRemove.getIdentifier());
} else {
// We cannot find the parent user of the user that is removed, hence try to remove
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 917f65a..547d253 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1048,9 +1048,9 @@
if (unloadStatus != SoundTriggerInternal.STATUS_OK) {
Slog.w(TAG, "Unable to unload keyphrase sound model:" + unloadStatus);
}
- deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUserId,
- bcp47Locale);
}
+ deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUserId,
+ bcp47Locale);
return deleted ? SoundTriggerInternal.STATUS_OK : SoundTriggerInternal.STATUS_ERROR;
} finally {
if (deleted) {
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
index 8fa0cde..150577a 100644
--- a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
@@ -124,7 +124,7 @@
data class InputData<T : Parcelable>(val valid: T, val validCopy: T, val validOther: T) {
val kls = valid.javaClass
init {
- assertThat(valid).isNotSameAs(validCopy)
+ assertThat(valid).isNotSameInstanceAs(validCopy)
// Don't use isInstanceOf because of phantom warnings in intellij about Class!
assertThat(validCopy.javaClass).isEqualTo(valid.javaClass)
assertThat(validOther.javaClass).isEqualTo(valid.javaClass)
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index a6d7245..9f16543 100755
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -966,6 +966,32 @@
/**
* Gets the verification status for the phone number of an incoming call as identified in
* ATIS-1000082.
+ * <p>
+ * For incoming calls, the number verification status indicates whether the device was
+ * able to verify the authenticity of the calling number using the STIR process outlined
+ * in ATIS-1000082. {@link Connection#VERIFICATION_STATUS_NOT_VERIFIED} indicates that
+ * the network was not able to use STIR to verify the caller's number (i.e. nothing is
+ * known regarding the authenticity of the number.
+ * {@link Connection#VERIFICATION_STATUS_PASSED} indicates that the network was able to
+ * use STIR to verify the caller's number. This indicates that the network has a high
+ * degree of confidence that the incoming call actually originated from the indicated
+ * number. {@link Connection#VERIFICATION_STATUS_FAILED} indicates that the network's
+ * STIR verification did not pass. This indicates that the incoming call may not
+ * actually be from the indicated number. This could occur if, for example, the caller
+ * is using an impersonated phone number.
+ * <p>
+ * A {@link CallScreeningService} can use this information to help determine if an
+ * incoming call is potentially an unwanted call. A verification status of
+ * {@link Connection#VERIFICATION_STATUS_FAILED} indicates that an incoming call may not
+ * actually be from the number indicated on the call (i.e. impersonated number) and that it
+ * should potentially be blocked. Likewise,
+ * {@link Connection#VERIFICATION_STATUS_PASSED} can be used as a positive signal to
+ * help clarify that the incoming call is originating from the indicated number and it
+ * is less likely to be an undesirable call.
+ * <p>
+ * An {@link InCallService} can use this information to provide a visual indicator to the
+ * user regarding the verification status of a call and to help identify calls from
+ * potentially impersonated numbers.
* @return the verification status.
*/
public @Connection.VerificationStatus int getCallerNumberVerificationStatus() {
diff --git a/telecomm/java/android/telecom/CallerInfo.java b/telecomm/java/android/telecom/CallerInfo.java
index fb6f994..aff2f01 100644
--- a/telecomm/java/android/telecom/CallerInfo.java
+++ b/telecomm/java/android/telecom/CallerInfo.java
@@ -405,7 +405,8 @@
// Change the callerInfo number ONLY if it is an emergency number
// or if it is the voicemail number. If it is either, take a
// shortcut and skip the query.
- if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) {
+ TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+ if (tm.isEmergencyNumber(number)) {
return new CallerInfo().markAsEmergency(context);
} else if (PhoneNumberUtils.isVoiceMailNumber(null, subId, number)) {
return new CallerInfo().markAsVoiceMail(context, subId);
diff --git a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
index 4a81a8e..a9e1a8f 100644
--- a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
+++ b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
@@ -34,6 +34,7 @@
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import java.util.ArrayList;
@@ -481,7 +482,8 @@
cw.subId = subId;
// check to see if these are recognized numbers, and use shortcuts if we can.
- if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) {
+ TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+ if (tm.isEmergencyNumber(number)) {
cw.event = EVENT_EMERGENCY_NUMBER;
} else if (PhoneNumberUtils.isVoiceMailNumber(context, subId, number)) {
cw.event = EVENT_VOICEMAIL_NUMBER;
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index af5badd..a67273c 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -227,6 +227,25 @@
field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
}
+ public final class ModemActivityInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo);
+ method public long getIdleTimeMillis();
+ method public static int getNumTxPowerLevels();
+ method public long getReceiveTimeMillis();
+ method public long getSleepTimeMillis();
+ method public long getTimestampMillis();
+ method public long getTransmitDurationMillisAtPowerLevel(int);
+ method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
+ field public static final int TX_POWER_LEVEL_0 = 0; // 0x0
+ field public static final int TX_POWER_LEVEL_1 = 1; // 0x1
+ field public static final int TX_POWER_LEVEL_2 = 2; // 0x2
+ field public static final int TX_POWER_LEVEL_3 = 3; // 0x3
+ field public static final int TX_POWER_LEVEL_4 = 4; // 0x4
+ }
+
public final class NetworkRegistrationInfo implements android.os.Parcelable {
method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
method public int getRegistrationState();
@@ -693,7 +712,6 @@
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<java.lang.String> getEquivalentHomePlmns();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
@@ -727,6 +745,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
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);
@@ -757,6 +776,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -807,6 +827,8 @@
field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
field public static final int KEY_TYPE_EPDG = 1; // 0x1
field public static final int KEY_TYPE_WLAN = 2; // 0x2
+ field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1
+ field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2
field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java
index 2e7bde3..e089657 100644
--- a/telephony/java/android/telephony/CellSignalStrength.java
+++ b/telephony/java/android/telephony/CellSignalStrength.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.annotation.IntRange;
+import android.annotation.SystemApi;
import android.os.PersistableBundle;
/**
@@ -155,11 +156,12 @@
/**
* Returns the number of signal strength levels.
- * @return Number of signal strength levels, enforced to be 5
+ * @return Number of signal strength levels, currently defined in the HAL as 5.
*
* @hide
*/
- public static final int getNumSignalStrengthLevels() {
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static int getNumSignalStrengthLevels() {
return NUM_SIGNAL_STRENGTH_BINS;
}
}
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index debb119..881d85c 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -16,8 +16,12 @@
package android.telephony;
+import android.annotation.DurationMillisLong;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -25,46 +29,50 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Arrays;
+import java.util.Objects;
/**
- * Reports modem activity information.
+ * Contains information about the modem's activity. May be useful for power stats reporting.
* @hide
*/
+@SystemApi
+@TestApi
public final class ModemActivityInfo implements Parcelable {
+ private static final int TX_POWER_LEVELS = 5;
+
/**
- * Tx(transmit) power level. see power index below
- * <ul>
- * <li> index 0 = tx_power < 0dBm. </li>
- * <li> index 1 = 0dBm < tx_power < 5dBm. </li>
- * <li> index 2 = 5dBm < tx_power < 15dBm. </li>
- * <li> index 3 = 15dBm < tx_power < 20dBm. </li>
- * <li> index 4 = tx_power > 20dBm. </li>
- * </ul>
- */
- public static final int TX_POWER_LEVELS = 5;
- /**
- * Tx(transmit) power level 0: tx_power < 0dBm
+ * Corresponds to transmit power of less than 0dBm.
*/
public static final int TX_POWER_LEVEL_0 = 0;
+
/**
- * Tx(transmit) power level 1: 0dBm < tx_power < 5dBm
+ * Corresponds to transmit power between 0dBm and 5dBm.
*/
public static final int TX_POWER_LEVEL_1 = 1;
+
/**
- * Tx(transmit) power level 2: 5dBm < tx_power < 15dBm
+ * Corresponds to transmit power between 5dBm and 15dBm.
*/
public static final int TX_POWER_LEVEL_2 = 2;
+
/**
- * Tx(transmit) power level 3: 15dBm < tx_power < 20dBm.
+ * Corresponds to transmit power between 15dBm and 20dBm.
*/
public static final int TX_POWER_LEVEL_3 = 3;
+
/**
- * Tx(transmit) power level 4: tx_power > 20dBm
+ * Corresponds to transmit power above 20dBm.
*/
public static final int TX_POWER_LEVEL_4 = 4;
+ /**
+ * The number of transmit power levels. Fixed by HAL definition.
+ */
+ public static int getNumTxPowerLevels() {
+ return TX_POWER_LEVELS;
+ }
+
/** @hide */
@IntDef(prefix = {"TX_POWER_LEVEL_"}, value = {
TX_POWER_LEVEL_0,
@@ -82,34 +90,39 @@
new Range<>(5, 15),
new Range<>(15, 20),
new Range<>(20, Integer.MAX_VALUE)
-
};
private long mTimestamp;
private int mSleepTimeMs;
private int mIdleTimeMs;
- private List<TransmitPower> mTransmitPowerInfo = new ArrayList<>(TX_POWER_LEVELS);
+ private int[] mTxTimeMs;
private int mRxTimeMs;
+ /**
+ * @hide
+ */
+ @TestApi
public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
@NonNull int[] txTimeMs, int rxTimeMs) {
+ Objects.requireNonNull(txTimeMs);
+ if (txTimeMs.length != TX_POWER_LEVELS) {
+ throw new IllegalArgumentException("txTimeMs must have length == TX_POWER_LEVELS");
+ }
mTimestamp = timestamp;
mSleepTimeMs = sleepTimeMs;
mIdleTimeMs = idleTimeMs;
- populateTransmitPowerRange(txTimeMs);
+ mTxTimeMs = txTimeMs;
mRxTimeMs = rxTimeMs;
}
- /** helper API to populate tx power range for each bucket **/
- private void populateTransmitPowerRange(@NonNull int[] transmitPowerMs) {
- int i = 0;
- for ( ; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) {
- mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i]));
- }
- // Make sure that mTransmitPowerInfo is fully initialized.
- for ( ; i < TX_POWER_LEVELS; i++) {
- mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], 0));
- }
+ /**
+ * Provided for convenience in manipulation since the API exposes long values but internal
+ * representations are ints.
+ * @hide
+ */
+ public ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs,
+ @NonNull int[] txTimeMs, long rxTimeMs) {
+ this(timestamp, (int) sleepTimeMs, (int) idleTimeMs, txTimeMs, (int) rxTimeMs);
}
@Override
@@ -118,7 +131,7 @@
+ " mTimestamp=" + mTimestamp
+ " mSleepTimeMs=" + mSleepTimeMs
+ " mIdleTimeMs=" + mIdleTimeMs
- + " mTransmitPowerInfo[]=" + mTransmitPowerInfo.toString()
+ + " mTxTimeMs[]=" + mTxTimeMs
+ " mRxTimeMs=" + mRxTimeMs
+ "}";
}
@@ -129,14 +142,12 @@
public static final @android.annotation.NonNull Parcelable.Creator<ModemActivityInfo> CREATOR =
new Parcelable.Creator<ModemActivityInfo>() {
- public ModemActivityInfo createFromParcel(Parcel in) {
+ public ModemActivityInfo createFromParcel(@NonNull Parcel in) {
long timestamp = in.readLong();
int sleepTimeMs = in.readInt();
int idleTimeMs = in.readInt();
int[] txTimeMs = new int[TX_POWER_LEVELS];
- for (int i = 0; i < TX_POWER_LEVELS; i++) {
- txTimeMs[i] = in.readInt();
- }
+ in.readIntArray(txTimeMs);
int rxTimeMs = in.readInt();
return new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs,
txTimeMs, rxTimeMs);
@@ -147,21 +158,25 @@
}
};
- public void writeToParcel(Parcel dest, int flags) {
+ /**
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ */
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeLong(mTimestamp);
dest.writeInt(mSleepTimeMs);
dest.writeInt(mIdleTimeMs);
- for (int i = 0; i < TX_POWER_LEVELS; i++) {
- dest.writeInt(mTransmitPowerInfo.get(i).getTimeInMillis());
- }
+ dest.writeIntArray(mTxTimeMs);
dest.writeInt(mRxTimeMs);
}
/**
- * @return milliseconds since boot, including mTimeInMillis spent in sleep.
- * @see SystemClock#elapsedRealtime()
+ * Gets the timestamp at which this modem activity info was recorded.
+ *
+ * @return The timestamp, as returned by {@link SystemClock#elapsedRealtime()}, when this
+ * {@link ModemActivityInfo} was recorded.
*/
- public long getTimestamp() {
+ public @ElapsedRealtimeLong long getTimestampMillis() {
return mTimestamp;
}
@@ -171,35 +186,48 @@
}
/**
- * @return an arrayList of {@link TransmitPower} with each element representing the total time where
- * transmitter is awake time (in ms) for a given power range (in dbm).
+ * Gets the amount of time the modem spent transmitting at a certain power level.
*
- * @see #TX_POWER_LEVELS
+ * @param powerLevel The power level to query.
+ * @return The amount of time, in milliseconds, that the modem spent transmitting at the
+ * given power level.
*/
- @NonNull
- public List<TransmitPower> getTransmitPowerInfo() {
- return mTransmitPowerInfo;
+ public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel(
+ @TxPowerLevel int powerLevel) {
+ return mTxTimeMs[powerLevel];
+ }
+
+ /**
+ * Gets the range of transmit powers corresponding to a certain power level.
+ *
+ * @param powerLevel The power level to query
+ * @return A {@link Range} object representing the range of intensities (in dBm) to which this
+ * power level corresponds.
+ */
+ public @NonNull Range<Integer> getTransmitPowerRange(@TxPowerLevel int powerLevel) {
+ return TX_POWER_RANGES[powerLevel];
}
/** @hide */
public void setTransmitTimeMillis(int[] txTimeMs) {
- populateTransmitPowerRange(txTimeMs);
- }
-
- /** @hide */
- @NonNull
- public int[] getTransmitTimeMillis() {
- int[] transmitTimeMillis = new int[TX_POWER_LEVELS];
- for (int i = 0; i < transmitTimeMillis.length; i++) {
- transmitTimeMillis[i] = mTransmitPowerInfo.get(i).getTimeInMillis();
- }
- return transmitTimeMillis;
+ mTxTimeMs = Arrays.copyOf(txTimeMs, TX_POWER_LEVELS);
}
/**
- * @return total mTimeInMillis (in ms) when modem is in a low power or sleep state.
+ * @return The raw array of transmit power durations
+ * @hide
*/
- public int getSleepTimeMillis() {
+ @NonNull
+ public int[] getTransmitTimeMillis() {
+ return mTxTimeMs;
+ }
+
+ /**
+ * Gets the amount of time (in milliseconds) when the modem is in a low power or sleep state.
+ *
+ * @return Time in milliseconds.
+ */
+ public @DurationMillisLong long getSleepTimeMillis() {
return mSleepTimeMs;
}
@@ -209,10 +237,44 @@
}
/**
- * @return total mTimeInMillis (in ms) when modem is awake but neither the transmitter nor receiver are
- * active.
+ * Provided for convenience, since the API surface needs to return longs but internal
+ * representations are ints.
+ * @hide
*/
- public int getIdleTimeMillis() {
+ public void setSleepTimeMillis(long sleepTimeMillis) {
+ mSleepTimeMs = (int) sleepTimeMillis;
+ }
+
+ /**
+ * Computes the difference between this instance of {@link ModemActivityInfo} and another
+ * instance.
+ *
+ * This method should be used to compute the amount of activity that has happened between two
+ * samples of modem activity taken at separate times. The sample passed in as an argument to
+ * this method should be the one that's taken later in time (and therefore has more activity).
+ * @param other The other instance of {@link ModemActivityInfo} to diff against.
+ * @return An instance of {@link ModemActivityInfo} representing the difference in modem
+ * activity.
+ */
+ public @NonNull ModemActivityInfo getDelta(@NonNull ModemActivityInfo other) {
+ int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+ for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+ txTimeMs[i] = other.mTxTimeMs[i] - mTxTimeMs[i];
+ }
+ return new ModemActivityInfo(other.getTimestampMillis(),
+ other.getSleepTimeMillis() - getSleepTimeMillis(),
+ other.getIdleTimeMillis() - getIdleTimeMillis(),
+ txTimeMs,
+ other.getReceiveTimeMillis() - getReceiveTimeMillis());
+ }
+
+ /**
+ * Gets the amount of time (in milliseconds) when the modem is awake but neither transmitting
+ * nor receiving.
+ *
+ * @return Time in milliseconds.
+ */
+ public @DurationMillisLong long getIdleTimeMillis() {
return mIdleTimeMs;
}
@@ -222,9 +284,20 @@
}
/**
- * @return rx(receive) mTimeInMillis in ms.
+ * Provided for convenience, since the API surface needs to return longs but internal
+ * representations are ints.
+ * @hide
*/
- public int getReceiveTimeMillis() {
+ public void setIdleTimeMillis(long idleTimeMillis) {
+ mIdleTimeMs = (int) idleTimeMillis;
+ }
+
+ /**
+ * Gets the amount of time (in milliseconds) when the modem is awake and receiving data.
+ *
+ * @return Time in milliseconds.
+ */
+ public @DurationMillisLong long getReceiveTimeMillis() {
return mRxTimeMs;
}
@@ -234,71 +307,56 @@
}
/**
+ * Provided for convenience, since the API surface needs to return longs but internal
+ * representations are ints.
+ * @hide
+ */
+ public void setReceiveTimeMillis(long receiveTimeMillis) {
+ mRxTimeMs = (int) receiveTimeMillis;
+ }
+
+ /**
* Indicates if the modem has reported valid {@link ModemActivityInfo}.
*
* @return {@code true} if this {@link ModemActivityInfo} record is valid,
* {@code false} otherwise.
+ * TODO: remove usages of this outside Telephony by always returning a valid (or null) result
+ * from telephony.
+ * @hide
*/
+ @TestApi
public boolean isValid() {
- for (TransmitPower powerInfo : getTransmitPowerInfo()) {
- if(powerInfo.getTimeInMillis() < 0) {
- return false;
- }
- }
+ boolean isTxPowerValid = Arrays.stream(mTxTimeMs).allMatch((i) -> i >= 0);
- return ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0)
+ return isTxPowerValid && ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0)
&& (getReceiveTimeMillis() >= 0) && !isEmpty());
}
private boolean isEmpty() {
- for (TransmitPower txVal : getTransmitPowerInfo()) {
- if(txVal.getTimeInMillis() != 0) {
- return false;
- }
- }
+ boolean isTxPowerEmpty = mTxTimeMs == null || mTxTimeMs.length == 0
+ || Arrays.stream(mTxTimeMs).allMatch((i) -> i == 0);
- return ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0)
+ return isTxPowerEmpty && ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0)
&& (getReceiveTimeMillis() == 0));
}
- /**
- * Transmit power Information, including the power range in dbm and the total time (in ms) where
- * the transmitter is active/awake for this power range.
- * e.g, range: 0dbm(lower) ~ 5dbm(upper)
- * time: 5ms
- */
- public class TransmitPower {
- private int mTimeInMillis;
- private Range<Integer> mPowerRangeInDbm;
- /** @hide */
- public TransmitPower(@NonNull Range<Integer> range, int time) {
- this.mTimeInMillis = time;
- this.mPowerRangeInDbm = range;
- }
- /**
- * @return the total time in ms where the transmitter is active/wake for this power range
- * {@link #getPowerRangeInDbm()}.
- */
- public int getTimeInMillis() {
- return mTimeInMillis;
- }
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ModemActivityInfo that = (ModemActivityInfo) o;
+ return mTimestamp == that.mTimestamp
+ && mSleepTimeMs == that.mSleepTimeMs
+ && mIdleTimeMs == that.mIdleTimeMs
+ && mRxTimeMs == that.mRxTimeMs
+ && Arrays.equals(mTxTimeMs, that.mTxTimeMs);
+ }
- /**
- * @return the power range in dbm. e.g, range: 0dbm(lower) ~ 5dbm(upper)
- */
- @NonNull
- public Range<Integer> getPowerRangeInDbm() {
- return mPowerRangeInDbm;
- }
-
- @Override
- public String toString() {
- return "TransmitPower{"
- + " mTimeInMillis=" + mTimeInMillis
- + " mPowerRangeInDbm={" + mPowerRangeInDbm.getLower()
- + "," + mPowerRangeInDbm.getUpper()
- + "}}";
- }
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(mTimestamp, mSleepTimeMs, mIdleTimeMs, mRxTimeMs);
+ result = 31 * result + Arrays.hashCode(mTxTimeMs);
+ return result;
}
}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index cab6209..d3fca3e 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2604,13 +2604,12 @@
/**
* Send an MMS message
*
- * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the operation being completed on the subscription associated
- * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
- * operation is performed on the correct subscription.
+ * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+ * manager on a multi-SIM device, this operation may fail sending the MMS message because no
+ * suitable default subscription could be found. In this case, if {@code sentIntent} is
+ * non-null, then the {@link PendingIntent} will be sent with an error code
+ * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the
+ * conditions where this operation may fail.
* </p>
*
* @param context application context
@@ -2629,21 +2628,30 @@
}
MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
if (m != null) {
- m.sendMultimediaMessage(getSubscriptionId(), contentUri, locationUrl, configOverrides,
- sentIntent, 0L /* messageId */);
+ resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+ @Override
+ public void onSuccess(int subId) {
+ m.sendMultimediaMessage(subId, contentUri, locationUrl, configOverrides,
+ sentIntent, 0L /* messageId */);
+ }
+
+ @Override
+ public void onFailure() {
+ notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP);
+ }
+ });
}
}
/**
* Download an MMS message from carrier by a given location URL
*
- * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the operation being completed on the subscription associated
- * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
- * operation is performed on the correct subscription.
+ * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this
+ * manager on a multi-SIM device, this operation may fail downloading the MMS message because no
+ * suitable default subscription could be found. In this case, if {@code downloadedIntent} is
+ * non-null, then the {@link PendingIntent} will be sent with an error code
+ * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the
+ * conditions where this operation may fail.
* </p>
*
* @param context application context
@@ -2666,8 +2674,18 @@
}
MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE);
if (m != null) {
- m.downloadMultimediaMessage(getSubscriptionId(), locationUrl, contentUri,
- configOverrides, downloadedIntent, 0L /* messageId */);
+ resolveSubscriptionForOperation(new SubscriptionResolverResult() {
+ @Override
+ public void onSuccess(int subId) {
+ m.downloadMultimediaMessage(subId, locationUrl, contentUri, configOverrides,
+ downloadedIntent, 0L /* messageId */);
+ }
+
+ @Override
+ public void onFailure() {
+ notifySmsError(downloadedIntent, RESULT_NO_DEFAULT_SMS_APP);
+ }
+ });
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 12e56cc..a202644 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2835,11 +2835,13 @@
};
/**
- * Return a collection of all network types
- * @return network types
+ * Returns an array of all valid network types.
+ *
+ * @return An integer array containing all valid network types in no particular order.
*
* @hide
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static @NonNull @NetworkType int[] getAllNetworkTypes() {
return NETWORK_TYPES;
}
@@ -10397,19 +10399,25 @@
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
* and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+ *
+ * May return {@code null} when the subscription is inactive or when there was an error
+ * communicating with the phone process.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(allOf = {
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.ACCESS_COARSE_LOCATION
})
- public ServiceState getServiceState() {
+ public @Nullable ServiceState getServiceState() {
return getServiceStateForSubscriber(getSubId());
}
/**
* Returns the service state information on specified subscription. Callers require
* either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
+ *
+ * May return {@code null} when the subscription is inactive or when there was an error
+ * communicating with the phone process.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -10436,9 +10444,9 @@
* @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
* voicemail ringtone.
* @return The URI for the ringtone to play when receiving a voicemail from a specific
- * PhoneAccount.
+ * PhoneAccount. May be {@code null} if no ringtone is set.
*/
- public Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
+ public @Nullable Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
try {
ITelephony service = getITelephony();
if (service != null) {
@@ -12808,7 +12816,7 @@
* 1) User data is turned on, or
* 2) APN is un-metered for this subscription, or
* 3) APN type is whitelisted. E.g. MMS is whitelisted if
- * {@link #setAlwaysAllowMmsData(boolean)} is turned on.
+ * {@link #MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED} is enabled.
*
* @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
* @return whether data is enabled for a apn type.
@@ -13285,26 +13293,66 @@
}
/**
- * Set allowing mobile data during voice call. This is used for allowing data on the non-default
- * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will
- * not be able to use mobile data. By calling this API, data will be temporarily enabled on the
- * non-default data SIM during the life cycle of the voice call.
+ * Controls whether mobile data on the non-default SIM is allowed during a voice call.
*
- * @param allow {@code true} if allowing using data during voice call, {@code false} if
- * disallowed.
+ * This is used for allowing data on the non-default data SIM when a voice call is placed on
+ * the non-default data SIM on DSDS devices. If this policy is disabled, users will not be able
+ * to use mobile data via the non-default data SIM during the call, which may mean no mobile
+ * data at all since some modem implementations disallow mobile data via the default data SIM
+ * during voice calls.
+ * If this policy is enabled, data will be temporarily enabled on the non-default data SIM
+ * during any voice calls.
*
- * @return {@code true} if operation is successful. otherwise {@code false}.
- *
- * @throws SecurityException if the caller doesn't have the permission.
- *
+ * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
* @hide
*/
+ @SystemApi
+ @TestApi
+ public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1;
+
+ /**
+ * Controls whether MMS messages bypass the user-specified "mobile data" toggle.
+ *
+ * When enabled, requests for connections to the MMS APN will be accepted by telephony even if
+ * the user has turned "mobile data" off on this specific sim card. {@link #isDataEnabledForApn}
+ * will also return true for {@link ApnSetting#TYPE_MMS}.
+ * When disabled, the MMS APN will be governed by the same rules as all other APNs.
+ *
+ * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "MOBILE_DATA_POLICY_" }, value = {
+ MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL,
+ MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MobileDataPolicy { }
+
+ /**
+ * Enables or disables a piece of mobile data policy.
+ *
+ * Enables or disables the mobile data policy specified in {@code policy}. See the detailed
+ * description of each policy constant for what they do.
+ *
+ * @param policy The data policy to enable.
+ * @param enabled Whether to enable or disable the policy.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public boolean setDataAllowedDuringVoiceCall(boolean allow) {
+ public void setMobileDataPolicyEnabledStatus(@MobileDataPolicy int policy, boolean enabled) {
try {
ITelephony service = getITelephony();
if (service != null) {
- return service.setDataAllowedDuringVoiceCall(getSubId(), allow);
+ service.setMobileDataPolicyEnabledStatus(getSubId(), policy, enabled);
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
@@ -13312,27 +13360,23 @@
ex.rethrowAsRuntimeException();
}
}
- return false;
}
/**
- * Check whether data is allowed during voice call. This is used for allowing data on the
- * non-default data SIM. When a voice call is placed on the non-default data SIM on DSDS
- * devices, users will not be able to use mobile data. By calling this API, data will be
- * temporarily enabled on the non-default data SIM during the life cycle of the voice call.
+ * Fetches the status of a piece of mobile data policy.
*
- * @return {@code true} if data is allowed during voice call.
- *
- * @throws SecurityException if the caller doesn't have the permission.
- *
+ * @param policy The data policy that you want the status for.
+ * @return {@code true} if enabled, {@code false} otherwise.
* @hide
*/
+ @SystemApi
+ @TestApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public boolean isDataAllowedInVoiceCall() {
+ public boolean isMobileDataPolicyEnabled(@MobileDataPolicy int policy) {
try {
ITelephony service = getITelephony();
if (service != null) {
- return service.isDataAllowedInVoiceCall(getSubId());
+ return service.isMobileDataPolicyEnabled(getSubId(), policy);
}
} catch (RemoteException ex) {
// This could happen if binder process crashes.
@@ -13344,31 +13388,6 @@
}
/**
- * Set whether the specific sim card always allows MMS connection. If true, MMS network
- * request will be accepted by telephony even if user turns "mobile data" off
- * on this specific sim card.
- *
- * @param alwaysAllow whether Mms data is always allowed.
- * @return whether operation is successful.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public boolean setAlwaysAllowMmsData(boolean alwaysAllow) {
- try {
- ITelephony service = getITelephony();
- if (service != null) {
- return service.setAlwaysAllowMmsData(getSubId(), alwaysAllow);
- }
- } catch (RemoteException ex) {
- if (!isSystemProcess()) {
- ex.rethrowAsRuntimeException();
- }
- }
- return false;
- }
-
- /**
* The IccLock state or password was changed successfully.
* @hide
*/
@@ -13671,9 +13690,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
* @throws SecurityException if the caller doesn't have the permission.
*
- * @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public @NonNull List<String> getEquivalentHomePlmns() {
try {
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 579200e..41381c5 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -411,6 +411,25 @@
};
/**
+ * Convert handover failure mode to string.
+ *
+ * @param handoverFailureMode Handover failure mode
+ * @return Handover failure mode in string
+ *
+ * @hide
+ */
+ public static String failureModeToString(@HandoverFailureMode int handoverFailureMode) {
+ switch (handoverFailureMode) {
+ case HANDOVER_FAILURE_MODE_UNKNOWN: return "unknown";
+ case HANDOVER_FAILURE_MODE_LEGACY: return "legacy";
+ case HANDOVER_FAILURE_MODE_DO_FALLBACK: return "fallback";
+ case HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER: return "retry handover";
+ case HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL: return "retry setup new one";
+ default: return Integer.toString(handoverFailureMode);
+ }
+ }
+
+ /**
* Provides a convenient way to set the fields of a {@link DataCallResponse} when creating a new
* instance.
*
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 8a05bdf..90a6de7 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -17,6 +17,7 @@
package android.telephony.ims;
import android.annotation.LongDef;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.Service;
@@ -30,12 +31,14 @@
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsServiceController;
import android.telephony.ims.aidl.IImsServiceControllerListener;
+import android.telephony.ims.aidl.ISipTransport;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature;
import android.telephony.ims.stub.ImsConfigImplBase;
import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.SipTransportImplBase;
import android.util.Log;
import android.util.SparseArray;
@@ -105,18 +108,47 @@
/**
* This ImsService supports the capability to place emergency calls over MMTEL.
+ * <p>
+ * Note: This should never be set by {@link #getImsServiceCapabilities()}, as whether it is
+ * there or not depends on whether or not {@link ImsFeature#FEATURE_EMERGENCY_MMTEL} is defined
+ * for this ImsService. If it is set, it will be removed during sanitization before the final
+ * capabilities bitfield is sent back to the framework.
* @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be
* adding other capabilities in a central location, so track this capability here as well.
*/
public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0;
/**
+ * This ImsService supports the capability to create SIP delegates for other IMS applications
+ * to use to proxy SIP messaging traffic through it.
+ * <p>
+ * In order for the framework to report SipDelegate creation as being available for this
+ * ImsService implementation, this ImsService must report this capability flag in
+ * {@link #getImsServiceCapabilities()}, {@link #getSipTransport(int)} must not return null, and
+ * this ImsService MUST report the ability to create both {@link ImsFeature#FEATURE_MMTEL} and
+ * {@link ImsFeature#FEATURE_RCS} features.
+ * @hide
+ */
+ public static final long CAPABILITY_SIP_DELEGATE_CREATION = 1 << 1;
+
+ /**
+ * Used for internal correctness checks of capabilities set by the ImsService implementation and
+ * tracks the index of the largest defined flag in the capabilities long.
+ * @hide
+ */
+ public static final long CAPABILITY_MAX_INDEX =
+ Long.numberOfTrailingZeros(CAPABILITY_SIP_DELEGATE_CREATION);
+
+ /**
* @hide
*/
@LongDef(flag = true,
prefix = "CAPABILITY_",
value = {
- CAPABILITY_EMERGENCY_OVER_MMTEL
+ // CAPABILITY_EMERGENCY_OVER_MMTEL is not included here because it is managed by
+ // whether or not ImsFeature.FEATURE_EMERGENCY_MMTEL feature is set and should
+ // not be set by users of ImsService.
+ CAPABILITY_SIP_DELEGATE_CREATION
})
@Retention(RetentionPolicy.SOURCE)
public @interface ImsServiceCapability {}
@@ -127,6 +159,7 @@
*/
private static final Map<Long, String> CAPABILITIES_LOG_MAP = new HashMap<Long, String>() {{
put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL");
+ put(CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION");
}};
/**
@@ -201,6 +234,17 @@
}
@Override
+ public long getImsServiceCapabilities() {
+ long caps = ImsService.this.getImsServiceCapabilities();
+ long sanitizedCaps = sanitizeCapabilities(caps);
+ if (caps != sanitizedCaps) {
+ Log.w(LOG_TAG, "removing invalid bits from field: 0x"
+ + Long.toHexString(caps ^ sanitizedCaps));
+ }
+ return sanitizedCaps;
+ }
+
+ @Override
public void notifyImsServiceReadyForFeatureCreation() {
ImsService.this.readyForFeatureCreation();
}
@@ -218,6 +262,12 @@
}
@Override
+ public ISipTransport getSipTransport(int slotId) {
+ SipTransportImplBase s = ImsService.this.getSipTransport(slotId);
+ return s != null ? s.getBinder() : null;
+ }
+
+ @Override
public void enableIms(int slotId) {
ImsService.this.enableIms(slotId);
}
@@ -371,6 +421,17 @@
}
/**
+ * The optional capabilities that this ImsService supports.
+ * <p>
+ * This should be a static configuration and should not change at runtime.
+ * @return The optional static capabilities of this ImsService implementation.
+ * @hide
+ */
+ public @ImsServiceCapability long getImsServiceCapabilities() {
+ return 0L;
+ }
+
+ /**
* The ImsService has been bound and is ready for ImsFeature creation based on the Features that
* the ImsService has registered for with the framework, either in the manifest or via
* {@link #querySupportedImsFeatures()}.
@@ -443,7 +504,37 @@
}
/**
- * @return A string representation of the ImsService capabilties for logging.
+ * Return the {@link SipTransportImplBase} implementation associated with the provided slot.
+ * <p>
+ * This is an optional interface used for devices that must support IMS single registration and
+ * proxy SIP traffic to remote IMS applications. If this is not supported for this IMS service,
+ * this method should return {@code null}. If this feature is supported, then this method must
+ * never be {@code null} and the optional ImsService capability flag
+ * {@link #CAPABILITY_SIP_DELEGATE_CREATION} must be set in
+ * {@link #getImsServiceCapabilities()}. Otherwise the framework will assume this feature is not
+ * supported for this ImsService.
+ * @param slotId The slot that is associated with the SipTransport implementation.
+ * @return the SipTransport implementation for the specified slot.
+ * @hide Keep this hidden until there is something to expose in SipTransport.
+ */
+ public @Nullable SipTransportImplBase getSipTransport(int slotId) {
+ return null;
+ }
+
+ private static long sanitizeCapabilities(@ImsServiceCapability long caps) {
+ long filter = 0xFFFFFFFFFFFFFFFFL;
+ // pad the "allowed" set with zeros
+ filter <<= CAPABILITY_MAX_INDEX + 1;
+ // remove values above the allowed set.
+ caps &= ~filter;
+ // CAPABILITY_EMERGENCY_OVER_MMTEL should also not be set here, will be set by telephony
+ // internally.
+ caps &= ~CAPABILITY_EMERGENCY_OVER_MMTEL;
+ return caps;
+ }
+
+ /**
+ * @return A string representation of the ImsService capabilities for logging.
* @hide
*/
public static String getCapabilitiesString(@ImsServiceCapability long caps) {
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl
similarity index 81%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl
index 71cd0a7..a704702 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 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,6 +14,7 @@
* limitations under the License.
*/
-package android.media.tv;
-parcelable TvChannelInfo;
+package android.telephony.ims;
+
+parcelable RcsContactPresenceTuple;
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
new file mode 100644
index 0000000..b0aaa92
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -0,0 +1,360 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Represents a PIDF tuple element that is part of the presence element returned from the carrier
+ * network during a SUBSCRIBE request. See RFC3863 for more information.
+ * @hide
+ */
+public class RcsContactPresenceTuple implements Parcelable {
+
+ /** The service id of the MMTEL */
+ public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
+
+ /** The service capabilities is available. */
+ public static final String TUPLE_BASIC_STATUS_OPEN = "open";
+
+ /** The service capabilities is unavailable. */
+ public static final String TUPLE_BASIC_STATUS_CLOSED = "closed";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "TUPLE_BASIC_STATUS_", value = {
+ TUPLE_BASIC_STATUS_OPEN,
+ TUPLE_BASIC_STATUS_CLOSED
+ })
+ public @interface BasicStatus {}
+
+ /**
+ * An optional addition to the PIDF Presence Tuple containing service capabilities, which is
+ * defined in the servcaps element. See RFC5196, section 3.2.1.
+ */
+ public static class ServiceCapabilities implements Parcelable {
+
+ /** The service can simultaneously send and receive data. */
+ public static final String DUPLEX_MODE_FULL = "full";
+
+ /** The service can alternate between sending and receiving data.*/
+ public static final String DUPLEX_MODE_HALF = "half";
+
+ /** The service can only receive data. */
+ public static final String DUPLEX_MODE_RECEIVE_ONLY = "receive-only";
+
+ /** The service can only send data. */
+ public static final String DUPLEX_MODE_SEND_ONLY = "send-only";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "DUPLEX_MODE_", value = {
+ DUPLEX_MODE_FULL,
+ DUPLEX_MODE_HALF,
+ DUPLEX_MODE_RECEIVE_ONLY,
+ DUPLEX_MODE_SEND_ONLY
+ })
+ public @interface DuplexMode {}
+
+ /**
+ * Builder to help construct {@link ServiceCapabilities} instances.
+ */
+ public static class Builder {
+
+ private ServiceCapabilities mCapabilities;
+
+ /**
+ * Create the ServiceCapabilities builder, which can be used to set service capabilities
+ * as well as custom capability extensions.
+ * @param isAudioCapable Whether the audio is capable or not.
+ * @param isVideoCapable Whether the video is capable or not.
+ */
+ public Builder(boolean isAudioCapable, boolean isVideoCapable) {
+ mCapabilities = new ServiceCapabilities(isAudioCapable, isVideoCapable);
+ }
+
+ /**
+ * Add the supported duplex mode.
+ * @param mode The supported duplex mode
+ */
+ public Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) {
+ mCapabilities.mSupportedDuplexModeList.add(mode);
+ return this;
+ }
+
+ /**
+ * Add the unsupported duplex mode.
+ * @param mode The unsupported duplex mode
+ */
+ public Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) {
+ mCapabilities.mUnsupportedDuplexModeList.add(mode);
+ return this;
+ }
+
+ /**
+ * @return the ServiceCapabilities instance.
+ */
+ public ServiceCapabilities build() {
+ return mCapabilities;
+ }
+ }
+
+ private final boolean mIsAudioCapable;
+ private final boolean mIsVideoCapable;
+ private final @DuplexMode List<String> mSupportedDuplexModeList = new ArrayList<>();
+ private final @DuplexMode List<String> mUnsupportedDuplexModeList = new ArrayList<>();
+
+ /**
+ * Use {@link Builder} to build an instance of this interface.
+ * @param isAudioCapable Whether the audio is capable.
+ * @param isVideoCapable Whether the video is capable.
+ */
+ ServiceCapabilities(boolean isAudioCapable, boolean isVideoCapable) {
+ mIsAudioCapable = isAudioCapable;
+ mIsVideoCapable = isVideoCapable;
+ }
+
+ private ServiceCapabilities(Parcel in) {
+ mIsAudioCapable = in.readBoolean();
+ mIsVideoCapable = in.readBoolean();
+ in.readStringList(mSupportedDuplexModeList);
+ in.readStringList(mUnsupportedDuplexModeList);
+ }
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeBoolean(mIsAudioCapable);
+ out.writeBoolean(mIsVideoCapable);
+ out.writeStringList(mSupportedDuplexModeList);
+ out.writeStringList(mUnsupportedDuplexModeList);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<ServiceCapabilities> CREATOR =
+ new Creator<ServiceCapabilities>() {
+ @Override
+ public ServiceCapabilities createFromParcel(Parcel in) {
+ return new ServiceCapabilities(in);
+ }
+
+ @Override
+ public ServiceCapabilities[] newArray(int size) {
+ return new ServiceCapabilities[size];
+ }
+ };
+
+ /**
+ * Query the audio capable.
+ * @return true if the audio is capable, false otherwise.
+ */
+ public boolean isAudioCapable() {
+ return mIsAudioCapable;
+ }
+
+ /**
+ * Query the video capable.
+ * @return true if the video is capable, false otherwise.
+ */
+ public boolean isVideoCapable() {
+ return mIsVideoCapable;
+ }
+
+ /**
+ * Get the supported duplex mode list.
+ * @return The list of supported duplex mode
+ */
+ public @NonNull @DuplexMode List<String> getSupportedDuplexModes() {
+ return Collections.unmodifiableList(mSupportedDuplexModeList);
+ }
+
+ /**
+ * Get the unsupported duplex mode list.
+ * @return The list of unsupported duplex mode
+ */
+ public @NonNull @DuplexMode List<String> getUnsupportedDuplexModes() {
+ return Collections.unmodifiableList(mUnsupportedDuplexModeList);
+ }
+ }
+
+ /**
+ * Builder to help construct {@link RcsContactPresenceTuple} instances.
+ */
+ public static class Builder {
+
+ private RcsContactPresenceTuple mPresenceTuple;
+
+ /**
+ * Builds a RcsContactPresenceTuple instance.
+ * @param serviceId The OMA Presence service-id associated with this capability. See the
+ * OMA Presence SIMPLE specification v1.1, section 10.5.1.
+ * @param serviceVersion The OMA Presence version associated with the service capability.
+ * See the OMA Presence SIMPLE specification v1.1, section 10.5.1.
+ */
+ public Builder(@NonNull @BasicStatus String status, @NonNull String serviceId,
+ @NonNull String serviceVersion) {
+ mPresenceTuple = new RcsContactPresenceTuple(status, serviceId, serviceVersion);
+ }
+
+ /**
+ * The optional SIP Contact URI associated with the PIDF tuple element.
+ */
+ public Builder addContactUri(@NonNull Uri contactUri) {
+ mPresenceTuple.mContactUri = contactUri;
+ return this;
+ }
+
+ /**
+ * The optional timestamp indicating the data and time of the status change of this tuple.
+ * See RFC3863, section 4.1.7 for more information on the expected format.
+ */
+ public Builder addTimeStamp(@NonNull String timestamp) {
+ mPresenceTuple.mTimestamp = timestamp;
+ return this;
+ }
+
+ /**
+ * An optional parameter containing the description element of the service-description. See
+ * OMA Presence SIMPLE specification v1.1
+ */
+ public Builder addDescription(@NonNull String description) {
+ mPresenceTuple.mServiceDescription = description;
+ return this;
+ }
+
+ /**
+ * An optional parameter containing the service capabilities of the presence tuple if they
+ * are present in the servcaps element.
+ */
+ public Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) {
+ mPresenceTuple.mServiceCapabilities = caps;
+ return this;
+ }
+
+ /**
+ * @return the constructed instance.
+ */
+ public RcsContactPresenceTuple build() {
+ return mPresenceTuple;
+ }
+ }
+
+ private Uri mContactUri;
+ private String mTimestamp;
+ private @BasicStatus String mStatus;
+
+ // The service information in the service-description element.
+ private String mServiceId;
+ private String mServiceVersion;
+ private String mServiceDescription;
+
+ private ServiceCapabilities mServiceCapabilities;
+
+ private RcsContactPresenceTuple(@NonNull @BasicStatus String status, @NonNull String serviceId,
+ @NonNull String serviceVersion) {
+ mStatus = status;
+ mServiceId = serviceId;
+ mServiceVersion = serviceVersion;
+ }
+
+ private RcsContactPresenceTuple(Parcel in) {
+ mContactUri = in.readParcelable(Uri.class.getClassLoader());
+ mTimestamp = in.readString();
+ mStatus = in.readString();
+ mServiceId = in.readString();
+ mServiceVersion = in.readString();
+ mServiceDescription = in.readString();
+ mServiceCapabilities = in.readParcelable(ServiceCapabilities.class.getClassLoader());
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeParcelable(mContactUri, flags);
+ out.writeString(mTimestamp);
+ out.writeString(mStatus);
+ out.writeString(mServiceId);
+ out.writeString(mServiceVersion);
+ out.writeString(mServiceDescription);
+ out.writeParcelable(mServiceCapabilities, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<RcsContactPresenceTuple> CREATOR =
+ new Creator<RcsContactPresenceTuple>() {
+ @Override
+ public RcsContactPresenceTuple createFromParcel(Parcel in) {
+ return new RcsContactPresenceTuple(in);
+ }
+
+ @Override
+ public RcsContactPresenceTuple[] newArray(int size) {
+ return new RcsContactPresenceTuple[size];
+ }
+ };
+
+ /** @return the status of the tuple element. */
+ public @NonNull @BasicStatus String getStatus() {
+ return mStatus;
+ }
+
+ /** @return the service-id element of the service-description */
+ public @NonNull String getServiceId() {
+ return mServiceId;
+ }
+
+ /** @return the version element of the service-description */
+ public @NonNull String getServiceVersion() {
+ return mServiceVersion;
+ }
+
+ /** @return the SIP URI contained in the contact element of the tuple if it exists. */
+ public @Nullable Uri getContactUri() {
+ return mContactUri;
+ }
+
+ /** @return the timestamp element contained in the tuple if it exists */
+ public @Nullable String getTimestamp() {
+ return mTimestamp;
+ }
+
+ /** @return the description element contained in the service-description if it exists */
+ public @Nullable String getServiceDescription() {
+ return mServiceDescription;
+ }
+
+ /** @return the {@link ServiceCapabilities} of the tuple if it exists. */
+ public @Nullable ServiceCapabilities getServiceCapabilities() {
+ return mServiceCapabilities;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index dc36edf..d12a6ae 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -16,7 +16,7 @@
package android.telephony.ims;
-import android.annotation.LongDef;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Uri;
@@ -27,9 +27,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
/**
* Contains the User Capability Exchange capabilities corresponding to a contact's URI.
@@ -37,114 +35,80 @@
*/
public final class RcsContactUceCapability implements Parcelable {
- /** Supports 1-to-1 chat */
- public static final int CAPABILITY_CHAT_STANDALONE = (1 << 0);
- /** Supports group chat */
- public static final int CAPABILITY_CHAT_SESSION = (1 << 1);
- /** Supports full store and forward group chat information. */
- public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = (1 << 2);
- /**
- * Supports file transfer via Message Session Relay Protocol (MSRP) without Store and Forward.
- */
- public static final int CAPABILITY_FILE_TRANSFER = (1 << 3);
- /** Supports File Transfer Thumbnail */
- public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = (1 << 4);
- /** Supports File Transfer with Store and Forward */
- public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = (1 << 5);
- /** Supports File Transfer via HTTP */
- public static final int CAPABILITY_FILE_TRANSFER_HTTP = (1 << 6);
- /** Supports file transfer via SMS */
- public static final int CAPABILITY_FILE_TRANSFER_SMS = (1 << 7);
- /** Supports image sharing */
- public static final int CAPABILITY_IMAGE_SHARE = (1 << 8);
- /** Supports video sharing during a circuit-switch call (IR.74)*/
- public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = (1 << 9);
- /** Supports video share outside of voice call (IR.84) */
- public static final int CAPABILITY_VIDEO_SHARE = (1 << 10);
- /** Supports social presence information */
- public static final int CAPABILITY_SOCIAL_PRESENCE = (1 << 11);
- /** Supports capability discovery via presence */
- public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = (1 << 12);
- /** Supports IP Voice calling over LTE or IWLAN (IR.92/IR.51) */
- public static final int CAPABILITY_IP_VOICE_CALL = (1 << 13);
- /** Supports IP video calling (IR.94) */
- public static final int CAPABILITY_IP_VIDEO_CALL = (1 << 14);
- /** Supports Geolocation PUSH during 1-to-1 or multiparty chat */
- public static final int CAPABILITY_GEOLOCATION_PUSH = (1 << 15);
- /** Supports Geolocation PUSH via SMS for fallback. */
- public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = (1 << 16);
- /** Supports Geolocation pull. */
- public static final int CAPABILITY_GEOLOCATION_PULL = (1 << 17);
- /** Supports Geolocation pull using file transfer support. */
- public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = (1 << 18);
- /** Supports RCS voice calling */
- public static final int CAPABILITY_RCS_VOICE_CALL = (1 << 19);
- /** Supports RCS video calling */
- public static final int CAPABILITY_RCS_VIDEO_CALL = (1 << 20);
- /** Supports RCS video calling, where video media can not be dropped. */
- public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = (1 << 21);
- /** Supports call composer, where outgoing calls can be enriched with pre-call content.*/
- public static final int CAPABILITY_CALL_COMPOSER = (1 << 22);
- /** Supports post call information that is included in the call if the call is missed.*/
- public static final int CAPABILITY_POST_CALL = (1 << 23);
- /** Supports sharing a map where the user can draw, share markers, and share their position. */
- public static final int CAPABILITY_SHARED_MAP = (1 << 24);
- /** Supports sharing a canvas, where users can draw, add images, and change background colors.*/
- public static final int CAPABILITY_SHARED_SKETCH = (1 << 25);
- /** Supports communication with Chatbots. */
- public static final int CAPABILITY_CHAT_BOT = (1 << 26);
- /** Supports Chatbot roles. */
- public static final int CAPABILITY_CHAT_BOT_ROLE = (1 << 27);
- /** Supports the unidirectional plug-ins framework. */
- public static final int CAPABILITY_PLUG_IN = (1 << 28);
- /** Supports standalone Chatbot communication. */
- public static final int CAPABILITY_STANDALONE_CHAT_BOT = (1 << 29);
- /** Supports MMTEL based call composer. */
- public static final int CAPABILITY_MMTEL_CALL_COMPOSER = (1 << 30);
+ /** Contains presence information associated with the contact */
+ public static final int CAPABILITY_MECHANISM_PRESENCE = 1;
+ /** Contains OPTIONS information associated with the contact */
+ public static final int CAPABILITY_MECHANISM_OPTIONS = 2;
-
- /** @hide*/
+ /** @hide */
@Retention(RetentionPolicy.SOURCE)
- @LongDef(prefix = "CAPABILITY_", flag = true, value = {
- CAPABILITY_CHAT_STANDALONE,
- CAPABILITY_CHAT_SESSION,
- CAPABILITY_CHAT_SESSION_STORE_FORWARD,
- CAPABILITY_FILE_TRANSFER,
- CAPABILITY_FILE_TRANSFER_THUMBNAIL,
- CAPABILITY_FILE_TRANSFER_STORE_FORWARD,
- CAPABILITY_FILE_TRANSFER_HTTP,
- CAPABILITY_FILE_TRANSFER_SMS,
- CAPABILITY_IMAGE_SHARE,
- CAPABILITY_VIDEO_SHARE_DURING_CS_CALL,
- CAPABILITY_VIDEO_SHARE,
- CAPABILITY_SOCIAL_PRESENCE,
- CAPABILITY_DISCOVERY_VIA_PRESENCE,
- CAPABILITY_IP_VOICE_CALL,
- CAPABILITY_IP_VIDEO_CALL,
- CAPABILITY_GEOLOCATION_PUSH,
- CAPABILITY_GEOLOCATION_PUSH_SMS,
- CAPABILITY_GEOLOCATION_PULL,
- CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER,
- CAPABILITY_RCS_VOICE_CALL,
- CAPABILITY_RCS_VIDEO_CALL,
- CAPABILITY_RCS_VIDEO_ONLY_CALL,
- CAPABILITY_CALL_COMPOSER,
- CAPABILITY_POST_CALL,
- CAPABILITY_SHARED_MAP,
- CAPABILITY_SHARED_SKETCH,
- CAPABILITY_CHAT_BOT,
- CAPABILITY_CHAT_BOT_ROLE,
- CAPABILITY_PLUG_IN,
- CAPABILITY_STANDALONE_CHAT_BOT,
- CAPABILITY_MMTEL_CALL_COMPOSER
+ @IntDef(prefix = "CAPABILITY_MECHANISM_", value = {
+ CAPABILITY_MECHANISM_PRESENCE,
+ CAPABILITY_MECHANISM_OPTIONS
})
- public @interface CapabilityFlag {}
+ public @interface CapabilityMechanism {}
/**
- * Builder to help construct {@link RcsContactUceCapability} instances.
+ * The capabilities of this contact were requested recently enough to still be considered in
+ * the availability window.
*/
- public static class Builder {
+ public static final int SOURCE_TYPE_NETWORK = 0;
+
+ /**
+ * The capabilities of this contact were retrieved from the cached information in the Enhanced
+ * Address Book.
+ */
+ public static final int SOURCE_TYPE_CACHED = 1;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "SOURCE_TYPE_", value = {
+ SOURCE_TYPE_NETWORK,
+ SOURCE_TYPE_CACHED
+ })
+ public @interface SourceType {}
+
+ /**
+ * The requested contact was found to be offline when queried. This is only applicable to
+ * contact capabilities that were queried via OPTIONS requests and the network returned a
+ * 408/480 response.
+ */
+ public static final int REQUEST_RESULT_NOT_ONLINE = 0;
+
+ /**
+ * Capability information for the requested contact was not found. The contact should not be
+ * considered an RCS user.
+ */
+ public static final int REQUEST_RESULT_NOT_FOUND = 1;
+
+ /**
+ * Capability information for the requested contact was found successfully.
+ */
+ public static final int REQUEST_RESULT_FOUND = 2;
+
+ /**
+ * Capability information for the requested contact has expired and can not be refreshed due to
+ * a temporary network error. This is a temporary error and the capabilities of the contact
+ * should be queried again at a later time.
+ */
+ public static final int REQUEST_RESULT_UNKNOWN = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "REQUEST_RESULT_", value = {
+ REQUEST_RESULT_NOT_ONLINE,
+ REQUEST_RESULT_NOT_FOUND,
+ REQUEST_RESULT_FOUND,
+ REQUEST_RESULT_UNKNOWN
+ })
+ public @interface RequestResult {}
+
+ /**
+ * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
+ * queried through SIP OPTIONS.
+ */
+ public static class OptionsBuilder {
private final RcsContactUceCapability mCapabilities;
@@ -153,51 +117,38 @@
* capability extensions.
* @param contact The contact URI that the capabilities are attached to.
*/
- public Builder(@NonNull Uri contact) {
- mCapabilities = new RcsContactUceCapability(contact);
+ public OptionsBuilder(@NonNull Uri contact) {
+ mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_OPTIONS,
+ SOURCE_TYPE_NETWORK);
}
/**
- * Add a UCE capability bit-field as well as the associated URI that the framework should
- * use for those services. This is mainly used for capabilities that may use a URI separate
- * from the contact's URI, for example the URI to use for VT calls.
- * @param type The capability to map to a service URI that is different from the contact's
- * URI.
+ * Set the result of the capabilities request.
+ * @param requestResult the request result
+ * @return this OptionBuilder
*/
- public @NonNull Builder add(@CapabilityFlag long type, @NonNull Uri serviceUri) {
- mCapabilities.mCapabilities |= type;
- // Put each of these capabilities into the map separately.
- for (long shift = 0; shift < Integer.SIZE; shift++) {
- long cap = type & (1 << shift);
- if (cap != 0) {
- mCapabilities.mServiceMap.put(cap, serviceUri);
- // remove that capability from the field.
- type &= ~cap;
- }
- if (type == 0) {
- // no need to keep going, end early.
- break;
- }
- }
+ public @NonNull OptionsBuilder setRequestResult(@RequestResult int requestResult) {
+ mCapabilities.mRequestResult = requestResult;
return this;
}
/**
- * Add a UCE capability flag that this contact supports.
- * @param type the capability that the contact supports.
+ * Add the feature tag into the capabilities instance.
+ * @param tag the supported feature tag
+ * @return this OptionBuilder
*/
- public @NonNull Builder add(@CapabilityFlag long type) {
- mCapabilities.mCapabilities |= type;
+ public @NonNull OptionsBuilder addFeatureTag(String tag) {
+ mCapabilities.mFeatureTags.add(tag);
return this;
}
/**
- * Add a carrier specific service tag.
- * @param extension A string containing a carrier specific service tag that is an extension
- * of the {@link CapabilityFlag}s that are defined here.
+ * Add the list of feature tag into the capabilities instance.
+ * @param tags the list of the supported feature tags
+ * @return this OptionBuilder
*/
- public @NonNull Builder add(@NonNull String extension) {
- mCapabilities.mExtensionTags.add(extension);
+ public @NonNull OptionsBuilder addFeatureTags(List<String> tags) {
+ mCapabilities.mFeatureTags.addAll(tags);
return this;
}
@@ -209,56 +160,88 @@
}
}
- private final Uri mContactUri;
- private long mCapabilities;
- private List<String> mExtensionTags = new ArrayList<>();
- private Map<Long, Uri> mServiceMap = new HashMap<>();
-
/**
- * Use {@link Builder} to build an instance of this interface.
- * @param contact The URI associated with this capability information.
- * @hide
+ * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
+ * queried through a presence server.
*/
- RcsContactUceCapability(@NonNull Uri contact) {
- mContactUri = contact;
+ public static class PresenceBuilder {
+
+ private final RcsContactUceCapability mCapabilities;
+
+ /**
+ * Create the builder, which can be used to set UCE capabilities as well as custom
+ * capability extensions.
+ * @param contact The contact URI that the capabilities are attached to.
+ * @param sourceType The type where the capabilities of this contact were retrieved from.
+ * @param requestResult the request result
+ */
+ public PresenceBuilder(@NonNull Uri contact, @SourceType int sourceType,
+ @RequestResult int requestResult) {
+ mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_PRESENCE,
+ sourceType);
+ mCapabilities.mRequestResult = requestResult;
+ }
+
+ /**
+ * Add the {@link RcsContactPresenceTuple} into the capabilities instance.
+ * @param tuple The {@link RcsContactPresenceTuple} to be added into.
+ * @return this PresenceBuilder
+ */
+ public @NonNull PresenceBuilder addCapabilityTuple(RcsContactPresenceTuple tuple) {
+ mCapabilities.mPresenceTuples.add(tuple);
+ return this;
+ }
+
+ /**
+ * Add the list of {@link RcsContactPresenceTuple} into the capabilities instance.
+ * @param tuples The list of the {@link RcsContactPresenceTuple} to be added into.
+ * @return this PresenceBuilder
+ */
+ public @NonNull PresenceBuilder addCapabilityTuples(List<RcsContactPresenceTuple> tuples) {
+ mCapabilities.mPresenceTuples.addAll(tuples);
+ return this;
+ }
+
+ /**
+ * @return the RcsContactUceCapability instance.
+ */
+ public @NonNull RcsContactUceCapability build() {
+ return mCapabilities;
+ }
+ }
+
+ private final Uri mContactUri;
+ private @SourceType int mSourceType;
+ private @CapabilityMechanism int mCapabilityMechanism;
+ private @RequestResult int mRequestResult;
+
+ private final List<String> mFeatureTags = new ArrayList<>();
+ private final List<RcsContactPresenceTuple> mPresenceTuples = new ArrayList<>();
+
+ private RcsContactUceCapability(@NonNull Uri contactUri, @CapabilityMechanism int mechanism,
+ @SourceType int sourceType) {
+ mContactUri = contactUri;
+ mCapabilityMechanism = mechanism;
+ mSourceType = sourceType;
}
private RcsContactUceCapability(Parcel in) {
mContactUri = in.readParcelable(Uri.class.getClassLoader());
- mCapabilities = in.readLong();
- in.readStringList(mExtensionTags);
- // read mServiceMap as key,value pair
- int mapSize = in.readInt();
- for (int i = 0; i < mapSize; i++) {
- mServiceMap.put(in.readLong(), in.readParcelable(Uri.class.getClassLoader()));
- }
+ mCapabilityMechanism = in.readInt();
+ mSourceType = in.readInt();
+ mRequestResult = in.readInt();
+ in.readStringList(mFeatureTags);
+ in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader());
}
- public static final @NonNull Creator<RcsContactUceCapability> CREATOR =
- new Creator<RcsContactUceCapability>() {
- @Override
- public RcsContactUceCapability createFromParcel(Parcel in) {
- return new RcsContactUceCapability(in);
- }
-
- @Override
- public RcsContactUceCapability[] newArray(int size) {
- return new RcsContactUceCapability[size];
- }
- };
-
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
- out.writeParcelable(mContactUri, 0);
- out.writeLong(mCapabilities);
- out.writeStringList(mExtensionTags);
- // write mServiceMap as key,value pairs
- int mapSize = mServiceMap.keySet().size();
- out.writeInt(mapSize);
- for (long key : mServiceMap.keySet()) {
- out.writeLong(key);
- out.writeParcelable(mServiceMap.get(key), 0);
- }
+ out.writeParcelable(mContactUri, flags);
+ out.writeInt(mCapabilityMechanism);
+ out.writeInt(mSourceType);
+ out.writeInt(mRequestResult);
+ out.writeStringList(mFeatureTags);
+ out.writeParcelableList(mPresenceTuples, flags);
}
@Override
@@ -266,49 +249,87 @@
return 0;
}
+ public static final @NonNull Creator<RcsContactUceCapability> CREATOR =
+ new Creator<RcsContactUceCapability>() {
+ @Override
+ public RcsContactUceCapability createFromParcel(Parcel in) {
+ return new RcsContactUceCapability(in);
+ }
+
+ @Override
+ public RcsContactUceCapability[] newArray(int size) {
+ return new RcsContactUceCapability[size];
+ }
+ };
+
/**
- * Query for a capability
- * @param type The capability flag to query.
- * @return true if the capability flag specified is set, false otherwise.
+ * @return The mechanism used to get the capabilities.
*/
- public boolean isCapable(@CapabilityFlag long type) {
- return (mCapabilities & type) > 0;
+ public @CapabilityMechanism int getCapabilityMechanism() {
+ return mCapabilityMechanism;
}
/**
- * @return true if the extension service tag is set, false otherwise.
- */
- public boolean isCapable(@NonNull String extensionTag) {
- return mExtensionTags.contains(extensionTag);
- }
-
- /**
- * @return An immutable list containing all of the extension tags that have been set as capable.
- * @throws UnsupportedOperationException if this list is modified.
- */
- public @NonNull List<String> getCapableExtensionTags() {
- return Collections.unmodifiableList(mExtensionTags);
- }
-
- /**
- * Retrieves the {@link Uri} associated with the capability being queried.
+ * @return The feature tags present in the OPTIONS response from the network.
* <p>
- * This will typically be the contact {@link Uri} available via {@link #getContactUri()} unless
- * a different service {@link Uri} was associated with this capability using
- * {@link Builder#add(long, Uri)}.
- *
- * @return a String containing the {@link Uri} associated with the service tag or
- * {@code null} if this capability is not set as capable.
- * @see #isCapable(long)
+ * Note: this is only populated if {@link #getCapabilityMechanism} is
+ * {@link CAPABILITY_MECHANISM_OPTIONS}
*/
- public @Nullable Uri getServiceUri(@CapabilityFlag long type) {
- Uri result = mServiceMap.getOrDefault(type, null);
- // If the capability is capable, but does not have a service URI associated, use the default
- // contact URI.
- if (result == null) {
- return isCapable(type) ? getContactUri() : null;
+ public @NonNull List<String> getOptionsFeatureTags() {
+ if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
+ return Collections.emptyList();
}
- return result;
+ return Collections.unmodifiableList(mFeatureTags);
+ }
+
+ /**
+ * @return The tuple elements associated with the presence element portion of the PIDF document
+ * contained in the NOTIFY response from the network.
+ * <p>
+ * Note: this is only populated if {@link #getCapabilityMechanism} is
+ * {@link CAPABILITY_MECHANISM_PRESENCE}
+ */
+ public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() {
+ if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableList(mPresenceTuples);
+ }
+
+ /**
+ * Get the RcsContactPresenceTuple associated with the given service id.
+ * @param serviceId The service id to get the presence tuple.
+ * @return The RcsContactPresenceTuple which has the given service id.
+ *
+ * <p>
+ * Note: this is only populated if {@link #getCapabilityMechanism} is
+ * {@link CAPABILITY_MECHANISM_PRESENCE}
+ */
+ public @Nullable RcsContactPresenceTuple getPresenceTuple(String serviceId) {
+ if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
+ return null;
+ }
+ for (RcsContactPresenceTuple tuple : mPresenceTuples) {
+ if (tuple.getServiceId().equals(serviceId)) {
+ return tuple;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the source of the data that was used to populate the capabilities of the requested
+ * contact.
+ */
+ public @SourceType int getSourceType() {
+ return mSourceType;
+ }
+
+ /**
+ * @return the result of querying the capabilities of the requested contact.
+ */
+ public @RequestResult int getRequestResult() {
+ return mRequestResult;
}
/**
diff --git a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
index c956cbc..c6966b3 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
@@ -21,6 +21,7 @@
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsServiceControllerListener;
+import android.telephony.ims.aidl.ISipTransport;
import android.telephony.ims.stub.ImsFeatureConfiguration;
import com.android.ims.internal.IImsFeatureStatusCallback;
@@ -34,6 +35,7 @@
IImsMmTelFeature createMmTelFeature(int slotId);
IImsRcsFeature createRcsFeature(int slotId);
ImsFeatureConfiguration querySupportedImsFeatures();
+ long getImsServiceCapabilities();
void addFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
void removeFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
// Synchronous call to ensure the ImsService is ready before continuing with feature creation.
@@ -41,6 +43,7 @@
void removeImsFeature(int slotId, int featureType);
IImsConfig getConfig(int slotId);
IImsRegistration getRegistration(int slotId);
+ ISipTransport getSipTransport(int slotId);
oneway void enableIms(int slotId);
oneway void disableIms(int slotId);
}
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
similarity index 73%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
index 71cd0a7..fe23343 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 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,6 +14,11 @@
* limitations under the License.
*/
-package android.media.tv;
+package android.telephony.ims.aidl;
-parcelable TvChannelInfo;
+/**
+ * Interface for commands to the SIP Transport implementation.
+ * {@hide}
+ */
+interface ISipTransport {
+}
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
new file mode 100644
index 0000000..17bd4b1
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -0,0 +1,57 @@
+/*
+ * 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.telephony.ims.stub;
+
+import android.annotation.NonNull;
+import android.telephony.ims.aidl.ISipTransport;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Manages the creation and destruction of SipDelegates in order to proxy SIP traffic to other
+ * IMS applications in order to support IMS single registration.
+ *
+ * @hide Until there is an implementation, keep this hidden
+ */
+public class SipTransportImplBase {
+
+ private final Executor mBinderExecutor;
+ private final ISipTransport mSipTransportImpl = new ISipTransport.Stub() {
+
+ };
+
+ /**
+ * Create an implementation of SipTransportImplBase.
+ *
+ * @param executor The executor that remote calls from the framework should be called on.
+ */
+ public SipTransportImplBase(@NonNull Executor executor) {
+ if (executor == null) {
+ throw new IllegalArgumentException("executor must not be null");
+ }
+
+ mBinderExecutor = executor;
+ }
+
+ /**
+ * @return The IInterface used by the framework.
+ * @hide
+ */
+ public ISipTransport getBinder() {
+ return mSipTransportImpl;
+ }
+}
diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.java b/telephony/java/com/android/ims/ImsFeatureContainer.java
index b259679..80c1d43 100644
--- a/telephony/java/com/android/ims/ImsFeatureContainer.java
+++ b/telephony/java/com/android/ims/ImsFeatureContainer.java
@@ -17,12 +17,14 @@
package com.android.ims;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.ims.ImsService;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.ISipTransport;
import android.telephony.ims.feature.ImsFeature;
import java.util.Objects;
@@ -49,6 +51,11 @@
public final IImsRegistration imsRegistration;
/**
+ * An optional interface containing the SIP transport implementation from the ImsService.
+ */
+ public final ISipTransport sipTransport;
+
+ /**
* State of the feature that is being tracked.
*/
private @ImsFeature.ImsState int mState = ImsFeature.STATE_UNAVAILABLE;
@@ -66,10 +73,11 @@
* @param initialCaps The initial capabilities that the ImsService supports.
*/
public ImsFeatureContainer(@NonNull IBinder iFace, @NonNull IImsConfig iConfig,
- @NonNull IImsRegistration iReg, long initialCaps) {
+ @NonNull IImsRegistration iReg, @Nullable ISipTransport transport, long initialCaps) {
imsFeature = iFace;
imsConfig = iConfig;
imsRegistration = iReg;
+ sipTransport = transport;
mCapabilities = initialCaps;
}
@@ -80,6 +88,7 @@
imsFeature = in.readStrongBinder();
imsConfig = IImsConfig.Stub.asInterface(in.readStrongBinder());
imsRegistration = IImsRegistration.Stub.asInterface(in.readStrongBinder());
+ sipTransport = ISipTransport.Stub.asInterface(in.readStrongBinder());
mState = in.readInt();
mCapabilities = in.readLong();
}
@@ -123,13 +132,15 @@
return imsFeature.equals(that.imsFeature) &&
imsConfig.equals(that.imsConfig) &&
imsRegistration.equals(that.imsRegistration) &&
+ sipTransport.equals(that.sipTransport) &&
mState == that.getState() &&
mCapabilities == that.getCapabilities();
}
@Override
public int hashCode() {
- return Objects.hash(imsFeature, imsConfig, imsRegistration, mState, mCapabilities);
+ return Objects.hash(imsFeature, imsConfig, imsRegistration, sipTransport, mState,
+ mCapabilities);
}
@Override
@@ -138,6 +149,7 @@
"imsFeature=" + imsFeature +
", imsConfig=" + imsConfig +
", imsRegistration=" + imsRegistration +
+ ", sipTransport=" + sipTransport +
", state=" + ImsFeature.STATE_LOG_MAP.get(mState) +
", capabilities = " + ImsService.getCapabilitiesString(mCapabilities) +
'}';
@@ -153,6 +165,7 @@
dest.writeStrongBinder(imsFeature);
dest.writeStrongInterface(imsConfig);
dest.writeStrongInterface(imsRegistration);
+ dest.writeStrongInterface(sipTransport);
dest.writeInt(mState);
dest.writeLong(mCapabilities);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 53069a1..0d8351d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2164,21 +2164,9 @@
*/
String getMmsUAProfUrl(int subId);
- /**
- * Set allowing mobile data during voice call.
- */
- boolean setDataAllowedDuringVoiceCall(int subId, boolean allow);
+ void setMobileDataPolicyEnabledStatus(int subscriptionId, int policy, boolean enabled);
- /**
- * Check whether data is allowed during voice call. Note this is for dual sim device that
- * data might be disabled on non-default data subscription but explicitly turned on by settings.
- */
- boolean isDataAllowedInVoiceCall(int subId);
-
- /**
- * Set whether a subscription always allows MMS connection.
- */
- boolean setAlwaysAllowMmsData(int subId, boolean allow);
+ boolean isMobileDataPolicyEnabled(int subscriptionId, int policy);
/**
* Command line command to enable or disable handling of CEP data for test purposes.
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index d430db5..3b9bec9 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -54,4 +54,18 @@
"launcher-aosp-tapl",
"platform-test-annotations",
],
+}
+
+java_library {
+ name: "wm-flicker-common-assertions",
+ platform_apis: true,
+ srcs: ["src/**/*Assertions.java", "src/**/*Assertions.kt"],
+ exclude_srcs: [
+ "**/helpers/*",
+ ],
+ static_libs: [
+ "flickerlib",
+ "truth-prebuilt",
+ "app-helpers-core"
+ ],
}
\ No newline at end of file
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 69b1187..8457039 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -21,13 +21,17 @@
import com.android.server.wm.flicker.dsl.WmAssertion
import com.android.server.wm.flicker.helpers.WindowUtils
+const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
+const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
+const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
+
@JvmOverloads
fun WmAssertion.statusBarWindowIsAlwaysVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
all("statusBarWindowIsAlwaysVisible", enabled, bugId) {
- this.showsAboveAppWindow(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
+ this.showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE)
}
}
@@ -37,7 +41,7 @@
enabled: Boolean = bugId == 0
) {
all("navBarWindowIsAlwaysVisible", enabled, bugId) {
- this.showsAboveAppWindow(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
+ this.showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE)
}
}
@@ -77,7 +81,7 @@
enabled: Boolean = bugId == 0
) {
all("navBarLayerIsAlwaysVisible", enabled, bugId) {
- this.showsLayer(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
+ this.showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
}
}
@@ -87,7 +91,7 @@
enabled: Boolean = bugId == 0
) {
all("statusBarLayerIsAlwaysVisible", enabled, bugId) {
- this.showsLayer(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
+ this.showsLayer(STATUS_BAR_WINDOW_TITLE)
}
}
@@ -102,15 +106,15 @@
val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
start("navBarLayerRotatesAndScales_StartingPos", enabled, bugId) {
- this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+ this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
}
end("navBarLayerRotatesAndScales_EndingPost", enabled, bugId) {
- this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, endingPos)
+ this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos)
}
if (startingPos == endingPos) {
all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) {
- this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+ this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
}
}
}
@@ -126,10 +130,10 @@
val endingPos = WindowUtils.getStatusBarPosition(endRotation)
start("statusBarLayerRotatesScales_StartingPos", enabled, bugId) {
- this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, startingPos)
+ this.hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos)
}
end("statusBarLayerRotatesScales_EndingPos", enabled, bugId) {
- this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, endingPos)
+ this.hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt
deleted file mode 100644
index abe7dbc..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt
+++ /dev/null
@@ -1,129 +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 com.android.server.wm.flicker
-
-import android.os.RemoteException
-import android.os.SystemClock
-import android.platform.helpers.IAppHelper
-import android.view.Surface
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.uiautomator.UiDevice
-
-/**
- * Base class of all Flicker test that performs common functions for all flicker tests:
- *
- *
- * - Caches transitions so that a transition is run once and the transition results are used by
- * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods
- * multiple times.
- * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed.
- * - Fails tests if results are not available for any test due to jank.
- */
-abstract class FlickerTestBase {
- val instrumentation by lazy {
- InstrumentationRegistry.getInstrumentation()
- }
- val uiDevice by lazy {
- UiDevice.getInstance(instrumentation)
- }
-
- /**
- * Build a test tag for the test
- * @param testName Name of the transition(s) being tested
- * @param app App being launcher
- * @param rotation Initial screen rotation
- *
- * @return test tag with pattern <NAME>__<APP>__<ROTATION>
- </ROTATION></APP></NAME> */
- protected fun buildTestTag(testName: String, app: IAppHelper, rotation: Int): String {
- return buildTestTag(
- testName, app, rotation, rotation, app2 = null, extraInfo = "")
- }
-
- /**
- * Build a test tag for the test
- * @param testName Name of the transition(s) being tested
- * @param app App being launcher
- * @param beginRotation Initial screen rotation
- * @param endRotation End screen rotation (if any, otherwise use same as initial)
- *
- * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
- </END_ROTATION></BEGIN_ROTATION></APP></NAME> */
- protected fun buildTestTag(
- testName: String,
- app: IAppHelper,
- beginRotation: Int,
- endRotation: Int
- ): String {
- return buildTestTag(
- testName, app, beginRotation, endRotation, app2 = null, extraInfo = "")
- }
-
- /**
- * Build a test tag for the test
- * @param testName Name of the transition(s) being tested
- * @param app App being launcher
- * @param app2 Second app being launched (if any)
- * @param beginRotation Initial screen rotation
- * @param endRotation End screen rotation (if any, otherwise use same as initial)
- * @param extraInfo Additional information to append to the tag
- *
- * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
- </EXTRA></NAME> */
- protected fun buildTestTag(
- testName: String,
- app: IAppHelper,
- beginRotation: Int,
- endRotation: Int,
- app2: IAppHelper?,
- extraInfo: String
- ): String {
- var testTag = "${testName}__${app.launcherName}"
- if (app2 != null) {
- testTag += "-${app2.launcherName}"
- }
- testTag += "__${Surface.rotationToString(beginRotation)}"
- if (endRotation != beginRotation) {
- testTag += "-${Surface.rotationToString(endRotation)}"
- }
- if (extraInfo.isNotEmpty()) {
- testTag += "__$extraInfo"
- }
- return testTag
- }
-
- protected fun Flicker.setRotation(rotation: Int) {
- try {
- when (rotation) {
- Surface.ROTATION_270 -> device.setOrientationLeft()
- Surface.ROTATION_90 -> device.setOrientationRight()
- Surface.ROTATION_0 -> device.setOrientationNatural()
- else -> device.setOrientationNatural()
- }
- // Wait for animation to complete
- SystemClock.sleep(1000)
- } catch (e: RemoteException) {
- throw RuntimeException(e)
- }
- }
-
- companion object {
- const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
- const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
- const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
- }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
deleted file mode 100644
index e7d1f8e..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
+++ /dev/null
@@ -1,36 +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 com.android.server.wm.flicker
-
-import android.view.Surface
-import org.junit.runners.Parameterized
-
-abstract class NonRotationTestBase(
- protected val rotationName: String,
- protected val rotation: Int
-) : FlickerTestBase() {
- companion object {
- const val SCREENSHOT_LAYER = "RotationLayer"
-
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
- }
- }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
deleted file mode 100644
index 3b67727..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
+++ /dev/null
@@ -1,49 +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 com.android.server.wm.flicker
-
-import android.view.Surface
-import org.junit.runners.Parameterized
-
-abstract class RotationTestBase(
- beginRotationName: String,
- endRotationName: String,
- protected val beginRotation: Int,
- protected val endRotation: Int
-) : FlickerTestBase() {
- companion object {
- @Parameterized.Parameters(name = "{0}-{1}")
- @JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
- val params: MutableCollection<Array<Any>> = mutableListOf()
- for (begin in supportedRotations) {
- for (end in supportedRotations) {
- if (begin != end) {
- params.add(arrayOf(
- Surface.rotationToString(begin),
- Surface.rotationToString(end),
- begin,
- end
- ))
- }
- }
- }
- return params
- }
- }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
new file mode 100644
index 0000000..742003a
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.os.Bundle
+import android.os.RemoteException
+import android.os.SystemClock
+import android.platform.helpers.IAppHelper
+import android.view.Surface
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.startRotation
+
+fun Flicker.setRotation(rotation: Int) {
+ try {
+ when (rotation) {
+ Surface.ROTATION_270 -> device.setOrientationLeft()
+ Surface.ROTATION_90 -> device.setOrientationRight()
+ Surface.ROTATION_0 -> device.setOrientationNatural()
+ else -> device.setOrientationNatural()
+ }
+ // Wait for animation to complete
+ SystemClock.sleep(1000)
+ } catch (e: RemoteException) {
+ throw RuntimeException(e)
+ }
+}
+
+/**
+ * Build a test tag for the test
+ * @param testName Name of the transition(s) being tested
+ * @param app App being launcher
+ * @param beginRotation Initial screen rotation
+ * @param endRotation End screen rotation (if any, otherwise use same as initial)
+ *
+ * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
+</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
+fun buildTestTag(
+ testName: String,
+ app: IAppHelper,
+ beginRotation: Int,
+ endRotation: Int
+): String {
+ return buildTestTag(
+ testName, app.launcherName, beginRotation, endRotation, app2 = null, extraInfo = "")
+}
+
+/**
+ * Build a test tag for the test
+ * @param testName Name of the transition(s) being tested
+ * @param app App being launcher
+ * @param rotation Screen rotation configuration for the test
+ *
+ * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
+</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
+fun buildTestTag(
+ testName: String,
+ app: IAppHelper?,
+ configuration: Bundle
+): String {
+ return buildTestTag(testName, app?.launcherName ?: "", configuration.startRotation,
+ configuration.endRotation, app2 = null, extraInfo = "")
+}
+
+/**
+ * Build a test tag for the test
+ * @param testName Name of the transition(s) being tested
+ * @param app App being launcher
+ * @param app2 Second app being launched (if any)
+ * @param beginRotation Initial screen rotation
+ * @param endRotation End screen rotation (if any, otherwise use same as initial)
+ * @param extraInfo Additional information to append to the tag
+ *
+ * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
+</EXTRA></NAME> */
+fun buildTestTag(
+ testName: String,
+ app: String,
+ beginRotation: Int,
+ endRotation: Int,
+ app2: String?,
+ extraInfo: String
+): String {
+ var testTag = "${testName}__$app"
+ if (app2 != null) {
+ testTag += "-$app2"
+ }
+ testTag += "__${Surface.rotationToString(beginRotation)}"
+ if (endRotation != beginRotation) {
+ testTag += "-${Surface.rotationToString(endRotation)}"
+ }
+ if (extraInfo.isNotEmpty()) {
+ testTag += "__$extraInfo"
+ }
+ return testTag
+}
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 404c789..a73264d 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
@@ -16,21 +16,27 @@
package com.android.server.wm.flicker.ime
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -39,56 +45,62 @@
* Test IME window closing back to app window transitions.
* To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class CloseImeAutoOpenWindowToAppTest(
- rotationName: String,
- rotation: Int
-) : CloseImeWindowToAppTest(rotationName, rotation) {
- override val testApp: ImeAppHelper
- get() = ImeAppAutoFocusHelper(instrumentation)
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
- @Test
- override fun test() {
- flicker(instrumentation) {
- withTag { buildTestTag("imeToAppAutoOpen", testApp, rotation) }
- repeat { 1 }
- setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(rotation)
- testApp.open()
- testApp.openIME(device)
- }
- }
- teardown {
- eachRun {
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- transitions {
- device.pressBack()
- device.waitForIdle()
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- imeAppWindowIsAlwaysVisible(testApp, bugId = 141458352)
- }
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = ImeAppAutoFocusHelper(instrumentation)
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(rotation)
- navBarLayerRotatesAndScales(rotation)
- statusBarLayerRotatesScales(rotation)
- imeLayerBecomesInvisible(bugId = 141458352)
- imeAppLayerIsAlwaysVisible(testApp, bugId = 141458352)
+ return FlickerTestRunnerFactory(instrumentation)
+ .buildTest { configuration ->
+ withTag { buildTestTag("imeToAppAutoOpen", testApp, configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ eachRun {
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(configuration.startRotation)
+ testApp.open()
+ testApp.openIME(device)
+ }
+ }
+ teardown {
+ eachRun {
+ testApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ transitions {
+ device.pressBack()
+ device.waitForIdle()
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ imeAppWindowIsAlwaysVisible(testApp, bugId = 141458352)
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation)
+ navBarLayerRotatesAndScales(configuration.startRotation)
+ statusBarLayerRotatesScales(configuration.startRotation)
+ imeLayerBecomesInvisible(bugId = 141458352)
+ imeAppLayerIsAlwaysVisible(testApp, bugId = 141458352)
+ }
+ }
}
- }
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index c1ba21a..7647802 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -19,19 +19,24 @@
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -45,53 +50,63 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class CloseImeAutoOpenWindowToHomeTest(
- rotationName: String,
- rotation: Int
-) : CloseImeWindowToHomeTest(rotationName, rotation) {
- override val testApp: ImeAppHelper
- get() = ImeAppAutoFocusHelper(instrumentation)
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
- @Test
- override fun test() {
- flicker(instrumentation) {
- withTag { buildTestTag("imeToHomeAutoOpen", testApp, rotation) }
- repeat { 1 }
- setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(rotation)
- testApp.open()
- testApp.openIME(device)
- }
- }
- teardown {
- eachRun {
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- transitions {
- device.pressHome()
- device.waitForIdle()
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- imeWindowBecomesInvisible(bugId = 141458352)
- imeAppWindowBecomesInvisible(testApp, bugId = 157449248)
- }
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = ImeAppAutoFocusHelper(instrumentation)
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
- navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0, bugId = 140855415)
- statusBarLayerRotatesScales(rotation, Surface.ROTATION_0)
- imeLayerBecomesInvisible(bugId = 141458352)
- imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+ return FlickerTestRunnerFactory(instrumentation)
+ .buildTest { configuration ->
+ withTestName {
+ buildTestTag("imeToHomeAutoOpen", testApp, configuration)
+ }
+ repeat { configuration.repetitions }
+ setup {
+ eachRun {
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(configuration.startRotation)
+ testApp.open()
+ testApp.openIME(device)
+ }
+ }
+ teardown {
+ eachRun {
+ testApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ transitions {
+ device.pressHome()
+ device.waitForIdle()
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ imeWindowBecomesInvisible(bugId = 141458352)
+ imeAppWindowBecomesInvisible(testApp, bugId = 157449248)
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
+ allStates = false)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0, bugId = 140855415)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ imeLayerBecomesInvisible(bugId = 141458352)
+ imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+ }
+ }
}
- }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 2c00722..136cf86 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -19,19 +19,24 @@
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -44,52 +49,57 @@
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeWindowToAppTest(
- rotationName: String,
- rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
- open val testApp = ImeAppHelper(instrumentation)
+class CloseImeWindowToAppTest(
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
- @Test
- open fun test() {
- flicker(instrumentation) {
- withTag { buildTestTag("imeToApp", testApp, rotation) }
- repeat { 1 }
- setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(rotation)
- testApp.open()
- testApp.openIME(device)
- }
- }
- teardown {
- eachRun {
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- transitions {
- device.pressBack()
- device.waitForIdle()
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- imeAppWindowIsAlwaysVisible(testApp)
- }
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = ImeAppHelper(instrumentation)
+ return FlickerTestRunnerFactory(instrumentation)
+ .buildTest { configuration ->
+ withTestName { buildTestTag("imeToApp", testApp, configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ eachRun {
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(configuration.startRotation)
+ testApp.open()
+ testApp.openIME(device)
+ }
+ }
+ teardown {
+ eachRun {
+ testApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ transitions {
+ device.pressBack()
+ device.waitForIdle()
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ imeAppWindowIsAlwaysVisible(testApp)
+ }
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(rotation)
- navBarLayerRotatesAndScales(rotation)
- statusBarLayerRotatesScales(rotation)
- imeLayerBecomesInvisible(enabled = false)
- imeAppLayerIsAlwaysVisible(testApp)
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation)
+ navBarLayerRotatesAndScales(configuration.startRotation)
+ statusBarLayerRotatesScales(configuration.startRotation)
+ imeLayerBecomesInvisible(enabled = false)
+ imeAppLayerIsAlwaysVisible(testApp)
+ }
+ }
}
- }
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 4697adc..6cfb282 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -19,21 +19,26 @@
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.openQuickstep
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -45,61 +50,69 @@
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeWindowToHomeTest(
- rotationName: String,
- rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
- open val testApp = ImeAppHelper(instrumentation)
+class CloseImeWindowToHomeTest(
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
- @Test
- open fun test() {
- flicker(instrumentation) {
- withTag { buildTestTag("imeToHome", testApp, rotation) }
- repeat { 1 }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(rotation)
- testApp.open()
- }
- eachRun {
- device.openQuickstep()
- device.reopenAppFromOverview()
- this.setRotation(rotation)
- testApp.openIME(device)
- }
- }
- transitions {
- device.pressHome()
- device.waitForIdle()
- }
- teardown {
- eachRun {
- device.pressHome()
- }
- test {
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- imeWindowBecomesInvisible()
- imeAppWindowBecomesInvisible(testApp)
- }
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = ImeAppHelper(instrumentation)
+ return FlickerTestRunnerFactory(instrumentation)
+ .buildTest { configuration ->
+ withTestName { buildTestTag("imeToHome", testApp, configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(configuration.startRotation)
+ testApp.open()
+ }
+ eachRun {
+ device.openQuickstep()
+ device.reopenAppFromOverview()
+ this.setRotation(configuration.startRotation)
+ testApp.openIME(device)
+ }
+ }
+ transitions {
+ device.pressHome()
+ device.waitForIdle()
+ }
+ teardown {
+ eachRun {
+ device.pressHome()
+ }
+ test {
+ testApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ imeWindowBecomesInvisible()
+ imeAppWindowBecomesInvisible(testApp)
+ }
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
- navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0, bugId = 140855415)
- statusBarLayerRotatesScales(rotation, Surface.ROTATION_0)
- imeLayerBecomesInvisible(bugId = 153739621)
- imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ noUncoveredRegions(configuration.startRotation,
+ Surface.ROTATION_0, allStates = false)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0, bugId = 140855415)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ imeLayerBecomesInvisible(bugId = 153739621)
+ imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+ }
+ }
}
- }
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ImeAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ImeAssertions.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 2caa8f3..5767a94 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -19,19 +19,24 @@
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -45,62 +50,65 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class OpenImeWindowTest(
- rotationName: String,
- rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
- @Test
- fun test() {
- val testApp = ImeAppHelper(instrumentation)
-
- flicker(instrumentation) {
- withTag { buildTestTag("openIme", testApp, rotation) }
- repeat { 1 }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(rotation)
- testApp.open()
- }
- }
- transitions {
- testApp.openIME(device)
- }
- teardown {
- eachRun {
- testApp.closeIME(device)
- }
- test {
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
-
- all("imeWindowBecomesVisible") {
- this.skipUntilFirstAssertion()
- .hidesNonAppWindow(IME_WINDOW_TITLE)
- .then()
- .showsNonAppWindow(IME_WINDOW_TITLE)
- }
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(rotation)
- navBarLayerRotatesAndScales(rotation)
- statusBarLayerRotatesScales(rotation)
-
- imeLayerBecomesVisible()
- }
- }
- }
- }
-
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
companion object {
private const val IME_WINDOW_TITLE = "InputMethod"
+
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = ImeAppHelper(instrumentation)
+
+ return FlickerTestRunnerFactory(instrumentation)
+ .buildTest { configuration ->
+ withTestName { buildTestTag("openIme", testApp, configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(configuration.startRotation)
+ testApp.open()
+ }
+ }
+ transitions {
+ testApp.openIME(device)
+ }
+ teardown {
+ eachRun {
+ testApp.closeIME(device)
+ }
+ test {
+ testApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+
+ all("imeWindowBecomesVisible") {
+ this.skipUntilFirstAssertion()
+ .hidesNonAppWindow(IME_WINDOW_TITLE)
+ .then()
+ .showsNonAppWindow(IME_WINDOW_TITLE)
+ }
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation)
+ navBarLayerRotatesAndScales(configuration.startRotation)
+ statusBarLayerRotatesScales(configuration.startRotation)
+
+ imeLayerBecomesVisible()
+ }
+ }
+ }
+ }
}
}
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
new file mode 100644
index 0000000..7e857f3
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import android.platform.helpers.IAppHelper
+import com.android.server.wm.flicker.dsl.LayersAssertion
+import com.android.server.wm.flicker.dsl.WmAssertion
+
+fun WmAssertion.wallpaperWindowBecomesInvisible(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("wallpaperWindowBecomesInvisible", enabled, bugId) {
+ this.showsBelowAppWindow("Wallpaper")
+ .then()
+ .hidesBelowAppWindow("Wallpaper")
+ }
+}
+
+fun WmAssertion.appWindowReplacesLauncherAsTopWindow(
+ testApp: IAppHelper,
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("appWindowReplacesLauncherAsTopWindow", enabled, bugId) {
+ this.showsAppWindowOnTop("Launcher")
+ .then()
+ .showsAppWindowOnTop("Snapshot", testApp.getPackage())
+ }
+}
+
+fun LayersAssertion.wallpaperLayerBecomesInvisible(
+ testApp: IAppHelper,
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("wallpaperLayerBecomesInvisible", enabled, bugId) {
+ this.showsLayer("Wallpaper")
+ .then()
+ .replaceVisibleLayer("Wallpaper", testApp.getPackage())
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 2c9c8ba..1081414 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -19,18 +19,26 @@
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -44,53 +52,64 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class OpenAppColdTest(
- rotationName: String,
- rotation: Int
-) : OpenAppTestBase(rotationName, rotation) {
- @Test
- fun test() {
- flicker(instrumentation) {
- withTag { buildTestTag("openAppCold", testApp, rotation) }
- repeat { 1 }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- this.setRotation(rotation)
- }
- }
- transitions {
- testApp.open()
- }
- teardown {
- eachRun {
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- appWindowReplacesLauncherAsTopWindow()
- wallpaperWindowBecomesInvisible()
- }
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = StandardAppHelper(instrumentation,
+ "com.android.server.wm.flicker.testapp", "SimpleApp")
+ return FlickerTestRunnerFactory(instrumentation)
+ .buildTest { configuration ->
+ withTag { buildTestTag("openAppCold", testApp, configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ }
+ eachRun {
+ this.setRotation(configuration.startRotation)
+ }
+ }
+ transitions {
+ testApp.open()
+ }
+ teardown {
+ eachRun {
+ testApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ appWindowReplacesLauncherAsTopWindow(testApp)
+ wallpaperWindowBecomesInvisible()
+ }
- layersTrace {
- // During testing the launcher is always in portrait mode
- noUncoveredRegions(Surface.ROTATION_0, rotation, bugId = 141361128)
- navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation)
- statusBarLayerRotatesScales(Surface.ROTATION_0, rotation)
- navBarLayerIsAlwaysVisible(enabled = rotation == Surface.ROTATION_0)
- statusBarLayerIsAlwaysVisible(enabled = false)
- wallpaperLayerBecomesInvisible()
- }
+ layersTrace {
+ // During testing the launcher is always in portrait mode
+ noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
+ bugId = 141361128)
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ navBarLayerIsAlwaysVisible(
+ enabled = configuration.endRotation == Surface.ROTATION_0)
+ statusBarLayerIsAlwaysVisible(enabled = false)
+ wallpaperLayerBecomesInvisible(testApp)
+ }
- eventLog {
- focusChanges("NexusLauncherActivity", testApp.`package`)
+ eventLog {
+ focusChanges("NexusLauncherActivity", testApp.`package`)
+ }
+ }
}
- }
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt
deleted file mode 100644
index 98e05d5..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt
+++ /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 com.android.server.wm.flicker.launch
-
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.LayersAssertion
-import com.android.server.wm.flicker.dsl.WmAssertion
-
-abstract class OpenAppTestBase(
- rotationName: String,
- rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
- protected val testApp = StandardAppHelper(instrumentation,
- "com.android.server.wm.flicker.testapp", "SimpleApp")
-
- protected fun WmAssertion.wallpaperWindowBecomesInvisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
- ) {
- all("wallpaperWindowBecomesInvisible", enabled, bugId) {
- this.showsBelowAppWindow("Wallpaper")
- .then()
- .hidesBelowAppWindow("Wallpaper")
- }
- }
-
- protected fun WmAssertion.appWindowReplacesLauncherAsTopWindow(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
- ) {
- all("appWindowReplacesLauncherAsTopWindow", enabled, bugId) {
- this.showsAppWindowOnTop("Launcher")
- .then()
- .showsAppWindowOnTop("Snapshot", testApp.getPackage())
- }
- }
-
- protected fun LayersAssertion.wallpaperLayerBecomesInvisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
- ) {
- all("appWindowReplacesLauncherAsTopWindow", enabled, bugId) {
- this.showsLayer("Wallpaper")
- .then()
- .replaceVisibleLayer("Wallpaper", testApp.getPackage())
- }
- }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index acd141a..2061994 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,21 +16,29 @@
package com.android.server.wm.flicker.launch
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -39,64 +47,73 @@
* Test warm launch app.
* To run this test: `atest FlickerTests:OpenAppWarmTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class OpenAppWarmTest(
- rotationName: String,
- rotation: Int
-) : OpenAppTestBase(rotationName, rotation) {
- @Test
- fun test() {
- val testApp = StandardAppHelper(instrumentation,
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = StandardAppHelper(instrumentation,
"com.android.server.wm.flicker.testapp", "SimpleApp")
+ return FlickerTestRunnerFactory(instrumentation)
+ .buildTest { configuration ->
+ withTag { buildTestTag("openAppWarm", testApp, configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ testApp.open()
+ }
+ eachRun {
+ device.pressHome()
+ this.setRotation(configuration.startRotation)
+ }
+ }
+ transitions {
+ testApp.open()
+ }
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
+ }
+ test {
+ testApp.exit()
+ }
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ appWindowReplacesLauncherAsTopWindow(testApp)
+ wallpaperWindowBecomesInvisible(enabled = false)
+ }
- flicker(instrumentation) {
- withTag { buildTestTag("openAppWarm", testApp, rotation) }
- repeat { 1 }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- testApp.open()
- }
- eachRun {
- device.pressHome()
- this.setRotation(rotation)
- }
- }
- transitions {
- testApp.open()
- }
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- testApp.exit()
- }
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- appWindowReplacesLauncherAsTopWindow()
- wallpaperWindowBecomesInvisible(enabled = false)
- }
+ layersTrace {
+ // During testing the launcher is always in portrait mode
+ noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
+ bugId = 141361128)
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ navBarLayerIsAlwaysVisible(
+ enabled = configuration.endRotation == Surface.ROTATION_0)
+ statusBarLayerIsAlwaysVisible(enabled = false)
+ wallpaperLayerBecomesInvisible(testApp)
+ }
- layersTrace {
- // During testing the launcher is always in portrait mode
- noUncoveredRegions(Surface.ROTATION_0, rotation, bugId = 141361128)
- navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation)
- statusBarLayerRotatesScales(Surface.ROTATION_0, rotation)
- navBarLayerIsAlwaysVisible(enabled = rotation == Surface.ROTATION_0)
- statusBarLayerIsAlwaysVisible(enabled = false)
- wallpaperLayerBecomesInvisible()
+ eventLog {
+ focusChanges("NexusLauncherActivity", testApp.`package`)
+ }
+ }
}
-
- eventLog {
- focusChanges("NexusLauncherActivity", testApp.`package`)
- }
- }
}
}
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/CommonAssertions.kt
similarity index 78%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to tests/FlickerTests/src/com/android/server/wm/flicker/pip/CommonAssertions.kt
index 71cd0a7..6bc9dcb 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/CommonAssertions.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 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,6 +14,6 @@
* limitations under the License.
*/
-package android.media.tv;
+package com.android.server.wm.flicker.pip
-parcelable TvChannelInfo;
+internal const val PIP_WINDOW_TITLE = "PipMenuActivity"
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt
index 9cfc033..89539fd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt
@@ -16,23 +16,31 @@
package com.android.server.wm.flicker.pip
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.closePipWindow
import com.android.server.wm.flicker.helpers.expandPipWindow
import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -41,81 +49,85 @@
* Test Pip launch.
* To run this test: `atest FlickerTests:PipToAppTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 152738416)
class EnterPipTest(
- rotationName: String,
- rotation: Int
-) : PipTestBase(rotationName, rotation) {
- @Test
- fun test() {
- flicker(instrumentation) {
- withTag { buildTestTag("enterPip", testApp, rotation) }
- repeat { 1 }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- device.pressHome()
- testApp.open()
- this.setRotation(rotation)
- }
- }
- teardown {
- eachRun {
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- testApp.exit()
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- }
- }
- transitions {
- testApp.clickEnterPipButton(device)
- device.expandPipWindow()
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- all("pipWindowBecomesVisible") {
- this.showsAppWindow(testApp.`package`)
- .then()
- .showsAppWindow(sPipWindowTitle)
- }
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
- navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0)
- statusBarLayerRotatesScales(rotation, Surface.ROTATION_0)
-
- all("pipLayerBecomesVisible") {
- this.showsLayer(testApp.launcherName)
- .then()
- .showsLayer(sPipWindowTitle)
- }
- }
- }
- }
- }
-
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ fun getParams(): List<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = PipAppHelper(instrumentation)
+ return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+ .buildTest { configuration ->
+ withTestName { buildTestTag("enterPip", testApp, configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ }
+ eachRun {
+ device.pressHome()
+ testApp.open()
+ this.setRotation(configuration.startRotation)
+ }
+ }
+ teardown {
+ eachRun {
+ if (device.hasPipWindow()) {
+ device.closePipWindow()
+ }
+ testApp.exit()
+ this.setRotation(Surface.ROTATION_0)
+ }
+ test {
+ if (device.hasPipWindow()) {
+ device.closePipWindow()
+ }
+ }
+ }
+ transitions {
+ testApp.clickEnterPipButton(device)
+ device.expandPipWindow()
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+
+ all("pipWindowBecomesVisible") {
+ this.showsAppWindow(testApp.`package`)
+ .then()
+ .showsAppWindow(PIP_WINDOW_TITLE)
+ }
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
+ enabled = false)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0, bugId = 140855415)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+
+ layersTrace {
+ all("pipLayerBecomesVisible") {
+ this.showsLayer(testApp.launcherName)
+ .then()
+ .showsLayer(PIP_WINDOW_TITLE)
+ }
+ }
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt
deleted file mode 100644
index 691db7fb..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt
+++ /dev/null
@@ -1,31 +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 com.android.server.wm.flicker.pip
-
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.helpers.PipAppHelper
-
-abstract class PipTestBase(
- rotationName: String,
- rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
- protected val testApp = PipAppHelper(instrumentation)
-
- companion object {
- const val sPipWindowTitle = "PipMenuActivity"
- }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
index deccc90..ac54a0a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
@@ -19,21 +19,28 @@
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.closePipWindow
import com.android.server.wm.flicker.helpers.expandPipWindow
import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -47,73 +54,82 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 152738416)
class PipToAppTest(
- rotationName: String,
- rotation: Int
-) : PipTestBase(rotationName, rotation) {
- @Test
- fun test() {
- flicker(instrumentation) {
- withTag { buildTestTag("exitPipModeToApp", testApp, rotation) }
- repeat { 1 }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- device.pressHome()
- testApp.open()
- }
- eachRun {
- this.setRotation(rotation)
- testApp.clickEnterPipButton(device)
- device.hasPipWindow()
- }
- }
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- if (device.hasPipWindow()) {
- device.closePipWindow()
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = PipAppHelper(instrumentation)
+ return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+ .buildTest { configuration ->
+ withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ device.pressHome()
+ testApp.open()
+ }
+ eachRun {
+ this.setRotation(configuration.startRotation)
+ testApp.clickEnterPipButton(device)
+ device.hasPipWindow()
+ }
}
- testApp.exit()
- }
- }
- transitions {
- device.expandPipWindow()
- device.waitForIdle()
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
+ }
+ test {
+ if (device.hasPipWindow()) {
+ device.closePipWindow()
+ }
+ testApp.exit()
+ }
+ }
+ transitions {
+ device.expandPipWindow()
+ device.waitForIdle()
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
- all("appReplacesPipWindow") {
- this.showsAppWindow(sPipWindowTitle)
- .then()
- .showsAppWindowOnTop(testApp.launcherName)
+ all("appReplacesPipWindow") {
+ this.showsAppWindow(PIP_WINDOW_TITLE)
+ .then()
+ .showsAppWindowOnTop(testApp.launcherName)
+ }
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
+ enabled = false)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0, bugId = 140855415)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+
+ all("appReplacesPipLayer") {
+ this.showsLayer(PIP_WINDOW_TITLE)
+ .then()
+ .showsLayer(testApp.launcherName)
+ }
+ }
+
+ eventLog {
+ focusChanges(
+ "NexusLauncherActivity", testApp.launcherName,
+ "NexusLauncherActivity", bugId = 151179149)
+ }
}
}
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(rotation)
- navBarLayerRotatesAndScales(rotation)
- statusBarLayerRotatesScales(rotation)
-
- all("appReplacesPipLayer") {
- this.showsLayer(sPipWindowTitle)
- .then()
- .showsLayer(testApp.launcherName)
- }
- }
-
- eventLog {
- focusChanges(
- "NexusLauncherActivity", testApp.launcherName, "NexusLauncherActivity",
- bugId = 151179149)
- }
- }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
index f40869c..f14a27d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
@@ -19,20 +19,27 @@
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.closePipWindow
import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -46,83 +53,83 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 152738416)
class PipToHomeTest(
- rotationName: String,
- rotation: Int
-) : PipTestBase(rotationName, rotation) {
- @Test
- fun test() {
- flicker(instrumentation) {
- withTag { buildTestTag("exitPipModeToApp", testApp, rotation) }
- repeat { 1 }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- device.pressHome()
- }
- eachRun {
- testApp.open()
- this.setRotation(rotation)
- testApp.clickEnterPipButton(device)
- device.hasPipWindow()
- }
- }
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- }
- test {
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- testApp.exit()
- }
- }
- transitions {
- testApp.closePipWindow(device)
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
-
- all("pipWindowBecomesInvisible") {
- this.showsAppWindow(sPipWindowTitle)
- .then()
- .hidesAppWindow(sPipWindowTitle)
- }
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- // The final state is the launcher, so always in portrait mode
- noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
- navBarLayerRotatesAndScales(rotation)
- statusBarLayerRotatesScales(rotation)
-
- all("pipLayerBecomesInvisible") {
- this.showsLayer(sPipWindowTitle)
- .then()
- .hidesLayer(sPipWindowTitle)
- }
- }
-
- eventLog {
- focusChanges(testApp.launcherName, "NexusLauncherActivity", bugId = 151179149)
- }
- }
- }
- }
-
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ fun getParams(): List<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = PipAppHelper(instrumentation)
+ return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+ .buildTest { configuration ->
+ withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ device.pressHome()
+ }
+ eachRun {
+ testApp.open()
+ this.setRotation(configuration.startRotation)
+ testApp.clickEnterPipButton(device)
+ device.hasPipWindow()
+ }
+ }
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
+ if (device.hasPipWindow()) {
+ device.closePipWindow()
+ }
+ }
+ test {
+ if (device.hasPipWindow()) {
+ device.closePipWindow()
+ }
+ testApp.exit()
+ }
+ }
+ transitions {
+ testApp.closePipWindow(device)
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+
+ all("pipWindowBecomesInvisible") {
+ this.showsAppWindow(PIP_WINDOW_TITLE)
+ .then()
+ .hidesAppWindow(PIP_WINDOW_TITLE)
+ }
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
+ enabled = false)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0, bugId = 140855415)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+
+ all("pipLayerBecomesInvisible") {
+ this.showsLayer(PIP_WINDOW_TITLE)
+ .then()
+ .hidesLayer(PIP_WINDOW_TITLE)
+ }
+ }
+
+ eventLog {
+ focusChanges(testApp.launcherName, "NexusLauncherActivity",
+ bugId = 151179149)
+ }
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 99218c2..24ca311 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -16,24 +16,30 @@
package com.android.server.wm.flicker.rotation
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import android.view.Surface
-import com.android.server.wm.flicker.NonRotationTestBase.Companion.SCREENSHOT_LAYER
-import com.android.server.wm.flicker.RotationTestBase
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.focusDoesNotChange
+import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -42,84 +48,95 @@
* Cycle through supported app rotations.
* To run this test: `atest FlickerTest:ChangeAppRotationTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class ChangeAppRotationTest(
- beginRotationName: String,
- endRotationName: String,
- beginRotation: Int,
- endRotation: Int
-) : RotationTestBase(beginRotationName, endRotationName, beginRotation, endRotation) {
- @Test
- fun test() {
- val testApp = StandardAppHelper(instrumentation,
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
+ companion object {
+ private const val SCREENSHOT_LAYER = "RotationLayer"
+
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = StandardAppHelper(instrumentation,
"com.android.server.wm.flicker.testapp", "SimpleApp")
-
- flicker(instrumentation) {
- withTag {
- buildTestTag("changeAppRotation", testApp, beginRotation, endRotation)
- }
- repeat { 1 }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- testApp.open()
- }
- eachRun {
- this.setRotation(beginRotation)
- }
- }
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- testApp.exit()
- }
- }
- transitions {
- this.setRotation(endRotation)
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- noUncoveredRegions(beginRotation, endRotation, allStates = false)
- navBarLayerRotatesAndScales(beginRotation, endRotation)
- statusBarLayerRotatesScales(beginRotation, endRotation)
- }
-
- layersTrace {
- val startingPos = WindowUtils.getDisplayBounds(beginRotation)
- val endingPos = WindowUtils.getDisplayBounds(endRotation)
-
- start("appLayerRotates_StartingPos") {
- this.hasVisibleRegion(testApp.getPackage(), startingPos)
+ return FlickerTestRunnerFactory(instrumentation)
+ .buildRotationTest { configuration ->
+ withTestName {
+ buildTestTag(
+ "changeAppRotation", testApp, configuration)
}
-
- end("appLayerRotates_EndingPos") {
- this.hasVisibleRegion(testApp.getPackage(), endingPos)
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ testApp.open()
+ }
+ eachRun {
+ this.setRotation(configuration.startRotation)
+ }
}
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
+ }
+ test {
+ testApp.exit()
+ }
+ }
+ transitions {
+ this.setRotation(configuration.endRotation)
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
- all("screenshotLayerBecomesInvisible") {
- this.showsLayer(testApp.getPackage())
- .then()
- .showsLayer(SCREENSHOT_LAYER)
- .then()
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ noUncoveredRegions(configuration.startRotation,
+ configuration.endRotation, allStates = false)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ configuration.endRotation)
+ }
+
+ layersTrace {
+ val startingPos = WindowUtils.getDisplayBounds(
+ configuration.startRotation)
+ val endingPos = WindowUtils.getDisplayBounds(
+ configuration.endRotation)
+
+ start("appLayerRotates_StartingPos") {
+ this.hasVisibleRegion(testApp.getPackage(), startingPos)
+ }
+
+ end("appLayerRotates_EndingPos") {
+ this.hasVisibleRegion(testApp.getPackage(), endingPos)
+ }
+
+ all("screenshotLayerBecomesInvisible") {
+ this.showsLayer(testApp.getPackage())
+ .then()
+ .showsLayer(SCREENSHOT_LAYER)
+ .then()
showsLayer(testApp.getPackage())
+ }
+ }
+
+ eventLog {
+ focusDoesNotChange(bugId = 151179149)
+ }
}
}
-
- eventLog {
- focusDoesNotChange(bugId = 151179149)
- }
- }
}
}
}
\ No newline at end of file
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 33a823d..b29fae3 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,29 +16,36 @@
package com.android.server.wm.flicker.rotation
+import android.content.ComponentName
import android.content.Intent
-import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
+import android.os.Bundle
+import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.RotationTestBase
-import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.focusDoesNotChange
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.stopPackage
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.testapp.ActivityOptions
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -47,150 +54,141 @@
* Cycle through supported app rotations using seamless rotations.
* To run this test: `atest FlickerTests:SeamlessAppRotationTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 147659548)
class SeamlessAppRotationTest(
- testId: String,
- private val intent: Intent,
- beginRotationName: String,
- endRotationName: String,
- beginRotation: Int,
- endRotation: Int
-) : RotationTestBase(beginRotationName, endRotationName, beginRotation, endRotation) {
- @Test
- fun test() {
- var intentId = ""
- if (intent.extras?.getBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD) == true) {
- intentId = "BUSY_UI_THREAD"
- }
-
- flicker(instrumentation) {
- withTag {
- "changeAppRotation_" + intentId + "_" +
- Surface.rotationToString(beginRotation) + "_" +
- Surface.rotationToString(endRotation)
- }
- repeat { 1 }
- setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
- instrumentation.targetContext.startActivity(intent)
- device.wait(Until.hasObject(By.pkg(intent.component?.packageName)
- .depth(0)), APP_LAUNCH_TIMEOUT)
- this.setRotation(beginRotation)
- }
- }
- teardown {
- eachRun {
- stopPackage(
- instrumentation.targetContext,
- intent.component?.packageName
- ?: error("Unable to determine package name for intent"))
- this.setRotation(Surface.ROTATION_0)
- }
- }
- transitions {
- this.setRotation(endRotation)
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible(bugId = 140855415)
- statusBarWindowIsAlwaysVisible(bugId = 140855415)
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- noUncoveredRegions(beginRotation, endRotation, allStates = true)
- navBarLayerRotatesAndScales(beginRotation, endRotation)
- statusBarLayerRotatesScales(beginRotation, endRotation, enabled = false)
- }
-
- layersTrace {
- all("appLayerRotates"/*, bugId = 147659548*/) {
- val startingPos = WindowUtils.getDisplayBounds(beginRotation)
- val endingPos = WindowUtils.getDisplayBounds(endRotation)
-
- if (startingPos == endingPos) {
- this.hasVisibleRegion(
- intent.component?.packageName ?: "",
- startingPos)
- } else {
- this.hasVisibleRegion(intent.component?.packageName ?: "", startingPos)
- .then()
- .hasVisibleRegion(intent.component?.packageName
- ?: "", endingPos)
- }
- }
-
- all("noUncoveredRegions"/*, bugId = 147659548*/) {
- val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
- val endingBounds = WindowUtils.getDisplayBounds(endRotation)
- if (startingBounds == endingBounds) {
- this.coversAtLeastRegion(startingBounds)
- } else {
- this.coversAtLeastRegion(startingBounds)
- .then()
- .coversAtLeastRegion(endingBounds)
- }
- }
- }
-
- eventLog {
- focusDoesNotChange(bugId = 151179149)
- }
- }
- }
- }
-
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
companion object {
private const val APP_LAUNCH_TIMEOUT: Long = 10000
- // launch test activity that supports seamless rotation with a busy UI thread to miss frames
- // when the app is asked to redraw
+ private val Bundle.intent: Intent?
+ get() = this.getParcelable(Intent::class.java.simpleName)
+
+ private val Bundle.intentPackageName: String
+ get() = this.intent?.component?.packageName ?: ""
+
+ private val Bundle.intentId get() = if (this.intent?.getBooleanExtra(
+ ActivityOptions.EXTRA_STARVE_UI_THREAD, false) == true) {
+ "BUSY_UI_THREAD"
+ } else {
+ ""
+ }
+
+ private fun Bundle.createConfig(starveUiThread: Boolean): Bundle {
+ val config = this.deepCopy()
+ val intent = Intent()
+ intent.addCategory(Intent.CATEGORY_LAUNCHER)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ intent.component = ComponentName("com.android.server.wm.flicker.testapp",
+ "com.android.server.wm.flicker.testapp.SeamlessRotationActivity")
+
+ intent.putExtra(ActivityOptions.EXTRA_STARVE_UI_THREAD, starveUiThread)
+
+ config.putParcelable(Intent::class.java.simpleName, intent)
+ return config
+ }
+
+ @JvmStatic
+ private fun FlickerTestRunnerFactory.getConfigurations(): List<Bundle> {
+ return this.getConfigRotationTests().flatMap {
+ val defaultRun = it.createConfig(starveUiThread = false)
+ val busyUiRun = it.createConfig(starveUiThread = true)
+ listOf(defaultRun, busyUiRun)
+ }
+ }
+
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
- val params = mutableListOf<Array<Any>>()
- val testIntents = mutableListOf<Intent>()
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val factory = FlickerTestRunnerFactory(instrumentation)
+ val configurations = factory.getConfigurations()
+ return factory.buildRotationTest(configurations) { configuration ->
+ withTestName {
+ buildTestTag("seamlessRotation_" + configuration.intentId,
+ app = null, configuration = configuration)
+ }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ instrumentation.targetContext.startActivity(configuration.intent)
+ val searchQuery = By.pkg(configuration.intent?.component?.packageName)
+ .depth(0)
+ device.wait(Until.hasObject(searchQuery), APP_LAUNCH_TIMEOUT)
+ }
+ eachRun {
+ this.setRotation(configuration.startRotation)
+ }
+ }
+ teardown {
+ test {
+ this.setRotation(Surface.ROTATION_0)
+ stopPackage(
+ instrumentation.targetContext,
+ configuration.intent?.component?.packageName
+ ?: error("Unable to determine package name for intent"))
+ }
+ }
+ transitions {
+ this.setRotation(configuration.endRotation)
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible(bugId = 140855415)
+ statusBarWindowIsAlwaysVisible(bugId = 140855415)
+ }
- // launch test activity that supports seamless rotation
- var intent = Intent(Intent.ACTION_MAIN)
- intent.component = ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME
- intent.flags = FLAG_ACTIVITY_NEW_TASK
- testIntents.add(intent)
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ noUncoveredRegions(configuration.startRotation,
+ configuration.endRotation, allStates = false, bugId = 147659548)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ configuration.endRotation, enabled = false)
+ }
- // launch test activity that supports seamless rotation with a busy UI thread to miss frames
- // when the app is asked to redraw
- intent = Intent(intent)
- intent.putExtra(ActivityOptions.EXTRA_STARVE_UI_THREAD, true)
- intent.flags = FLAG_ACTIVITY_NEW_TASK
- testIntents.add(intent)
- for (testIntent in testIntents) {
- for (begin in supportedRotations) {
- for (end in supportedRotations) {
- if (begin != end) {
- var testId: String = Surface.rotationToString(begin) +
- "_" + Surface.rotationToString(end)
- if (testIntent.extras?.getBoolean(
- ActivityOptions.EXTRA_STARVE_UI_THREAD) == true) {
- testId += "_" + "BUSY_UI_THREAD"
+ layersTrace {
+ val startingBounds = WindowUtils
+ .getDisplayBounds(configuration.startRotation)
+ val endingBounds = WindowUtils
+ .getDisplayBounds(configuration.endRotation)
+
+ all("appLayerRotates", bugId = 147659548) {
+ if (startingBounds == endingBounds) {
+ this.hasVisibleRegion(
+ configuration.intentPackageName, startingBounds)
+ } else {
+ this.hasVisibleRegion(configuration.intentPackageName,
+ startingBounds)
+ .then()
+ .hasVisibleRegion(configuration.intentPackageName,
+ endingBounds)
}
- params.add(arrayOf(
- testId,
- testIntent,
- Surface.rotationToString(begin),
- Surface.rotationToString(end),
- begin,
- end))
}
+
+ all("noUncoveredRegions"/*, bugId = 147659548*/) {
+ if (startingBounds == endingBounds) {
+ this.coversAtLeastRegion(startingBounds)
+ } else {
+ this.coversAtLeastRegion(startingBounds)
+ .then()
+ .coversAtLeastRegion(endingBounds)
+ }
+ }
+ }
+
+ eventLog {
+ focusDoesNotChange(bugId = 151179149)
}
}
}
- return params
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
index 3b5e669..ae9fcf9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
@@ -16,25 +16,32 @@
package com.android.server.wm.flicker.splitscreen
-import android.view.Surface
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.exitSplitScreen
import com.android.server.wm.flicker.helpers.isInSplitScreen
import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -43,78 +50,79 @@
* Test open app to split screen.
* To run this test: `atest FlickerTests:OpenAppToSplitScreenTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 161435597)
class OpenAppToSplitScreenTest(
- rotationName: String,
- rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
- @Test
- fun test() {
- val testApp = StandardAppHelper(instrumentation,
- "com.android.server.wm.flicker.testapp", "SimpleApp")
-
- flicker(instrumentation) {
- withTag { buildTestTag("appToSplitScreen", testApp, rotation) }
- repeat { 1 }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- testApp.open()
- this.setRotation(rotation)
- }
- }
- teardown {
- eachRun {
- if (device.isInSplitScreen()) {
- device.exitSplitScreen()
- }
- }
- test {
- testApp.exit()
- }
- }
- transitions {
- device.launchSplitScreen()
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(rotation, enabled = false)
- navBarLayerRotatesAndScales(rotation, bugId = 140855415)
- statusBarLayerRotatesScales(rotation)
-
- all("dividerLayerBecomesVisible") {
- this.hidesLayer(DOCKED_STACK_DIVIDER)
- .then()
- .showsLayer(DOCKED_STACK_DIVIDER)
- }
- }
-
- eventLog {
- focusChanges(testApp.`package`,
- "recents_animation_input_consumer", "NexusLauncherActivity",
- bugId = 151179149)
- }
- }
- }
- }
-
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = StandardAppHelper(instrumentation,
+ "com.android.server.wm.flicker.testapp", "SimpleApp")
+
+ return FlickerTestRunnerFactory(instrumentation)
+ .buildTest { configuration ->
+ withTestName {
+ buildTestTag("appToSplitScreen", testApp, configuration)
+ }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ }
+ eachRun {
+ testApp.open()
+ this.setRotation(configuration.endRotation)
+ }
+ }
+ teardown {
+ eachRun {
+ if (device.isInSplitScreen()) {
+ device.exitSplitScreen()
+ }
+ }
+ test {
+ testApp.exit()
+ }
+ }
+ transitions {
+ device.launchSplitScreen()
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.endRotation, enabled = false)
+ navBarLayerRotatesAndScales(configuration.endRotation,
+ bugId = 140855415)
+ statusBarLayerRotatesScales(configuration.endRotation)
+
+ all("dividerLayerBecomesVisible") {
+ this.hidesLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .showsLayer(DOCKED_STACK_DIVIDER)
+ }
+ }
+
+ eventLog {
+ focusChanges(testApp.`package`,
+ "recents_animation_input_consumer", "NexusLauncherActivity",
+ bugId = 151179149)
+ }
+ }
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
index abf41a1..4b9f024 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
@@ -19,32 +19,39 @@
import android.graphics.Region
import android.util.Rational
import android.view.Surface
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
-import com.android.server.wm.flicker.FlickerTestBase
+import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
import com.android.server.wm.flicker.focusDoesNotChange
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.exitSplitScreen
import com.android.server.wm.flicker.helpers.isInSplitScreen
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.resizeSplitScreen
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
/**
* Test split screen resizing window transitions.
@@ -53,138 +60,149 @@
* Currently it runs only in 0 degrees because of b/156100803
*/
@RequiresDevice
-@RunWith(AndroidJUnit4::class)
+@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 159096424)
-class ResizeSplitScreenTest : FlickerTestBase() {
- @Test
- fun test() {
- val testAppTop = StandardAppHelper(instrumentation,
- "com.android.server.wm.flicker.testapp", "SimpleApp")
- val testAppBottom = ImeAppHelper(instrumentation)
-
- flicker(instrumentation) {
- withTag {
- val description = (startRatio.toString().replace("/", "-") + "_to_" +
- stopRatio.toString().replace("/", "-"))
- buildTestTag("resizeSplitScreen", testAppTop, rotation,
- rotation, testAppBottom, description)
- }
- repeat { 1 }
- setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(rotation)
- this.launcherStrategy.clearRecentAppsFromOverview()
- testAppBottom.open()
- device.pressHome()
- testAppTop.open()
- device.waitForIdle()
- device.launchSplitScreen()
- val snapshot = device.findObject(By.res(device.launcherPackageName, "snapshot"))
- snapshot.click()
- testAppBottom.openIME(device)
- device.pressBack()
- device.resizeSplitScreen(startRatio)
- }
- }
- teardown {
- eachRun {
- device.exitSplitScreen()
- device.pressHome()
- testAppTop.exit()
- testAppBottom.exit()
- }
- test {
- if (device.isInSplitScreen()) {
- device.exitSplitScreen()
- }
- }
- }
- transitions {
- device.resizeSplitScreen(stopRatio)
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
-
- all("topAppWindowIsAlwaysVisible", bugId = 156223549) {
- this.showsAppWindow(sSimpleActivity)
- }
-
- all("bottomAppWindowIsAlwaysVisible", bugId = 156223549) {
- this.showsAppWindow(sImeActivity)
- }
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(rotation)
- navBarLayerRotatesAndScales(rotation)
- statusBarLayerRotatesScales(rotation)
-
- all("topAppLayerIsAlwaysVisible") {
- this.showsLayer(sSimpleActivity)
- }
-
- all("bottomAppLayerIsAlwaysVisible") {
- this.showsLayer(sImeActivity)
- }
-
- all("dividerLayerIsAlwaysVisible") {
- this.showsLayer(DOCKED_STACK_DIVIDER)
- }
-
- start("appsStartingBounds", enabled = false) {
- val displayBounds = WindowUtils.displayBounds
- val entry = this.trace.entries.firstOrNull()
- ?: throw IllegalStateException("Trace is empty")
- val dividerBounds = entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
-
- val topAppBounds = Region(0, 0, dividerBounds.right,
- dividerBounds.top + WindowUtils.dockedStackDividerInset)
- val bottomAppBounds = Region(0,
- dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
- displayBounds.right,
- displayBounds.bottom - WindowUtils.navigationBarHeight)
- this.hasVisibleRegion("SimpleActivity", topAppBounds)
- .and()
- .hasVisibleRegion("ImeActivity", bottomAppBounds)
- }
-
- end("appsEndingBounds", enabled = false) {
- val displayBounds = WindowUtils.displayBounds
- val entry = this.trace.entries.lastOrNull()
- ?: throw IllegalStateException("Trace is empty")
- val dividerBounds = entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
-
- val topAppBounds = Region(0, 0, dividerBounds.right,
- dividerBounds.top + WindowUtils.dockedStackDividerInset)
- val bottomAppBounds = Region(0,
- dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
- displayBounds.right,
- displayBounds.bottom - WindowUtils.navigationBarHeight)
-
- this.hasVisibleRegion(sSimpleActivity, topAppBounds)
- .and()
- .hasVisibleRegion(sImeActivity, bottomAppBounds)
- }
- }
-
- eventLog {
- focusDoesNotChange()
- }
- }
- }
- }
-
+class ResizeSplitScreenTest(
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
companion object {
private const val sSimpleActivity = "SimpleActivity"
private const val sImeActivity = "ImeActivity"
- private val rotation = Surface.ROTATION_0
private val startRatio = Rational(1, 3)
private val stopRatio = Rational(2, 3)
+
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testAppTop = StandardAppHelper(instrumentation,
+ "com.android.server.wm.flicker.testapp", "SimpleApp")
+ val testAppBottom = ImeAppHelper(instrumentation)
+
+ return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+ .buildTest { configuration ->
+ withTestName {
+ val description = (startRatio.toString().replace("/", "-") + "_to_" +
+ stopRatio.toString().replace("/", "-"))
+ buildTestTag("resizeSplitScreen", testAppTop.launcherName,
+ configuration.startRotation, configuration.endRotation,
+ testAppBottom.launcherName, description)
+ }
+ repeat { configuration.repetitions }
+ setup {
+ eachRun {
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(configuration.startRotation)
+ this.launcherStrategy.clearRecentAppsFromOverview()
+ testAppBottom.open()
+ device.pressHome()
+ testAppTop.open()
+ device.waitForIdle()
+ device.launchSplitScreen()
+ val snapshot =
+ device.findObject(By.res(device.launcherPackageName, "snapshot"))
+ snapshot.click()
+ testAppBottom.openIME(device)
+ device.pressBack()
+ device.resizeSplitScreen(startRatio)
+ }
+ }
+ teardown {
+ eachRun {
+ if (device.isInSplitScreen()) {
+ device.exitSplitScreen()
+ }
+ device.pressHome()
+ testAppTop.exit()
+ testAppBottom.exit()
+ }
+ test {
+ if (device.isInSplitScreen()) {
+ device.exitSplitScreen()
+ }
+ }
+ }
+ transitions {
+ device.resizeSplitScreen(stopRatio)
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+
+ all("topAppWindowIsAlwaysVisible", bugId = 156223549) {
+ this.showsAppWindow(sSimpleActivity)
+ }
+
+ all("bottomAppWindowIsAlwaysVisible", bugId = 156223549) {
+ this.showsAppWindow(sImeActivity)
+ }
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.endRotation)
+ navBarLayerRotatesAndScales(configuration.endRotation)
+ statusBarLayerRotatesScales(configuration.endRotation)
+
+ all("topAppLayerIsAlwaysVisible") {
+ this.showsLayer(sSimpleActivity)
+ }
+
+ all("bottomAppLayerIsAlwaysVisible") {
+ this.showsLayer(sImeActivity)
+ }
+
+ all("dividerLayerIsAlwaysVisible") {
+ this.showsLayer(DOCKED_STACK_DIVIDER)
+ }
+
+ start("appsStartingBounds", enabled = false) {
+ val displayBounds = WindowUtils.displayBounds
+ val entry = this.trace.entries.firstOrNull()
+ ?: throw IllegalStateException("Trace is empty")
+ val dividerBounds =
+ entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+
+ val topAppBounds = Region(0, 0, dividerBounds.right,
+ dividerBounds.top + WindowUtils.dockedStackDividerInset)
+ val bottomAppBounds = Region(0,
+ dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
+ displayBounds.right,
+ displayBounds.bottom - WindowUtils.navigationBarHeight)
+ this.hasVisibleRegion("SimpleActivity", topAppBounds)
+ .and()
+ .hasVisibleRegion("ImeActivity", bottomAppBounds)
+ }
+
+ end("appsEndingBounds", enabled = false) {
+ val displayBounds = WindowUtils.displayBounds
+ val entry = this.trace.entries.lastOrNull()
+ ?: throw IllegalStateException("Trace is empty")
+ val dividerBounds =
+ entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+
+ val topAppBounds = Region(0, 0, dividerBounds.right,
+ dividerBounds.top + WindowUtils.dockedStackDividerInset)
+ val bottomAppBounds = Region(0,
+ dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
+ displayBounds.right,
+ displayBounds.bottom - WindowUtils.navigationBarHeight)
+
+ this.hasVisibleRegion(sSimpleActivity, topAppBounds)
+ .and()
+ .hasVisibleRegion(sImeActivity, bottomAppBounds)
+ }
+ }
+
+ eventLog {
+ focusDoesNotChange()
+ }
+ }
+ }
+ }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
index 7447bda..f966a66 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
@@ -19,23 +19,29 @@
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.focusDoesNotChange
+import com.android.server.wm.flicker.helpers.buildTestTag
import com.android.server.wm.flicker.helpers.exitSplitScreen
import com.android.server.wm.flicker.helpers.isInSplitScreen
import com.android.server.wm.flicker.helpers.launchSplitScreen
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -49,82 +55,80 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class SplitScreenToLauncherTest(
- rotationName: String,
- rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
- @Test
- fun test() {
- val testApp = StandardAppHelper(instrumentation,
- "com.android.server.wm.flicker.testapp", "SimpleApp")
-
- flicker(instrumentation) {
- withTag { buildTestTag("splitScreenToLauncher", testApp, rotation) }
- repeat { 1 }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- testApp.open()
- this.setRotation(rotation)
- device.launchSplitScreen()
- device.waitForIdle()
- }
- }
- teardown {
- eachRun {
- testApp.exit()
- }
- test {
- if (device.isInSplitScreen()) {
- device.exitSplitScreen()
- }
- }
- }
- transitions {
- device.exitSplitScreen()
- }
- assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(rotation)
- navBarLayerRotatesAndScales(rotation)
- statusBarLayerRotatesScales(rotation)
-
- // b/161435597 causes the test not to work on 90 degrees
- all("dividerLayerBecomesInvisible") {
- this.showsLayer(DOCKED_STACK_DIVIDER)
- .then()
- .hidesLayer(DOCKED_STACK_DIVIDER)
- }
-
- all("appLayerBecomesInvisible") {
- this.showsLayer(testApp.getPackage())
- .then()
- .hidesLayer(testApp.getPackage())
- }
- }
-
- eventLog {
- focusDoesNotChange(bugId = 151179149)
- }
- }
- }
- }
-
+ testName: String,
+ flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<Array<Any>> {
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val testApp = StandardAppHelper(instrumentation,
+ "com.android.server.wm.flicker.testapp", "SimpleApp")
+
// b/161435597 causes the test not to work on 90 degrees
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+ .buildTest { configuration ->
+ withTestName {
+ buildTestTag("splitScreenToLauncher", testApp, configuration)
+ }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ }
+ eachRun {
+ testApp.open()
+ this.setRotation(configuration.endRotation)
+ device.launchSplitScreen()
+ device.waitForIdle()
+ }
+ }
+ teardown {
+ eachRun {
+ testApp.exit()
+ }
+ test {
+ if (device.isInSplitScreen()) {
+ device.exitSplitScreen()
+ }
+ }
+ }
+ transitions {
+ device.exitSplitScreen()
+ }
+ assertions {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.endRotation)
+ navBarLayerRotatesAndScales(configuration.endRotation)
+ statusBarLayerRotatesScales(configuration.endRotation)
+
+ // b/161435597 causes the test not to work on 90 degrees
+ all("dividerLayerBecomesInvisible") {
+ this.showsLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .hidesLayer(DOCKED_STACK_DIVIDER)
+ }
+
+ all("appLayerBecomesInvisible") {
+ this.showsLayer(testApp.getPackage())
+ .then()
+ .hidesLayer(testApp.getPackage())
+ }
+ }
+
+ eventLog {
+ focusDoesNotChange(bugId = 151179149)
+ }
+ }
+ }
}
}
}
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 226d2d5..bf7a6ff 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
@@ -18,6 +18,8 @@
import static com.android.tests.rollback.host.WatchdogEventLogger.watchdogEventOccurred;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -25,6 +27,7 @@
import static org.junit.Assume.assumeTrue;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.Log;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.IFileEntry;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -51,6 +54,7 @@
*/
@RunWith(DeviceJUnit4ClassRunner.class)
public class StagedRollbackTest extends BaseHostJUnit4Test {
+ private static final String TAG = "StagedRollbackTest";
private static final int NATIVE_CRASHES_THRESHOLD = 5;
/**
@@ -124,12 +128,14 @@
}
if (found) {
- if (!getDevice().isAdbRoot()) {
+ try {
getDevice().enableAdbRoot();
- }
- getDevice().remountSystemWritable();
- for (String file : files) {
- getDevice().executeShellCommand("rm -rf " + file);
+ getDevice().remountSystemWritable();
+ for (String file : files) {
+ getDevice().executeShellCommand("rm -rf " + file);
+ }
+ } finally {
+ getDevice().disableAdbRoot();
}
getDevice().reboot();
}
@@ -272,6 +278,8 @@
List<String> after = getSnapshotDirectories("/data/misc_ce/0/rollback");
// Only check directories newly created during the test
after.removeAll(before);
+ // There should be only one /data/misc_ce/0/rollback/<rollbackId> created during test
+ assertThat(after).hasSize(1);
after.forEach(dir -> assertDirectoryIsEmpty(dir));
}
@@ -328,8 +336,8 @@
String oldFilePath1 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "/" + TEST_FILENAME_1;
String oldFilePath2 =
apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + TEST_SUBDIR + TEST_FILENAME_2;
- assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
- assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+ pushString(TEST_STRING_1, oldFilePath1);
+ pushString(TEST_STRING_2, oldFilePath2);
// Install new version of the APEX with rollback enabled
runPhase("testRollbackApexDataDirectories_Phase1");
@@ -341,8 +349,8 @@
String newFilePath3 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "/" + TEST_FILENAME_3;
String newFilePath4 =
apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + TEST_SUBDIR + TEST_FILENAME_4;
- assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3));
- assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4));
+ pushString(TEST_STRING_3, newFilePath3);
+ pushString(TEST_STRING_4, newFilePath4);
// Roll back the APEX
runPhase("testRollbackApexDataDirectories_Phase2");
@@ -358,6 +366,8 @@
List<String> after = getSnapshotDirectories("/data/misc/apexrollback");
// Only check directories newly created during the test
after.removeAll(before);
+ // There should be only one /data/misc/apexrollback/<rollbackId> created during test
+ assertThat(after).hasSize(1);
after.forEach(dir -> assertDirectoryIsEmpty(dir));
}
@@ -374,8 +384,8 @@
APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
String oldFilePath2 =
apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
- assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
- assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+ pushString(TEST_STRING_1, oldFilePath1);
+ pushString(TEST_STRING_2, oldFilePath2);
// Install new version of the APEX with rollback enabled
runPhase("testRollbackApexDataDirectories_Phase1");
@@ -388,8 +398,8 @@
apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_3;
String newFilePath4 =
apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4;
- assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3));
- assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4));
+ pushString(TEST_STRING_3, newFilePath3);
+ pushString(TEST_STRING_4, newFilePath4);
// Roll back the APEX
runPhase("testRollbackApexDataDirectories_Phase2");
@@ -405,6 +415,8 @@
List<String> after = getSnapshotDirectories("/data/misc_de/0/apexrollback");
// Only check directories newly created during the test
after.removeAll(before);
+ // There should be only one /data/misc_de/0/apexrollback/<rollbackId> created during test
+ assertThat(after).hasSize(1);
after.forEach(dir -> assertDirectoryIsEmpty(dir));
}
@@ -420,8 +432,8 @@
String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
String oldFilePath2 =
apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
- assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
- assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+ pushString(TEST_STRING_1, oldFilePath1);
+ pushString(TEST_STRING_2, oldFilePath2);
// Install new version of the APEX with rollback enabled
runPhase("testRollbackApexDataDirectories_Phase1");
@@ -433,8 +445,8 @@
String newFilePath3 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_3;
String newFilePath4 =
apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4;
- assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3));
- assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4));
+ pushString(TEST_STRING_3, newFilePath3);
+ pushString(TEST_STRING_4, newFilePath4);
// Roll back the APEX
runPhase("testRollbackApexDataDirectories_Phase2");
@@ -450,6 +462,8 @@
List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
// Only check directories newly created during the test
after.removeAll(before);
+ // There should be only one /data/misc_ce/0/apexrollback/<rollbackId> created during test
+ assertThat(after).hasSize(1);
after.forEach(dir -> assertDirectoryIsEmpty(dir));
}
@@ -464,8 +478,8 @@
// Push files to apk data directory
String oldFilePath1 = apkDataDirDe(TESTAPP_A, 0) + "/" + TEST_FILENAME_1;
String oldFilePath2 = apkDataDirDe(TESTAPP_A, 0) + TEST_SUBDIR + TEST_FILENAME_2;
- assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
- assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+ pushString(TEST_STRING_1, oldFilePath1);
+ pushString(TEST_STRING_2, oldFilePath2);
// Install version 2 of TESTAPP_A with rollback enabled
runPhase("testRollbackApkDataDirectories_Phase2");
@@ -476,8 +490,8 @@
getDevice().deleteFile(oldFilePath2);
String newFilePath3 = apkDataDirDe(TESTAPP_A, 0) + "/" + TEST_FILENAME_3;
String newFilePath4 = apkDataDirDe(TESTAPP_A, 0) + TEST_SUBDIR + TEST_FILENAME_4;
- assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3));
- assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4));
+ pushString(TEST_STRING_3, newFilePath3);
+ pushString(TEST_STRING_4, newFilePath4);
// Roll back the APK
runPhase("testRollbackApkDataDirectories_Phase3");
@@ -499,8 +513,8 @@
String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
String oldFilePath2 =
apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
- assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
- assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+ pushString(TEST_STRING_1, oldFilePath1);
+ pushString(TEST_STRING_2, oldFilePath2);
// Install new version of the APEX with rollback enabled
runPhase("testRollbackApexDataDirectories_Phase1");
@@ -509,6 +523,8 @@
List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
// Only check directories newly created during the test
after.removeAll(before);
+ // There should be only one /data/misc_ce/0/apexrollback/<rollbackId> created during test
+ assertThat(after).hasSize(1);
// Expire all rollbacks and check CE snapshot directories are deleted
runPhase("testCleanUp");
for (String dir : after) {
@@ -520,11 +536,13 @@
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
final File apex = buildHelper.getTestFile(fileName);
- if (!getDevice().isAdbRoot()) {
+ try {
getDevice().enableAdbRoot();
+ getDevice().remountSystemWritable();
+ assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
+ } finally {
+ getDevice().disableAdbRoot();
}
- getDevice().remountSystemWritable();
- assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
getDevice().reboot();
}
@@ -544,16 +562,18 @@
return String.format("/data/user_de/%d/%s", userId, apexName);
}
- private List<String> getSnapshotDirectories(String baseDir) {
- try {
- return getDevice().getFileEntry(baseDir).getChildren(false)
- .stream().filter(entry -> entry.getName().matches("\\d+(-prerestore)?"))
- .map(entry -> entry.getFullPath())
- .collect(Collectors.toList());
- } catch (Exception e) {
- // Return an empty list if any error
+ private List<String> getSnapshotDirectories(String baseDir) throws Exception {
+ IFileEntry f = getDevice().getFileEntry(baseDir);
+ if (f == null) {
+ Log.d(TAG, "baseDir doesn't exist: " + baseDir);
return Collections.EMPTY_LIST;
}
+ List<String> list = f.getChildren(false)
+ .stream().filter(entry -> entry.getName().matches("\\d+(-prerestore)?"))
+ .map(entry -> entry.getFullPath())
+ .collect(Collectors.toList());
+ Log.d(TAG, "getSnapshotDirectories=" + list);
+ return list;
}
private void assertDirectoryIsEmpty(String path) {
@@ -600,4 +620,13 @@
return false;
}
}
+
+ private void pushString(String contents, String deviceFilePath) throws Exception {
+ try {
+ getDevice().enableAdbRoot();
+ assertThat(getDevice().pushString(contents, deviceFilePath)).isTrue();
+ } finally {
+ getDevice().disableAdbRoot();
+ }
+ }
}
diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml
index 050e9c3..c30d761 100644
--- a/tests/SilkFX/AndroidManifest.xml
+++ b/tests/SilkFX/AndroidManifest.xml
@@ -4,9 +4,9 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -40,7 +40,14 @@
android:label="Glow Examples"/>
<activity android:name=".materials.GlassActivity"
- android:label="Glass Examples"/>
+ android:label="Glass Examples"
+ android:banner="@drawable/background1"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/SilkFX/res/layout-television/activity_glass.xml b/tests/SilkFX/res/layout-television/activity_glass.xml
new file mode 100644
index 0000000..1f566860
--- /dev/null
+++ b/tests/SilkFX/res/layout-television/activity_glass.xml
@@ -0,0 +1,302 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** 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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity">
+
+ <ImageView
+ android:id="@+id/background"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:scaleType="matrix"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:srcCompat="@drawable/background1" />
+
+ <com.android.test.silkfx.materials.GlassView
+ android:id="@+id/materialView"
+ android:layout_width="400dp"
+ android:layout_height="100dp"
+ android:layout_marginEnd="64dp"
+ android:layout_marginStart="64dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="@id/background"
+ app:layout_constraintStart_toStartOf="@id/background"
+ app:layout_constraintTop_toTopOf="parent">
+ <TextView
+ android:id="@+id/textOverlay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="18dp"
+ android:layout_gravity="center"
+ android:textColor="#ffffff"
+ android:text="Lorem Ipsum dolor sit amet." />
+ </com.android.test.silkfx.materials.GlassView>
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/bottomPanel"
+ android:layout_width="400dp"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/colorBackground"
+ android:paddingTop="24dp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent">
+
+ <SeekBar
+ android:id="@+id/materialOpacity"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="12dp"
+ android:layout_marginEnd="12dp"
+ android:layout_marginBottom="16dp"
+ android:max="100"
+ android:progress="12"
+ app:layout_constraintBottom_toTopOf="@+id/scrimOpacityTitle"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="1.0"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <SeekBar
+ android:id="@+id/zoom"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ android:layout_marginEnd="12dp"
+ android:layout_marginStart="12dp"
+ android:min="-100"
+ android:max="100"
+ android:progress="-15"
+ app:layout_constraintBottom_toTopOf="@+id/blurRadiusTitle"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="1.0"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <SeekBar
+ android:id="@+id/blurRadius"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ android:layout_marginEnd="12dp"
+ android:layout_marginStart="12dp"
+ android:max="150"
+ android:progress="40"
+ app:layout_constraintBottom_toTopOf="@+id/materialOpacityTitle"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="1.0"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <SeekBar
+ android:id="@+id/scrimOpacity"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="12dp"
+ android:layout_marginEnd="12dp"
+ android:layout_marginBottom="16dp"
+ android:max="100"
+ android:progress="50"
+ app:layout_constraintBottom_toTopOf="@+id/noiseOpacityTitle"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="1.0"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <SeekBar
+ android:id="@+id/noiseOpacity"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="12dp"
+ android:layout_marginEnd="12dp"
+ android:layout_marginBottom="24dp"
+ android:max="100"
+ android:progress="15"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <TextView
+ android:id="@+id/scrimOpacityTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="8dp"
+ android:text="Scrim Opacity"
+ android:textColor="@android:color/white"
+ app:layout_constraintBottom_toTopOf="@+id/scrimOpacity"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <TextView
+ android:id="@+id/materialOpacityTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="8dp"
+ android:text="Soft light Opacity"
+ android:textColor="@android:color/white"
+ app:layout_constraintBottom_toTopOf="@+id/materialOpacity"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <TextView
+ android:id="@+id/zoomTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="8dp"
+ android:text="Zoom"
+ android:textColor="@android:color/white"
+ app:layout_constraintBottom_toTopOf="@+id/zoom"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <TextView
+ android:id="@+id/blurRadiusTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="8dp"
+ android:text="Blur Radius"
+ android:textColor="@android:color/white"
+ app:layout_constraintBottom_toTopOf="@+id/blurRadius"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <TextView
+ android:id="@+id/noiseOpacityTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="8dp"
+ android:textColor="@android:color/white"
+ android:text="Noise Opacity"
+ app:layout_constraintBottom_toTopOf="@+id/noiseOpacity"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <ImageView
+ android:id="@+id/background1"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="16dp"
+ android:foreground="?android:attr/selectableItemBackgroundBorderless"
+ android:clickable="true"
+ android:onClick="onBackgroundClick"
+ android:scaleType="centerCrop"
+ app:layout_constraintBottom_toTopOf="@+id/lightMaterialSwitch"
+ app:layout_constraintStart_toStartOf="parent"
+ android:src="@drawable/background1" />
+
+ <ImageView
+ android:id="@+id/background2"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_marginStart="8dp"
+ android:foreground="?android:attr/selectableItemBackgroundBorderless"
+ android:clickable="true"
+ android:onClick="onBackgroundClick"
+ android:scaleType="centerCrop"
+ app:layout_constraintBottom_toBottomOf="@+id/background1"
+ app:layout_constraintStart_toEndOf="@+id/background1"
+ android:src="@drawable/background2" />
+
+ <ImageView
+ android:id="@+id/background3"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_marginStart="8dp"
+ android:scaleType="centerCrop"
+ android:foreground="?android:attr/selectableItemBackgroundBorderless"
+ android:clickable="true"
+ android:onClick="onBackgroundClick"
+ app:layout_constraintBottom_toBottomOf="@+id/background1"
+ app:layout_constraintStart_toEndOf="@+id/background2"
+ android:src="@drawable/background3" />
+
+ <Button
+ android:id="@+id/pickImage"
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_marginStart="8dp"
+ android:scaleType="centerCrop"
+ android:foreground="?android:attr/selectableItemBackgroundBorderless"
+ android:clickable="true"
+ android:onClick="onPickImageClick"
+ app:layout_constraintBottom_toBottomOf="@+id/background1"
+ app:layout_constraintStart_toEndOf="@+id/background3"
+ android:text="Pick file" />
+
+ <Switch
+ android:id="@+id/lightMaterialSwitch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="8dp"
+ android:text="Light Material"
+ app:layout_constraintBottom_toTopOf="@+id/zoomTitle"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <TextView
+ android:id="@+id/blurRadiusValue"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="TextView"
+ android:layout_marginLeft="8dp"
+ app:layout_constraintBottom_toBottomOf="@+id/blurRadiusTitle"
+ app:layout_constraintStart_toEndOf="@+id/blurRadiusTitle" />
+
+ <TextView
+ android:id="@+id/zoomValue"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="TextView"
+ android:layout_marginLeft="8dp"
+ app:layout_constraintBottom_toBottomOf="@+id/zoomTitle"
+ app:layout_constraintStart_toEndOf="@+id/zoomTitle" />
+
+ <TextView
+ android:id="@+id/materialOpacityValue"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="TextView"
+ android:layout_marginLeft="8dp"
+ app:layout_constraintBottom_toBottomOf="@+id/materialOpacityTitle"
+ app:layout_constraintStart_toEndOf="@+id/materialOpacityTitle" />
+
+ <TextView
+ android:id="@+id/noiseOpacityValue"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="TextView"
+ android:layout_marginLeft="8dp"
+ app:layout_constraintBottom_toBottomOf="@+id/noiseOpacityTitle"
+ app:layout_constraintStart_toEndOf="@+id/noiseOpacityTitle" />
+
+
+ <TextView
+ android:id="@+id/scrimOpacityValue"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="TextView"
+ android:layout_marginLeft="8dp"
+ app:layout_constraintBottom_toBottomOf="@+id/scrimOpacityTitle"
+ app:layout_constraintStart_toEndOf="@+id/scrimOpacityTitle" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 0fe84ab..cd2dc04 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -59,6 +59,7 @@
"mockito-target-minus-junit4",
"net-tests-utils",
"platform-test-annotations",
+ "service-connectivity",
"services.core",
"services.net",
],
diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING
index 005cbe9..89fc6ea 100644
--- a/tests/net/TEST_MAPPING
+++ b/tests/net/TEST_MAPPING
@@ -8,5 +8,10 @@
{
"name": "FrameworksNetDeflakeTest"
}
+ ],
+ "imports": [
+ {
+ "path": "cts/tests/tests/net"
+ }
]
}
\ No newline at end of file
diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp
index 874bd4b..69742b9 100644
--- a/tests/net/integration/Android.bp
+++ b/tests/net/integration/Android.bp
@@ -32,6 +32,7 @@
"kotlin-reflect",
"mockito-target-extended-minus-junit4",
"net-tests-utils",
+ "service-connectivity",
"services.core",
"services.net",
"testables",
@@ -59,6 +60,7 @@
"net-tests-utils",
],
libs: [
+ "service-connectivity",
"services.core",
"services.net",
],
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index bc069e1..dba1856e 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -167,7 +167,7 @@
cm = ConnectivityManager(context, service)
context.addMockSystemService(Context.CONNECTIVITY_SERVICE, cm)
- service.systemReady()
+ service.systemReadyInternal()
}
private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService(
diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java
index 8e9d08c..2e1c29a 100644
--- a/tests/net/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/net/java/android/net/IpSecAlgorithmTest.java
@@ -16,34 +16,50 @@
package android.net;
+import static android.net.IpSecAlgorithm.ALGO_TO_REQUIRED_FIRST_SDK;
+
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import android.content.res.Resources;
+import android.os.Build;
import android.os.Parcel;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.CollectionUtils;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Random;
+import java.util.Set;
/** Unit tests for {@link IpSecAlgorithm}. */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class IpSecAlgorithmTest {
-
private static final byte[] KEY_MATERIAL;
+ private final Resources mMockResources = mock(Resources.class);
+
static {
KEY_MATERIAL = new byte[128];
new Random().nextBytes(KEY_MATERIAL);
};
+ private static byte[] generateKey(int keyLenInBits) {
+ return Arrays.copyOf(KEY_MATERIAL, keyLenInBits / 8);
+ }
+
@Test
public void testNoTruncLen() throws Exception {
Entry<String, Integer>[] authAndAeadList =
@@ -53,7 +69,7 @@
new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA256, 256),
new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA384, 384),
new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA512, 512),
- new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224)
+ new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224),
};
// Expect auth and aead algorithms to throw errors if trunclen is omitted.
@@ -70,6 +86,52 @@
new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
}
+ private void checkAuthKeyAndTruncLenValidation(String algoName, int keyLen, int truncLen)
+ throws Exception {
+ new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen);
+
+ try {
+ new IpSecAlgorithm(algoName, generateKey(keyLen));
+ fail("Expected exception on unprovided auth trunclen");
+ } catch (IllegalArgumentException pass) {
+ }
+
+ try {
+ new IpSecAlgorithm(algoName, generateKey(keyLen + 8), truncLen);
+ fail("Invalid key length not validated");
+ } catch (IllegalArgumentException pass) {
+ }
+
+ try {
+ new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen + 1);
+ fail("Invalid truncation length not validated");
+ } catch (IllegalArgumentException pass) {
+ }
+ }
+
+ private void checkCryptKeyLenValidation(String algoName, int keyLen) throws Exception {
+ new IpSecAlgorithm(algoName, generateKey(keyLen));
+
+ try {
+ new IpSecAlgorithm(algoName, generateKey(keyLen + 8));
+ fail("Invalid key length not validated");
+ } catch (IllegalArgumentException pass) {
+ }
+ }
+
+ @Test
+ public void testValidationForAlgosAddedInS() throws Exception {
+ if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.R) {
+ return;
+ }
+
+ for (int len : new int[] {160, 224, 288}) {
+ checkCryptKeyLenValidation(IpSecAlgorithm.CRYPT_AES_CTR, len);
+ }
+ checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_AES_XCBC, 128, 96);
+ checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305, 288, 128);
+ }
+
@Test
public void testTruncLenValidation() throws Exception {
for (int truncLen : new int[] {256, 512}) {
@@ -127,4 +189,37 @@
assertTrue("Parcel/Unparcel failed!", IpSecAlgorithm.equals(init, fin));
p.recycle();
}
+
+ private static Set<String> getMandatoryAlgos() {
+ return CollectionUtils.filter(
+ ALGO_TO_REQUIRED_FIRST_SDK.keySet(),
+ i -> Build.VERSION.FIRST_SDK_INT >= ALGO_TO_REQUIRED_FIRST_SDK.get(i));
+ }
+
+ private static Set<String> getOptionalAlgos() {
+ return CollectionUtils.filter(
+ ALGO_TO_REQUIRED_FIRST_SDK.keySet(),
+ i -> Build.VERSION.FIRST_SDK_INT < ALGO_TO_REQUIRED_FIRST_SDK.get(i));
+ }
+
+ @Test
+ public void testGetSupportedAlgorithms() throws Exception {
+ assertTrue(IpSecAlgorithm.getSupportedAlgorithms().containsAll(getMandatoryAlgos()));
+ assertTrue(ALGO_TO_REQUIRED_FIRST_SDK.keySet().containsAll(
+ IpSecAlgorithm.getSupportedAlgorithms()));
+ }
+
+ @Test
+ public void testLoadAlgos() throws Exception {
+ final Set<String> optionalAlgoSet = getOptionalAlgos();
+ final String[] optionalAlgos = optionalAlgoSet.toArray(new String[0]);
+
+ doReturn(optionalAlgos).when(mMockResources)
+ .getStringArray(com.android.internal.R.array.config_optionalIpSecAlgorithms);
+
+ final Set<String> enabledAlgos = new HashSet<>(IpSecAlgorithm.loadAlgos(mMockResources));
+ final Set<String> expectedAlgos = ALGO_TO_REQUIRED_FIRST_SDK.keySet();
+
+ assertEquals(expectedAlgos, enabledAlgos);
+ }
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 7dfac9c..71fa3b4 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1246,7 +1246,7 @@
// Create local CM before sending system ready so that we can answer
// getSystemService() correctly.
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
- mService.systemReady();
+ mService.systemReadyInternal();
mockVpn(Process.myUid());
mCm.bindProcessToNetwork(null);
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
index 753dbf8..32bfa70 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -159,7 +159,6 @@
// Send a validation event that is tracked on the alternate netId
mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
- mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
mDnsManager.flushVmDnsCache();
mDnsManager.updateTransportsForNetwork(TEST_NETID_ALTERNATE, TEST_TRANSPORT_TYPES);
mDnsManager.noteDnsServersForNetwork(TEST_NETID_ALTERNATE, lp);
@@ -196,7 +195,6 @@
}));
mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
- mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
mDnsManager.flushVmDnsCache();
fixedLp = new LinkProperties(lp);
mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp);
@@ -232,7 +230,6 @@
lp.addDnsServer(InetAddress.getByName("3.3.3.3"));
mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
- mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
mDnsManager.flushVmDnsCache();
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
@@ -246,7 +243,6 @@
mDnsManager.getPrivateDnsConfig());
mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
- mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
mDnsManager.flushVmDnsCache();
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_UNTRACKED,
@@ -295,7 +291,6 @@
mDnsManager.getPrivateDnsConfig());
mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
- mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
mDnsManager.flushVmDnsCache();
mDnsManager.updatePrivateDnsValidation(
new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
@@ -341,7 +336,6 @@
lp.addDnsServer(InetAddress.getByName("4.4.4.4"));
mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES);
mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp);
- mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers());
mDnsManager.flushVmDnsCache();
final ArgumentCaptor<ResolverParamsParcel> resolverParamsParcelCaptor =
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 7ab4d97..bc3db11 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -1155,7 +1155,7 @@
new String[] { EGRESS_IFACE, "l2tp", expectedAddr, "1701", profile.l2tpSecret,
"name", profile.username, "password", profile.password,
"linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns",
- "idle", "1800", "mtu", "1400", "mru", "1400" },
+ "idle", "1800", "mtu", "1270", "mru", "1270" },
deps.mtpdArgs.get(10, TimeUnit.SECONDS));
// Now wait for the runner to be ready before testing for the route.
legacyRunnerReady.block(10_000);
diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
index 8f09377..6d2c7dc 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -150,7 +151,7 @@
}
private void assertRatTypeChangedForSub(String subscriberId, int ratType) {
- assertEquals(mMonitor.getRatTypeForSubscriberId(subscriberId), ratType);
+ assertEquals(ratType, mMonitor.getRatTypeForSubscriberId(subscriberId));
final ArgumentCaptor<Integer> typeCaptor = ArgumentCaptor.forClass(Integer.class);
// Verify callback with the subscriberId and the RAT type should be as expected.
// It will fail if get a callback with an unexpected RAT type.
@@ -302,26 +303,84 @@
reset(mDelegate);
// Set IMSI to null again to simulate somehow IMSI is not available, such as
- // modem crash. Verify service should not unregister listener.
+ // modem crash. Verify service should unregister listener.
updateSubscriberIdForTestSub(TEST_SUBID1, null);
- verify(mTelephonyManager, never()).listen(any(), anyInt());
- assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+ verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()),
+ eq(PhoneStateListener.LISTEN_NONE));
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
reset(mDelegate);
+ clearInvocations(mTelephonyManager);
- // Set RAT type of sim1 to LTE. Verify RAT type of sim1 is still changed even if the IMSI
- // is not available. The monitor keeps the listener even if the IMSI disappears because
- // the IMSI can never change for any given subId, therefore even if the IMSI is updated
- // to null, the monitor should continue accepting updates of the RAT type. However,
- // telephony is never actually supposed to do this, if the IMSI disappears there should
- // not be updates, but it's still the right thing to do theoretically.
- setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+ // Simulate somehow IMSI is back. Verify service will register with
+ // another listener and fire callback accordingly.
+ final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor2 =
+ ArgumentCaptor.forClass(RatTypeListener.class);
+ updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1);
+ verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(),
+ eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ reset(mDelegate);
+ clearInvocations(mTelephonyManager);
+
+ // Set RAT type of sim1 to LTE. Verify RAT type of sim1 still works.
+ setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1,
TelephonyManager.NETWORK_TYPE_LTE);
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE);
reset(mDelegate);
mMonitor.stop();
+ verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor2.getValue()),
+ eq(PhoneStateListener.LISTEN_NONE));
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ }
+
+ /**
+ * Verify that when IMSI suddenly changed for a given subId, the service will register a new
+ * listener and unregister the old one, and report changes on updated IMSI. This is for modem
+ * feature that may be enabled for certain carrier, which changes to use a different IMSI while
+ * roaming on certain networks for multi-IMSI SIM cards, but the subId stays the same.
+ */
+ @Test
+ public void testSubscriberIdChanged() {
+ mMonitor.start();
+ // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback
+ // before changing RAT type.
+ addTestSub(TEST_SUBID1, TEST_IMSI1);
+ final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
+ ArgumentCaptor.forClass(RatTypeListener.class);
+ verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(),
+ eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+
+ // Set RAT type of sim1 to UMTS.
+ // Verify RAT type of sim1 changes accordingly.
+ setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+ TelephonyManager.NETWORK_TYPE_UMTS);
+ assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+ reset(mDelegate);
+ clearInvocations(mTelephonyManager);
+
+ // Simulate IMSI of sim1 changed to IMSI2. Verify the service will register with
+ // another listener and remove the old one. The RAT type of new IMSI stays at
+ // NETWORK_TYPE_UNKNOWN until received initial callback from telephony.
+ final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor2 =
+ ArgumentCaptor.forClass(RatTypeListener.class);
+ updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI2);
+ verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(),
+ eq(PhoneStateListener.LISTEN_SERVICE_STATE));
verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()),
eq(PhoneStateListener.LISTEN_NONE));
assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ reset(mDelegate);
+
+ // Set RAT type of sim1 to UMTS for new listener to simulate the initial callback received
+ // from telephony after registration. Verify RAT type of sim1 changes with IMSI2
+ // accordingly.
+ setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1,
+ TelephonyManager.NETWORK_TYPE_UMTS);
+ assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UMTS);
+ reset(mDelegate);
}
}
diff --git a/tests/vcn/OWNERS b/tests/vcn/OWNERS
new file mode 100644
index 0000000..33b9f0f
--- /dev/null
+++ b/tests/vcn/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+benedictwong@google.com
+ckesting@google.com
+evitayan@google.com
+nharold@google.com
+jchalard@google.com
\ No newline at end of file
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index 2d48a83..d6e8922 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -584,7 +584,7 @@
method public void onPublishStarted(@NonNull android.net.wifi.aware.PublishDiscoverySession);
method public void onServiceDiscovered(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>);
method public void onServiceDiscoveredWithinRange(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>, int);
- method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle);
+ method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle, int);
method public void onSessionConfigFailed();
method public void onSessionConfigUpdated();
method public void onSessionTerminated();
@@ -664,6 +664,8 @@
field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0
field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1
+ field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1; // 0x1
+ field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0; // 0x0
}
public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo {
@@ -803,13 +805,13 @@
method public String getFriendlyName();
method @Nullable public long[] getMatchAllOis();
method @Nullable public long[] getMatchAnyOis();
- method @Nullable public String[] getOtherHomePartners();
+ method @NonNull public java.util.Collection<java.lang.String> getOtherHomePartnersList();
method public long[] getRoamingConsortiumOis();
method public void setFqdn(String);
method public void setFriendlyName(String);
method public void setMatchAllOis(@Nullable long[]);
method public void setMatchAnyOis(@Nullable long[]);
- method public void setOtherHomePartners(@Nullable String[]);
+ method public void setOtherHomePartnersList(@NonNull java.util.Collection<java.lang.String>);
method public void setRoamingConsortiumOis(long[]);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR;
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
index e3800ad..da8e17e 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -191,14 +191,18 @@
}
/**
- * Called when the discovered peer is no longer visible. All further operations on this
- * discovery session will fail. If the peer is visible again,
+ * Called when the discovered service is not available. All further operations on this
+ * discovery session will fail. If the service is available again,
* {@link #onServiceDiscovered(PeerHandle, byte[], List)} or
* {@link #onServiceDiscoveredWithinRange(PeerHandle, byte[], List, int)} will be called.
*
* @param peerHandle An opaque handle to the peer matching our discovery operation.
+ * @param reason Discovered service lost reason code. One of
+ * {@link WifiAwareManager#WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE},
+ * {@link WifiAwareManager#WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN
*/
- public void onServiceLost(@NonNull PeerHandle peerHandle) {
+ public void onServiceLost(@NonNull PeerHandle peerHandle,
+ @WifiAwareManager.DiscoveryLostReasonCode int reason) {
/* empty */
}
}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index d6e46fd..03f5f40 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -151,6 +151,27 @@
*/
public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1;
+ /** @hide */
+ @IntDef({
+ WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN,
+ WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DiscoveryLostReasonCode {
+ }
+
+ /**
+ * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)}
+ * indicating that the service was lost for unknown reason.
+ */
+ public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0;
+
+ /**
+ * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)}
+ * indicating that the service advertised by the peer is no longer visible. This may be because
+ * the peer is out of range or because the peer stopped advertising this service.
+ */
+ public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1;
+
private final Context mContext;
private final IWifiAwareManager mService;
@@ -695,7 +716,8 @@
break;
case CALLBACK_MATCH_EXPIRED:
mOriginalCallback
- .onServiceLost(new PeerHandle(msg.arg1));
+ .onServiceLost(new PeerHandle(msg.arg1),
+ WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE);
}
}
};
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
index 35a8ff6..64aad61 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
@@ -16,6 +16,7 @@
package android.net.wifi.hotspot2.pps;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,6 +25,7 @@
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -214,23 +216,52 @@
*
* @param otherHomePartners Array of Strings containing the FQDNs of other Home partner
* providers
+ * @hide
*/
public void setOtherHomePartners(@Nullable String[] otherHomePartners) {
mOtherHomePartners = otherHomePartners;
}
/**
+ * Set the list of FQDN (Fully Qualified Domain Name) of other Home partner providers.
+ *
+ * @param otherHomePartners Collection of Strings containing the FQDNs of other Home partner
+ * providers
+ */
+ public void setOtherHomePartnersList(@NonNull Collection<String> otherHomePartners) {
+ if (otherHomePartners == null) {
+ return;
+ }
+ mOtherHomePartners = otherHomePartners.toArray(new String[otherHomePartners.size()]);
+ }
+
+ /**
* Get the list of FQDN (Fully Qualified Domain Name) of other Home partner providers set in
* the profile.
*
* @return Array of Strings containing the FQDNs of other Home partner providers set in the
* profile
+ * @hide
*/
public @Nullable String[] getOtherHomePartners() {
return mOtherHomePartners;
}
/**
+ * Get the list of FQDN (Fully Qualified Domain Name) of other Home partner providers set in
+ * the profile.
+ *
+ * @return Collection of Strings containing the FQDNs of other Home partner providers set in the
+ * profile
+ */
+ public @NonNull Collection<String> getOtherHomePartnersList() {
+ if (mOtherHomePartners == null) {
+ return Collections.emptyList();
+ }
+ return Arrays.asList(mOtherHomePartners);
+ }
+
+ /**
* List of Organization Identifiers (OIs) identifying a roaming consortium of
* which this provider is a member.
*/
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 7d7d2ba..7877ea1 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -87,12 +87,12 @@
}
SoftApConfiguration unparceled = parcelUnparcel(original);
- assertThat(unparceled).isNotSameAs(original);
+ assertThat(unparceled).isNotSameInstanceAs(original);
assertThat(unparceled).isEqualTo(original);
assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
- assertThat(copy).isNotSameAs(original);
+ assertThat(copy).isNotSameInstanceAs(original);
assertThat(copy).isEqualTo(original);
assertThat(copy.hashCode()).isEqualTo(original.hashCode());
}
@@ -111,12 +111,12 @@
assertThat(original.getMaxNumberOfClients()).isEqualTo(0);
SoftApConfiguration unparceled = parcelUnparcel(original);
- assertThat(unparceled).isNotSameAs(original);
+ assertThat(unparceled).isNotSameInstanceAs(original);
assertThat(unparceled).isEqualTo(original);
assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
- assertThat(copy).isNotSameAs(original);
+ assertThat(copy).isNotSameInstanceAs(original);
assertThat(copy).isEqualTo(original);
assertThat(copy.hashCode()).isEqualTo(original.hashCode());
}
@@ -159,12 +159,12 @@
}
SoftApConfiguration unparceled = parcelUnparcel(original);
- assertThat(unparceled).isNotSameAs(original);
+ assertThat(unparceled).isNotSameInstanceAs(original);
assertThat(unparceled).isEqualTo(original);
assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
- assertThat(copy).isNotSameAs(original);
+ assertThat(copy).isNotSameInstanceAs(original);
assertThat(copy).isEqualTo(original);
assertThat(copy.hashCode()).isEqualTo(original.hashCode());
}
@@ -185,12 +185,12 @@
SoftApConfiguration unparceled = parcelUnparcel(original);
- assertThat(unparceled).isNotSameAs(original);
+ assertThat(unparceled).isNotSameInstanceAs(original);
assertThat(unparceled).isEqualTo(original);
assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
- assertThat(copy).isNotSameAs(original);
+ assertThat(copy).isNotSameInstanceAs(original);
assertThat(copy).isEqualTo(original);
assertThat(copy.hashCode()).isEqualTo(original.hashCode());
}
@@ -212,12 +212,12 @@
SoftApConfiguration unparceled = parcelUnparcel(original);
- assertThat(unparceled).isNotSameAs(original);
+ assertThat(unparceled).isNotSameInstanceAs(original);
assertThat(unparceled).isEqualTo(original);
assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
- assertThat(copy).isNotSameAs(original);
+ assertThat(copy).isNotSameInstanceAs(original);
assertThat(copy).isEqualTo(original);
assertThat(copy.hashCode()).isEqualTo(original.hashCode());
}
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 5fe0cb4..d163fb0 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -16,6 +16,7 @@
package android.net.wifi.aware;
+import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE;
import static android.net.wifi.aware.WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB;
import static org.hamcrest.core.IsEqual.equalTo;
@@ -372,7 +373,8 @@
// (5) discovery session is no longer visible
sessionProxyCallback.getValue().onMatchExpired(peerHandle.peerId);
mMockLooper.dispatchAll();
- inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture());
+ inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture(),
+ eq(WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE));
assertEquals(peerHandle.peerId, peerIdCaptor.getValue().peerId);
// (6) terminate
@@ -520,7 +522,8 @@
// (5) discovery session is no longer visible
sessionProxyCallback.getValue().onMatchExpired(peerHandle.peerId);
mMockLooper.dispatchAll();
- inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture());
+ inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture(),
+ eq(WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE));
assertEquals(peerHandle.peerId, peerIdCaptor.getValue().peerId);
// (6) terminate
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
index 93d471a..fe889fc 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java
@@ -16,6 +16,7 @@
package android.net.wifi.hotspot2.pps;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -26,7 +27,9 @@
import org.junit.Test;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@@ -35,6 +38,7 @@
*/
@SmallTest
public class HomeSpTest {
+ private static final String[] OTHER_HOME_PARTNER_LIST = new String[]{"partner1", "partner2"};
/**
* Helper function for creating a map of home network IDs for testing.
@@ -62,7 +66,7 @@
homeSp.setHomeNetworkIds(homeNetworkIds);
homeSp.setMatchAllOis(new long[] {0x11L, 0x22L});
homeSp.setMatchAnyOis(new long[] {0x33L, 0x44L});
- homeSp.setOtherHomePartners(new String[] {"partner1", "partner2"});
+ homeSp.setOtherHomePartners(OTHER_HOME_PARTNER_LIST);
homeSp.setRoamingConsortiumOis(new long[] {0x55, 0x66});
return homeSp;
}
@@ -218,4 +222,34 @@
HomeSp copySp = new HomeSp(sourceSp);
assertTrue(copySp.equals(sourceSp));
}
+
+ /**
+ * Verify that the getOtherHomePartnersList gets the list of partners as expected.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateGetOtherHomePartnersList() throws Exception {
+ HomeSp homeSp = createHomeSpWithoutHomeNetworkIds();
+
+ Collection<String> otherHomePartnersList = homeSp.getOtherHomePartnersList();
+ assertEquals(2, otherHomePartnersList.size());
+ assertTrue(Arrays.equals(OTHER_HOME_PARTNER_LIST, otherHomePartnersList.toArray()));
+ }
+
+ /**
+ * Verify that the setOtherHomePartnersList sets the list of partners as expected.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateSetOtherHomePartnersList() throws Exception {
+ HomeSp homeSp = createHomeSpWithoutHomeNetworkIds();
+
+ final Collection<String> homePartners =
+ new ArrayList<>(Arrays.asList(OTHER_HOME_PARTNER_LIST));
+
+ homeSp.setOtherHomePartnersList(homePartners);
+ assertTrue(Arrays.equals(homeSp.getOtherHomePartners(), OTHER_HOME_PARTNER_LIST));
+ }
}